/*! 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", "<p>"], ["Header 1", "<h1>"], ["Header 2", "<h2>"], ["Header 3", "<h3>"], ["Header 4", "<h4>"], ["Header 5", "<h5>"], ["Header 6", "<h6>"]], useCSS: true, // use CSS to style HTML when possible (not supported in ie) docType: // Document type contained within the editor '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">', 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 = "<div>", FOCUSED = "focused", 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 // 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, "<br />"); 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('<font size="' + size + '">' + size + '</font>'); }); // Style else if (popupName === "style") $.each(options.styles, function (idx, style) { $(DIV_TAG).appendTo($popup) .html(style[1] + style[0] + style[1].replace("<", "</")); }); // URL else if (popupName === "url") { $popup.html('<label>Enter URL:<br /><input type="text" value="http://" style="width:200px" /></label><br /><input type="button" value="Submit" />'); popupTypeClass = PROMPT_CLASS; } // Paste as Text else if (popupName === "pastetext") { $popup.html('<label>Paste your content here:<br /><textarea rows="3" style="width:200px"></textarea></label><br /><input type="button" value="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 inserthtml = command.toLowerCase() === "inserthtml"; if (ie && inserthtml) getRange(editor).pasteHTML(value); 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 = $('<iframe frameborder="0" src="javascript:true;" />') .hide() .appendTo($main); // Load the iframe document content var contentWindow = $frame[0].contentWindow, doc = editor.doc = contentWindow.document, $doc = $(doc); doc.open(); doc.write( options.docType + '<html>' + ((options.docCSSFile === '') ? '' : '<head><link rel="stylesheet" type="text/css" href="' + options.docCSSFile + '" /></head>') + '<body style="' + options.bodyStyle + '"></body></html>' ); doc.close(); // Work around for bug in IE which causes the editor to lose // focus when clicking below the end of the document. if (ie || iege11) $doc.click(function () { focus(editor); }); // Load the content updateFrame(editor); // Bind the ie specific iframe event handlers if (ie || iege11) { // Save the current user selection. This code is needed since IE will // reset the selection just after the beforedeactivate event and just // before the beforeactivate event. $doc.bind("beforedeactivate beforeactivate selectionchange keypress keyup", function (e) { // Flag the editor as inactive if (e.type === "beforedeactivate") editor.inactive = true; // Get rid of the bogus selection and flag the editor as active else if (e.type === "beforeactivate") { if (!editor.inactive && editor.range && editor.range.length > 1) editor.range.shift(); delete editor.inactive; } // Save the selection when the editor is active else if (!editor.inactive) { if (!editor.range) editor.range = []; editor.range.unshift(getRange(editor)); // We only need the last 2 selections while (editor.range.length > 2) editor.range.pop(); } }); // Restore the text range and trigger focused event when the iframe gains focus $frame.focus(function () { restoreRange(editor); $(editor).triggerHandler(FOCUSED); }); // Trigger blurred event when the iframe looses focus $frame.blur(function () { $(editor).triggerHandler(BLURRED); }); } // Trigger focused and blurred events for all other browsers else { $($frame[0].contentWindow) .focus(function () { $(editor).triggerHandler(FOCUSED); }) .blur(function () { $(editor).triggerHandler(BLURRED); }); } // Enable the toolbar buttons and update the textarea as the user types or clicks $doc.click(hidePopups) .keydown(function (e) { // Prevent Internet Explorer from going to prior page when an image // is selected and the backspace key is pressed. if (ie && getSelection(editor).type == "Control" && e.keyCode == 8) { getSelection(editor).clear(); e.preventDefault(); } }) .bind("keyup mouseup", function () { refreshButtons(editor); updateTextArea(editor, true); }); // Show the textarea for iPhone/iTouch/iPad or // the iframe when design mode is supported. if (iOS) editor.$area.show(); else $frame.show(); // Wait for the layout to finish - shortcut for $(document).ready() $(function () { var $toolbar = editor.$toolbar, $group = $toolbar.children("div:last"), wid = $main.width(); // Resize the toolbar var hgt = $group.offset().top + $group.outerHeight() - $toolbar.offset().top + 1; $toolbar.height(hgt); // Resize the iframe hgt = (/%/.test("" + options.height) ? $main.height() : parseInt(options.height, 10)) - hgt; $frame.width(wid).height(hgt); // Resize the textarea. IE6 textareas have a 1px top // & bottom margin that cannot be removed using css. editor.$area.width(wid).height(ie6 ? hgt - 2 : hgt); // Switch the iframe into design mode if enabled disable(editor, editor.disabled); // Enable or disable the toolbar buttons refreshButtons(editor); }); } // refreshButtons - enables or disables buttons based on availability function refreshButtons(editor) { // Webkit requires focus before queryCommandEnabled will return anything but false if (!iOS && webkit && !editor.focused) { editor.$frame[0].contentWindow.focus(); window.focus(); editor.focused = true; } // Get the object used for checking queryCommandEnabled var queryObj = editor.doc; if (ie) queryObj = getRange(editor); // Loop through each button var inSourceMode = sourceMode(editor); $.each(editor.$toolbar.find("." + BUTTON_CLASS), function (idx, elem) { var $elem = $(elem), button = $.cleditor.buttons[$.data(elem, BUTTON_NAME)], command = button.command, enabled = true; // Determine the state if (editor.disabled) enabled = false; else if (button.getEnabled) { var data = { editor: editor, button: elem, buttonName: button.name, popup: popups[button.popupName], popupName: button.popupName, command: button.command, useCSS: editor.options.useCSS }; enabled = button.getEnabled(data); if (enabled === undefined) enabled = true; } else if (((inSourceMode || iOS) && button.name !== "source") || (ie && (command === "undo" || command === "redo"))) enabled = false; else if (command && command !== "print") { if (ie && command === "hilitecolor") command = "backcolor"; // IE does not support inserthtml, so it's always enabled if ((!ie && !iege11) || command !== "inserthtml") { try { enabled = queryObj.queryCommandEnabled(command); } catch (err) { enabled = false; } } } // Enable or disable the button if (enabled) { $elem.removeClass(DISABLED_CLASS); $elem.removeAttr(DISABLED); } else { $elem.addClass(DISABLED_CLASS); $elem.attr(DISABLED, DISABLED); } }); } // restoreRange - restores the current ie selection function restoreRange(editor) { if (editor.range) { if (ie) editor.range[0].select(); else if (iege11) getSelection(editor).addRange(editor.range[0]); } } // select - selects all the text in either the textarea or iframe function select(editor) { setTimeout(function () { if (sourceMode(editor)) editor.$area.select(); else execCommand(editor, "selectall"); }, 0); } // selectedHTML - returns the current HTML selection or and empty string function selectedHTML(editor) { restoreRange(editor); var range = getRange(editor); if (ie) return range.htmlText; var layer = $("<layer>")[0]; layer.appendChild(range.cloneContents()); var html = layer.innerHTML; layer = null; return html; } // selectedText - returns the current text selection or and empty string function selectedText(editor) { restoreRange(editor); if (ie) return getRange(editor).text; return getSelection(editor).toString(); } // showMessage - alert replacement function showMessage(editor, message, button) { var popup = createPopup("msg", editor.options, MSG_CLASS); popup.innerHTML = message; showPopup(editor, popup, button); } // showPopup - shows a popup function showPopup(editor, popup, button) { var offset, left, top, $popup = $(popup); // Determine the popup location if (button) { var $button = $(button); offset = $button.offset(); left = --offset.left; top = offset.top + $button.height(); } else { var $toolbar = editor.$toolbar; offset = $toolbar.offset(); left = Math.floor(($toolbar.width() - $popup.width()) / 2) + offset.left; top = offset.top + $toolbar.height() - 2; } // Position and show the popup hidePopups(); $popup.css({ left: left, top: top }) .show(); // Assign the popup button and click event handler if (button) { $.data(popup, BUTTON, button); $popup.bind(CLICK, { popup: popup }, $.proxy(popupClick, editor)); } // Focus the first input element if any setTimeout(function () { $popup.find(":text,textarea").eq(0).focus().select(); }, 100); } // sourceMode - returns true if the textarea is showing function sourceMode(editor) { return editor.$area.is(":visible"); } // updateFrame - updates the iframe with the textarea contents function updateFrame(editor, checkForChange) { var code = editor.$area.val(), options = editor.options, updateFrameCallback = options.updateFrame, $body = $(editor.doc.body); // Check for textarea change to avoid unnecessary firing // of potentially heavy updateFrame callbacks. if (updateFrameCallback) { var sum = checksum(code); if (checkForChange && editor.areaChecksum === sum) return; editor.areaChecksum = sum; } // Convert the textarea source code into iframe html var html = updateFrameCallback ? updateFrameCallback(code) : code; // Prevent script injection attacks by html encoding script tags html = html.replace(/<(?=\/?script)/ig, "<"); // Update the iframe checksum if (options.updateTextArea) editor.frameChecksum = checksum(html); // Update the iframe and trigger the change event if (html !== $body.html()) { $body.html(html); $(editor).triggerHandler(CHANGE); } } // updateTextArea - updates the textarea with the iframe contents function updateTextArea(editor, checkForChange) { var html = $(editor.doc.body).html(), options = editor.options, updateTextAreaCallback = options.updateTextArea, $area = editor.$area; // Check for iframe change to avoid unnecessary firing // of potentially heavy updateTextArea callbacks. if (updateTextAreaCallback) { var sum = checksum(html); if (checkForChange && editor.frameChecksum === sum) return; editor.frameChecksum = sum; } // Convert the iframe html into textarea source code var code = updateTextAreaCallback ? updateTextAreaCallback(html) : html; // Update the textarea checksum if (options.updateFrame) editor.areaChecksum = checksum(code); // Update the textarea and trigger the change event if (code !== $area.val()) { $area.val(code); $(editor).triggerHandler(CHANGE); } } })(jQuery);