All Downloads are FREE. Search and download functionalities are using the official Maven repository.

META-INF.resources.primefaces.editor.editor.js Maven / Gradle / Ivy

There is a newer version: 14.0.0-RC3
Show newest version
/*!
 CLEditor WYSIWYG HTML Editor v1.4.5
 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.
*/

(function ($) {

    //==============
    // jQuery Plugin
    //==============

    $.cleditor = {

        // Define the defaults used for all new cleditor instances
        defaultOptions: {
            width: 'auto', // 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: true, // 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 // separate 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.toUpperCase() === "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", BLURRED = "blurred", BUTTON = "button", BUTTON_NAME = "buttonName", CHANGE = "change", CLEDITOR = "cleditor", CLICK = "click", DISABLED = "disabled", DIV_TAG = "
", FOCUSED = "focused", TRANSPARENT = "transparent", UNSELECTABLE = "unselectable", // Class name constants MAIN_CLASS = "ui-editor ui-widget-content", // main containing div TOOLBAR_CLASS = "ui-editor-toolbar", // toolbar div inside main div GROUP_CLASS = "ui-editor-group", // group divs inside the toolbar div BUTTON_CLASS = "ui-editor-button", // button divs inside group div DISABLED_CLASS = "ui-editor-disabled",// disabled button divs DIVIDER_CLASS = "ui-editor-divider", // divider divs inside group div POPUP_CLASS = "ui-editor-popup", // popup divs inside body LIST_CLASS = "ui-editor-list", // list popup divs inside body COLOR_CLASS = "ui-editor-color", // color popup div inside body PROMPT_CLASS = "ui-editor-prompt", // prompt popup divs inside body MSG_CLASS = "ui-editor-message", // message popup div inside body // Browser detection ua = navigator.userAgent.toLowerCase(), ie = /msie/.test(ua), ie6 = /msie\s6/.test(ua), iege11 = /(trident)(?:.*rv:([\w.]+))?/.test(ua), webkit = /webkit/.test(ua), // Test for iPhone/iTouch/iPad iOS = /iPhone|iPad|iPod/i.test(ua), // 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) .css({ border: "none", margin: 0, padding: 0 }) // Needed for IE6 & 7 (won't work in CSS file) .hide() .data(CLEDITOR, editor) .blur(function () { // Update the iframe when the textarea loses focus updateFrame(editor, true); }); // Create the main container 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); // Initialize the group width var groupWidth = 0; // 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); // Update the group width $group.width(groupWidth + 1); groupWidth = 0; // 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); // Update the group width groupWidth += 24; $group.width(groupWidth + 1); // 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).bind("resize.cleditor", 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; }; }); // blurred - shortcut for .bind("blurred", handler) or .trigger("blurred") fn.blurred = function (handler) { var $this = $(this); return handler ? $this.bind(BLURRED, handler) : $this.trigger(BLURRED); }; // change - shortcut for .bind("change", handler) or .trigger("change") fn.change = function change(handler) { var $this = $(this); return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE); }; // focused - shortcut for .bind("focused", handler) or .trigger("focused") fn.focused = function (handler) { var $this = $(this); return handler ? $this.bind(FOCUSED, handler) : $this.trigger(FOCUSED); }; //=============== // 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"; } } // 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 document 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.toUpperCase() === "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("<", "Enter URL:

'); popupTypeClass = PROMPT_CLASS; } // Paste as Text else if (popupName === "pastetext") { $popup.html('
'); 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 inserthtml = command.toLowerCase() === "inserthtml"; if (ie && inserthtml) { /* Despite having access to pasteHTML, IE8 will produce an 'unspecified error' if it is invoked. The only way to detect this bug is via try catch. */ try{ getRange(editor).pasteHTML(value); } catch(e){ // An empty document needs selection beforehand if(/^\s*$/.test(editor.doc.body.innerText)){ editor.doc.execCommand('selectAll',false, null); } // execCommand is the standard method for contentEditable elements editor.doc.execCommand("Paste", 0, value || null); } } else if (iege11 && inserthtml) { var selection = getSelection(editor), range = selection.getRangeAt(0); range.deleteContents(); range.insertNode(range.createContextualFragment(value)); selection.removeAllRanges(); selection.addRange(range); } else { var success = true, message; try { success = editor.doc.execCommand(command, 0, value || null); } catch (err) { message = err.message; 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, (message ? message : "Error executing the " + command + " command."), button); } } // Enable the buttons and update the textarea refreshButtons(editor); updateTextArea(editor, true); 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(); } // hex - returns the hex value for the passed in color string function hex(s) { // hex("rgb(255, 0, 0)") returns #FF0000 var m = /rgba?\((\d+), (\d+), (\d+)/.exec(s); if (m) { s = (m[1] << 16 | m[2] << 8 | m[3]).toString(16); while (s.length < 6) s = "0" + s; return "#" + s; } // hex("#F00") returns #FF0000 var c = s.split(""); if (s.length === 4) return "#" + c[1] + c[1] + c[2] + c[2] + c[3] + c[3]; // hex("#FF0000") returns #FF0000 return s; } // 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 href = $("link[href*=cleditor]").attr("href"); return href.replace(/^(.*\/)[^\/]+$/, '$1') + "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 = $('