diff -r 1c40dc81184f -r de72873a5ddb integration/lib/cleditor/jquery.cleditor.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/integration/lib/cleditor/jquery.cleditor.js Fri Jun 21 10:15:31 2013 +0200 @@ -0,0 +1,1132 @@ +/** + @preserve CLEditor WYSIWYG HTML Editor v1.3.0 + http://premiumsoftware.net/cleditor + requires jQuery v1.4.2 or later + + Copyright 2010, Chris Landowski, Premium Software, LLC + Dual licensed under the MIT or GPL Version 2 licenses. +*/ + +// ==ClosureCompiler== +// @compilation_level SIMPLE_OPTIMIZATIONS +// @output_file_name jquery.cleditor.min.js +// ==/ClosureCompiler== + +(function($) { + + //============== + // jQuery Plugin + //============== + + $.cleditor = { + + // Define the defaults used for all new cleditor instances + defaultOptions: { + width: 500, // width not including margins, borders or padding + height: 250, // height not including margins, borders or padding + controls: // controls to add to the toolbar + "bold italic underline strikethrough subscript superscript | font size " + + "style | color highlight removeformat | bullets numbering | outdent " + + "indent | alignleft center alignright justify | undo redo | " + + "rule image link unlink | cut copy paste pastetext | print source", + colors: // colors in the color popup + "FFF FCC FC9 FF9 FFC 9F9 9FF CFF CCF FCF " + + "CCC F66 F96 FF6 FF3 6F9 3FF 6FF 99F F9F " + + "BBB F00 F90 FC6 FF0 3F3 6CC 3CF 66C C6C " + + "999 C00 F60 FC3 FC0 3C0 0CC 36F 63F C3C " + + "666 900 C60 C93 990 090 399 33F 60C 939 " + + "333 600 930 963 660 060 366 009 339 636 " + + "000 300 630 633 330 030 033 006 309 303", + fonts: // font names in the font popup + "Arial,Arial Black,Comic Sans MS,Courier New,Narrow,Garamond," + + "Georgia,Impact,Sans Serif,Serif,Tahoma,Trebuchet MS,Verdana", + sizes: // sizes in the font size popup + "1,2,3,4,5,6,7", + styles: // styles in the style popup + [["Paragraph", "

"], ["Header 1", "

"], ["Header 2", "

"], + ["Header 3", "

"], ["Header 4","

"], ["Header 5","

"], + ["Header 6","
"]], + useCSS: false, // use CSS to style HTML when possible (not supported in ie) + docType: // Document type contained within the editor + '', + docCSSFile: // CSS file used to style the document contained within the editor + "", + bodyStyle: // style to assign to document body contained within the editor + "margin:4px; font:10pt Arial,Verdana; cursor:text" + }, + + // Define all usable toolbar buttons - the init string property is + // expanded during initialization back into the buttons object and + // seperate object properties are created for each button. + // e.g. buttons.size.title = "Font Size" + buttons: { + // name,title,command,popupName (""=use name) + init: + "bold,,|" + + "italic,,|" + + "underline,,|" + + "strikethrough,,|" + + "subscript,,|" + + "superscript,,|" + + "font,,fontname,|" + + "size,Font Size,fontsize,|" + + "style,,formatblock,|" + + "color,Font Color,forecolor,|" + + "highlight,Text Highlight Color,hilitecolor,color|" + + "removeformat,Remove Formatting,|" + + "bullets,,insertunorderedlist|" + + "numbering,,insertorderedlist|" + + "outdent,,|" + + "indent,,|" + + "alignleft,Align Text Left,justifyleft|" + + "center,,justifycenter|" + + "alignright,Align Text Right,justifyright|" + + "justify,,justifyfull|" + + "undo,,|" + + "redo,,|" + + "rule,Insert Horizontal Rule,inserthorizontalrule|" + + "image,Insert Image,insertimage,url|" + + "link,Insert Hyperlink,createlink,url|" + + "unlink,Remove Hyperlink,|" + + "cut,,|" + + "copy,,|" + + "paste,,|" + + "pastetext,Paste as Text,inserthtml,|" + + "print,,|" + + "source,Show Source" + }, + + // imagesPath - returns the path to the images folder + imagesPath: function() { return imagesPath(); } + + }; + + // cleditor - creates a new editor for each of the matched textareas + $.fn.cleditor = function(options) { + + // Create a new jQuery object to hold the results + var $result = $([]); + + // Loop through all matching textareas and create the editors + this.each(function(idx, elem) { + if (elem.tagName == "TEXTAREA") { + var data = $.data(elem, CLEDITOR); + if (!data) data = new cleditor(elem, options); + $result = $result.add(data); + } + }); + + // return the new jQuery object + return $result; + + }; + + //================== + // Private Variables + //================== + + var + + // Misc constants + BACKGROUND_COLOR = "backgroundColor", + BUTTON = "button", + BUTTON_NAME = "buttonName", + CHANGE = "change", + CLEDITOR = "cleditor", + CLICK = "click", + DISABLED = "disabled", + DIV_TAG = "
", + TRANSPARENT = "transparent", + UNSELECTABLE = "unselectable", + + // Class name constants + MAIN_CLASS = "cleditorMain", // main containing div + TOOLBAR_CLASS = "cleditorToolbar", // toolbar div inside main div + GROUP_CLASS = "cleditorGroup", // group divs inside the toolbar div + BUTTON_CLASS = "cleditorButton", // button divs inside group div + DISABLED_CLASS = "cleditorDisabled",// disabled button divs + DIVIDER_CLASS = "cleditorDivider", // divider divs inside group div + POPUP_CLASS = "cleditorPopup", // popup divs inside body + LIST_CLASS = "cleditorList", // list popup divs inside body + COLOR_CLASS = "cleditorColor", // color popup div inside body + PROMPT_CLASS = "cleditorPrompt", // prompt popup divs inside body + MSG_CLASS = "cleditorMsg", // message popup div inside body + + // Test for ie + ie = $.browser.msie, + ie6 = /msie\s6/i.test(navigator.userAgent), + + // Test for iPhone/iTouch/iPad + iOS = /iphone|ipad|ipod/i.test(navigator.userAgent), + + // Popups are created once as needed and shared by all editor instances + popups = {}, + + // Used to prevent the document click event from being bound more than once + documentClickAssigned, + + // Local copy of the buttons object + buttons = $.cleditor.buttons; + + //=============== + // Initialization + //=============== + + // Expand the buttons.init string back into the buttons object + // and create seperate object properties for each button. + // e.g. buttons.size.title = "Font Size" + $.each(buttons.init.split("|"), function(idx, button) { + var items = button.split(","), name = items[0]; + buttons[name] = { + stripIndex: idx, + name: name, + title: items[1] === "" ? name.charAt(0).toUpperCase() + name.substr(1) : items[1], + command: items[2] === "" ? name : items[2], + popupName: items[3] === "" ? name : items[3] + }; + }); + delete buttons.init; + + //============ + // Constructor + //============ + + // cleditor - creates a new editor for the passed in textarea element + cleditor = function(area, options) { + + var editor = this; + + // Get the defaults and override with options + editor.options = options = $.extend({}, $.cleditor.defaultOptions, options); + + // Hide the textarea and associate it with this editor + var $area = editor.$area = $(area) + .hide() + .data(CLEDITOR, editor) + .blur(function() { + // Update the iframe when the textarea loses focus + updateFrame(editor, true); + }); + + // Create the main container and append the textarea + var $main = editor.$main = $(DIV_TAG) + .addClass(MAIN_CLASS) + .width(options.width) + .height(options.height); + + // Create the toolbar + var $toolbar = editor.$toolbar = $(DIV_TAG) + .addClass(TOOLBAR_CLASS) + .appendTo($main); + + // Add the first group to the toolbar + var $group = $(DIV_TAG) + .addClass(GROUP_CLASS) + .appendTo($toolbar); + + // Add the buttons to the toolbar + $.each(options.controls.split(" "), function(idx, buttonName) { + if (buttonName === "") return true; + + // Divider + if (buttonName == "|") { + + // Add a new divider to the group + var $div = $(DIV_TAG) + .addClass(DIVIDER_CLASS) + .appendTo($group); + + // Create a new group + $group = $(DIV_TAG) + .addClass(GROUP_CLASS) + .appendTo($toolbar); + + } + + // Button + else { + + // Get the button definition + var button = buttons[buttonName]; + + // Add a new button to the group + var $buttonDiv = $(DIV_TAG) + .data(BUTTON_NAME, button.name) + .addClass(BUTTON_CLASS) + .attr("title", button.title) + .bind(CLICK, $.proxy(buttonClick, editor)) + .appendTo($group) + .hover(hoverEnter, hoverLeave); + + // Prepare the button image + var map = {}; + if (button.css) map = button.css; + else if (button.image) map.backgroundImage = imageUrl(button.image); + if (button.stripIndex) map.backgroundPosition = button.stripIndex * -24; + $buttonDiv.css(map); + + // Add the unselectable attribute for ie + if (ie) + $buttonDiv.attr(UNSELECTABLE, "on"); + + // Create the popup + if (button.popupName) + createPopup(button.popupName, options, button.popupClass, + button.popupContent, button.popupHover); + + } + + }); + + // Add the main div to the DOM and append the textarea + $main.insertBefore($area) + .append($area); + + // Bind the document click event handler + if (!documentClickAssigned) { + $(document).click(function(e) { + // Dismiss all non-prompt popups + var $target = $(e.target); + if (!$target.add($target.parents()).is("." + PROMPT_CLASS)) + hidePopups(); + }); + documentClickAssigned = true; + } + + // Bind the window resize event when the width or height is auto or % + if (/auto|%/.test("" + options.width + options.height)) + $(window).resize(function() {refresh(editor);}); + + // Create the iframe and resize the controls + refresh(editor); + + }; + + //=============== + // Public Methods + //=============== + + var fn = cleditor.prototype, + + // Expose the following private functions as methods on the cleditor object. + // The closure compiler will rename the private functions. However, the + // exposed method names on the cleditor object will remain fixed. + methods = [ + ["clear", clear], + ["disable", disable], + ["execCommand", execCommand], + ["focus", focus], + ["hidePopups", hidePopups], + ["sourceMode", sourceMode, true], + ["refresh", refresh], + ["select", select], + ["selectedHTML", selectedHTML, true], + ["selectedText", selectedText, true], + ["showMessage", showMessage], + ["updateFrame", updateFrame], + ["updateTextArea", updateTextArea] + ]; + + $.each(methods, function(idx, method) { + fn[method[0]] = function() { + var editor = this, args = [editor]; + // using each here would cast booleans into objects! + for(var x = 0; x < arguments.length; x++) {args.push(arguments[x]);} + var result = method[1].apply(editor, args); + if (method[2]) return result; + return editor; + }; + }); + + // change - shortcut for .bind("change", handler) or .trigger("change") + fn.change = function(handler) { + var $this = $(this); + return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE); + }; + + //=============== + // Event Handlers + //=============== + + // buttonClick - click event handler for toolbar buttons + function buttonClick(e) { + + var editor = this, + buttonDiv = e.target, + buttonName = $.data(buttonDiv, BUTTON_NAME), + button = buttons[buttonName], + popupName = button.popupName, + popup = popups[popupName]; + + // Check if disabled + if (editor.disabled || $(buttonDiv).attr(DISABLED) == DISABLED) + return; + + // Fire the buttonClick event + var data = { + editor: editor, + button: buttonDiv, + buttonName: buttonName, + popup: popup, + popupName: popupName, + command: button.command, + useCSS: editor.options.useCSS + }; + + if (button.buttonClick && button.buttonClick(e, data) === false) + return false; + + // Toggle source + if (buttonName == "source") { + + // Show the iframe + if (sourceMode(editor)) { + delete editor.range; + editor.$area.hide(); + editor.$frame.show(); + buttonDiv.title = button.title; + } + + // Show the textarea + else { + editor.$frame.hide(); + editor.$area.show(); + buttonDiv.title = "Show Rich Text"; + } + + // Enable or disable the toolbar buttons + // IE requires the timeout + setTimeout(function() {refreshButtons(editor);}, 100); + + } + + // Check for rich text mode + else if (!sourceMode(editor)) { + + // Handle popups + if (popupName) { + var $popup = $(popup); + + // URL + if (popupName == "url") { + + // Check for selection before showing the link url popup + if (buttonName == "link" && selectedText(editor) === "") { + showMessage(editor, "A selection is required when inserting a link.", buttonDiv); + return false; + } + + // Wire up the submit button click event handler + $popup.children(":button") + .unbind(CLICK) + .bind(CLICK, function() { + + // Insert the image or link if a url was entered + var $text = $popup.find(":text"), + url = $.trim($text.val()); + if (url !== "") + execCommand(editor, data.command, url, null, data.button); + + // Reset the text, hide the popup and set focus + $text.val("http://"); + hidePopups(); + focus(editor); + + }); + + } + + // Paste as Text + else if (popupName == "pastetext") { + + // Wire up the submit button click event handler + $popup.children(":button") + .unbind(CLICK) + .bind(CLICK, function() { + + // Insert the unformatted text replacing new lines with break tags + var $textarea = $popup.find("textarea"), + text = $textarea.val().replace(/\n/g, "
"); + if (text !== "") + execCommand(editor, data.command, text, null, data.button); + + // Reset the text, hide the popup and set focus + $textarea.val(""); + hidePopups(); + focus(editor); + + }); + + } + + // Show the popup if not already showing for this button + if (buttonDiv !== $.data(popup, BUTTON)) { + showPopup(editor, popup, buttonDiv); + return false; // stop propagination to document click + } + + // propaginate to documnt click + return; + + } + + // Print + else if (buttonName == "print") + editor.$frame[0].contentWindow.print(); + + // All other buttons + else if (!execCommand(editor, data.command, data.value, data.useCSS, buttonDiv)) + return false; + + } + + // Focus the editor + focus(editor); + + } + + // hoverEnter - mouseenter event handler for buttons and popup items + function hoverEnter(e) { + var $div = $(e.target).closest("div"); + $div.css(BACKGROUND_COLOR, $div.data(BUTTON_NAME) ? "#FFF" : "#FFC"); + } + + // hoverLeave - mouseleave event handler for buttons and popup items + function hoverLeave(e) { + $(e.target).closest("div").css(BACKGROUND_COLOR, "transparent"); + } + + // popupClick - click event handler for popup items + function popupClick(e) { + + var editor = this, + popup = e.data.popup, + target = e.target; + + // Check for message and prompt popups + if (popup === popups.msg || $(popup).hasClass(PROMPT_CLASS)) + return; + + // Get the button info + var buttonDiv = $.data(popup, BUTTON), + buttonName = $.data(buttonDiv, BUTTON_NAME), + button = buttons[buttonName], + command = button.command, + value, + useCSS = editor.options.useCSS; + + // Get the command value + if (buttonName == "font") + // Opera returns the fontfamily wrapped in quotes + value = target.style.fontFamily.replace(/"/g, ""); + else if (buttonName == "size") { + if (target.tagName == "DIV") + target = target.children[0]; + value = target.innerHTML; + } + else if (buttonName == "style") + value = "<" + target.tagName + ">"; + else if (buttonName == "color") + value = hex(target.style.backgroundColor); + else if (buttonName == "highlight") { + value = hex(target.style.backgroundColor); + if (ie) command = 'backcolor'; + else useCSS = true; + } + + // Fire the popupClick event + var data = { + editor: editor, + button: buttonDiv, + buttonName: buttonName, + popup: popup, + popupName: button.popupName, + command: command, + value: value, + useCSS: useCSS + }; + + if (button.popupClick && button.popupClick(e, data) === false) + return; + + // Execute the command + if (data.command && !execCommand(editor, data.command, data.value, data.useCSS, buttonDiv)) + return false; + + // Hide the popup and focus the editor + hidePopups(); + focus(editor); + + } + + //================== + // Private Functions + //================== + + // checksum - returns a checksum using the Adler-32 method + function checksum(text) + { + var a = 1, b = 0; + for (var index = 0; index < text.length; ++index) { + a = (a + text.charCodeAt(index)) % 65521; + b = (b + a) % 65521; + } + return (b << 16) | a; + } + + // clear - clears the contents of the editor + function clear(editor) { + editor.$area.val(""); + updateFrame(editor); + } + + // createPopup - creates a popup and adds it to the body + function createPopup(popupName, options, popupTypeClass, popupContent, popupHover) { + + // Check if popup already exists + if (popups[popupName]) + return popups[popupName]; + + // Create the popup + var $popup = $(DIV_TAG) + .hide() + .addClass(POPUP_CLASS) + .appendTo("body"); + + // Add the content + + // Custom popup + if (popupContent) + $popup.html(popupContent); + + // Color + else if (popupName == "color") { + var colors = options.colors.split(" "); + if (colors.length < 10) + $popup.width("auto"); + $.each(colors, function(idx, color) { + $(DIV_TAG).appendTo($popup) + .css(BACKGROUND_COLOR, "#" + color); + }); + popupTypeClass = COLOR_CLASS; + } + + // Font + else if (popupName == "font") + $.each(options.fonts.split(","), function(idx, font) { + $(DIV_TAG).appendTo($popup) + .css("fontFamily", font) + .html(font); + }); + + // Size + else if (popupName == "size") + $.each(options.sizes.split(","), function(idx, size) { + $(DIV_TAG).appendTo($popup) + .html("" + size + ""); + }); + + // Style + else if (popupName == "style") + $.each(options.styles, function(idx, style) { + $(DIV_TAG).appendTo($popup) + .html(style[1] + style[0] + style[1].replace("<", "
'); + popupTypeClass = PROMPT_CLASS; + } + + // Paste as Text + else if (popupName == "pastetext") { + $popup.html('Paste your content here and click submit.

'); + popupTypeClass = PROMPT_CLASS; + } + + // Add the popup type class name + if (!popupTypeClass && !popupContent) + popupTypeClass = LIST_CLASS; + $popup.addClass(popupTypeClass); + + // Add the unselectable attribute to all items + if (ie) { + $popup.attr(UNSELECTABLE, "on") + .find("div,font,p,h1,h2,h3,h4,h5,h6") + .attr(UNSELECTABLE, "on"); + } + + // Add the hover effect to all items + if ($popup.hasClass(LIST_CLASS) || popupHover === true) + $popup.children().hover(hoverEnter, hoverLeave); + + // Add the popup to the array and return it + popups[popupName] = $popup[0]; + return $popup[0]; + + } + + // disable - enables or disables the editor + function disable(editor, disabled) { + + // Update the textarea and save the state + if (disabled) { + editor.$area.attr(DISABLED, DISABLED); + editor.disabled = true; + } + else { + editor.$area.removeAttr(DISABLED); + delete editor.disabled; + } + + // Switch the iframe into design mode. + // ie6 does not support designMode. + // ie7 & ie8 do not properly support designMode="off". + try { + if (ie) editor.doc.body.contentEditable = !disabled; + else editor.doc.designMode = !disabled ? "on" : "off"; + } + // Firefox 1.5 throws an exception that can be ignored + // when toggling designMode from off to on. + catch (err) {} + + // Enable or disable the toolbar buttons + refreshButtons(editor); + + } + + // execCommand - executes a designMode command + function execCommand(editor, command, value, useCSS, button) { + + // Restore the current ie selection + restoreRange(editor); + + // Set the styling method + if (!ie) { + if (useCSS === undefined || useCSS === null) + useCSS = editor.options.useCSS; + editor.doc.execCommand("styleWithCSS", 0, useCSS.toString()); + } + + // Execute the command and check for error + var success = true, description; + if (ie && command.toLowerCase() == "inserthtml") + getRange(editor).pasteHTML(value); + else { + try { success = editor.doc.execCommand(command, 0, value || null); } + catch (err) { description = err.description; success = false; } + if (!success) { + if ("cutcopypaste".indexOf(command) > -1) + showMessage(editor, "For security reasons, your browser does not support the " + + command + " command. Try using the keyboard shortcut or context menu instead.", + button); + else + showMessage(editor, + (description ? description : "Error executing the " + command + " command."), + button); + } + } + + // Enable the buttons + refreshButtons(editor); + return success; + + } + + // focus - sets focus to either the textarea or iframe + function focus(editor) { + setTimeout(function() { + if (sourceMode(editor)) editor.$area.focus(); + else editor.$frame[0].contentWindow.focus(); + refreshButtons(editor); + }, 0); + } + + // getRange - gets the current text range object + function getRange(editor) { + if (ie) return getSelection(editor).createRange(); + return getSelection(editor).getRangeAt(0); + } + + // getSelection - gets the current text range object + function getSelection(editor) { + if (ie) return editor.doc.selection; + return editor.$frame[0].contentWindow.getSelection(); + } + + // Returns the hex value for the passed in string. + // hex("rgb(255, 0, 0)"); // #FF0000 + // hex("#FF0000"); // #FF0000 + // hex("#F00"); // #FF0000 + function hex(s) { + var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s), + c = s.split(""); + if (m) { + s = ( m[1] << 16 | m[2] << 8 | m[3] ).toString(16); + while (s.length < 6) + s = "0" + s; + } + return "#" + (s.length == 6 ? s : c[1] + c[1] + c[2] + c[2] + c[3] + c[3]); + } + + // hidePopups - hides all popups + function hidePopups() { + $.each(popups, function(idx, popup) { + $(popup) + .hide() + .unbind(CLICK) + .removeData(BUTTON); + }); + } + + // imagesPath - returns the path to the images folder + function imagesPath() { + var cssFile = "jquery.cleditor.css", + href = $("link[href$='" + cssFile +"']").attr("href"); + return href.substr(0, href.length - cssFile.length) + "images/"; + } + + // imageUrl - Returns the css url string for a filemane + function imageUrl(filename) { + return "url(" + imagesPath() + filename + ")"; + } + + // refresh - creates the iframe and resizes the controls + function refresh(editor) { + + var $main = editor.$main, + options = editor.options; + + // Remove the old iframe + if (editor.$frame) + editor.$frame.remove(); + + // Create a new iframe + var $frame = editor.$frame = $('