/** * SCEditor * http://www.sceditor.com/ * * Copyright (C) 2011-2013, Sam Clarke (samclarke.com) * * SCEditor is licensed under the MIT license: * http://www.opensource.org/licenses/mit-license.php * * @fileoverview SCEditor - A lightweight WYSIWYG BBCode and HTML editor * @author Sam Clarke * @requires jQuery */ // ==ClosureCompiler== // @output_file_name jquery.sceditor.min.js // @compilation_level SIMPLE_OPTIMIZATIONS // ==/ClosureCompiler== /*jshint smarttabs: true, scripturl: true, jquery: true, devel:true, eqnull:true, curly: false */ /*global Range: true, browser*/ ;(function ($, window, document) { 'use strict'; /** * HTML templates used by the editor and default commands * @type {Object} * @private */ var _templates = { html: '' + '' + '' + '' + '' + '' + '' + '' + '', toolbarButton: '
{dispName}
', emoticon: '{key}', fontOpt: '{font}', sizeOpt: '{size}', pastetext: '
' + '
' + '
', table: '
' + '
' + '
', image: '
' + '
' + '
' + '
', email: '
' + '
', link: '
' + '
' + '
', youtubeMenu: '
', youtube: '' }; /** *

Replaces any params in a template with the passed params.

* *

If createHTML is passed it will use jQuery to create the HTML. The * same as doing: $(editor.tmpl("html", {params...}));

* * @param {string} templateName * @param {Object} params * @param {Boolean} createHTML * @private */ var _tmpl = function(name, params, createHTML) { var template = _templates[name]; $.each(params, function(name, val) { template = template.replace(new RegExp('\\{' + name + '\\}', 'g'), val); }); if(createHTML) template = $(template); return template; }; /** * SCEditor - A lightweight WYSIWYG editor * * @param {Element} el The textarea to be converted * @return {Object} options * @class sceditor * @name jQuery.sceditor */ $.sceditor = function (el, options) { /** * Alias of this * @private */ var base = this; /** * The textarea element being replaced * @private */ var original = el.get ? el.get(0) : el; var $original = $(original); /** * The div which contains the editor and toolbar * @private */ var $editorContainer; /** * The editors toolbar * @private */ var $toolbar; /** * The editors iframe which should be in design mode * @private */ var $wysiwygEditor; var wysiwygEditor; /** * The WYSIWYG editors body element * @private */ var $wysiwygBody; /** * The WYSIWYG editors document * @private */ var $wysiwygDoc; /** * The editors textarea for viewing source * @private */ var $sourceEditor; var sourceEditor; /** * The current dropdown * @private */ var $dropdown; /** * Array of all the commands key press functions * @private * @type {Array} */ var keyPressFuncs = []; /** * Store the last cursor position. Needed for IE because it forgets * @private */ var lastRange; /** * The editors locale * @private */ var locale; /** * Stores a cache of preloaded images * @private * @type {Array} */ var preLoadCache = []; /** * The editors rangeHelper instance * @type {jQuery.sceditor.rangeHelper} * @private */ var rangeHelper; /** * Tags which require the new line fix * @type {Array} * @private */ var requireNewLineFix = []; /** * An array of button state handlers * @type {Array} * @private */ var btnStateHandlers = []; /** * Element which gets focused to blur the editor. * * This will be null until blur() is called. * @type {HTMLElement} * @private */ var $blurElm; /** * Plugin manager instance * @type {jQuery.sceditor.PluginManager} * @private */ var pluginManager; /** * The current node containing the selection/caret * @type {Node} * @private */ var currentNode; /** * The first block level parent of the current node * @type {node} * @private */ var currentBlockNode; /** * The current node selection/caret * @type {Object} * @private */ var currentSelection; /** * Used to make sure only 1 selection changed check is called every 100ms. * Helps improve performance as it is checked a lot. * @type {Boolean} * @private */ var isSelectionCheckPending; /** * If content is required (equivalent to the HTML5 required attribute) * @type {Boolean} * @private */ var isRequired; /** * The inline CSS style element. Will be undefined until css() is called * for the first time. * @type {HTMLElement} * @private */ var inlineCss; /** * Object containing a list of shortcut handlers * @type {Object} * @private */ var shortcutHandlers = {}; /** * An array of all the current emoticons. * * Only used or populated when emoticonsCompat is enabled. * @type {Array} * @private */ var currentEmoticons = []; /** * Private functions * @private */ var init, replaceEmoticons, handleCommand, saveRange, initEditor, initPlugins, initLocale, initToolBar, initOptions, initEvents, initCommands, initResize, initEmoticons, getWysiwygDoc, handlePasteEvt, handlePasteData, handleKeyDown, handleBackSpace, handleKeyPress, handleFormReset, handleMouseDown, handleEvent, handleDocumentClick, handleWindowResize, updateToolBar, updateActiveButtons, sourceEditorSelectedText, appendNewLine, checkSelectionChanged, checkNodeChanged, autofocus, emoticonsKeyPress, emoticonsCheckWhitespace, currentStyledBlockNode; /** * All the commands supported by the editor * @name commands * @memberOf jQuery.sceditor.prototype */ base.commands = $.extend(true, {}, (options.commands || $.sceditor.commands)); /** * Options for this editor instance * @name opts * @memberOf jQuery.sceditor.prototype */ base.opts = options = $.extend({}, $.sceditor.defaultOptions, options); /** * Creates the editor iframe and textarea * @private */ init = function () { $original.data("sceditor", base); // Clone any objects in options $.each(options, function(key, val) { if($.isPlainObject(val)) options[key] = $.extend(true, {}, val); }); // Load locale if(options.locale && options.locale !== 'en') initLocale(); $editorContainer = $('
') .insertAfter($original) .css('z-index', options.zIndex); // Add IE version to the container to allow IE specific CSS // fixes without using CSS hacks or conditional comments if($.sceditor.ie) $editorContainer.addClass('ie ie' + $.sceditor.ie); isRequired = !!$original.attr('required'); $original.removeAttr('required'); // create the editor initPlugins(); initEmoticons(); initToolBar(); initEditor(); initCommands(); initOptions(); initEvents(); // force into source mode if is a browser that can't handle // full editing if(!$.sceditor.isWysiwygSupported) base.toggleSourceMode(); var loaded = function() { $(window).unbind('load', loaded); if(options.autofocus) autofocus(); if(options.autoExpand) base.expandToContent(); // Page width might have changed after CSS is loaded so // call handleWindowResize to update any % based dimensions handleWindowResize(); }; $(window).load(loaded); if(document.readyState && document.readyState === 'complete') loaded(); updateActiveButtons(); pluginManager.call('ready'); }; initPlugins = function() { var plugins = options.plugins; plugins = plugins ? plugins.toString().split(',') : []; pluginManager = new $.sceditor.PluginManager(base); $.each(plugins, function(idx, plugin) { pluginManager.register($.trim(plugin)); }); }; /** * Init the locale variable with the specified locale if possible * @private * @return void */ initLocale = function() { var lang; if($.sceditor.locale[options.locale]) locale = $.sceditor.locale[options.locale]; else { lang = options.locale.split('-'); if($.sceditor.locale[lang[0]]) locale = $.sceditor.locale[lang[0]]; } if(locale && locale.dateFormat) options.dateFormat = locale.dateFormat; }; /** * Creates the editor iframe and textarea * @private */ initEditor = function () { var doc, tabIndex; // @SMF code: tabindex applied to the editor $sourceEditor = $('').attr('tabindex', $original.attr('tabindex')).hide(); $wysiwygEditor = $('').attr('tabindex', $original.attr('tabindex')); if(!options.spellcheck) $sourceEditor.attr('spellcheck', 'false'); if(window.location.protocol === 'https:') $wysiwygEditor.attr('src', 'javascript:false'); // add the editor to the HTML and store the editors element $editorContainer.append($wysiwygEditor).append($sourceEditor); wysiwygEditor = $wysiwygEditor[0]; sourceEditor = $sourceEditor[0]; base.width(options.width || $original.width()); base.height(options.height || $original.height()); doc = getWysiwygDoc(); doc.open(); doc.write(_tmpl('html', { spellcheck: options.spellcheck ? '' : 'spellcheck="false"', charset: options.charset, style: options.style })); doc.close(); $wysiwygDoc = $(doc); $wysiwygBody = $(doc.body); base.readOnly(!!options.readOnly); // Add IE version class to the HTML element so can apply // conditional styling without CSS hacks if($.sceditor.ie) $wysiwygDoc.find('html').addClass('ie ie' + $.sceditor.ie); // iframe overflow fix for iOS, also fixes an IE issue with the // editor not getting focus when clicking inside if($.sceditor.ios || $.sceditor.ie) { $wysiwygBody.height('100%'); if(!$.sceditor.ie) $wysiwygBody.bind('touchend', base.focus); } rangeHelper = new $.sceditor.rangeHelper(wysiwygEditor.contentWindow); // load any textarea value into the editor base.val($original.hide().val()); tabIndex = $original.attr('tabindex'); $sourceEditor.attr('tabindex', tabIndex); $wysiwygEditor.attr('tabindex', tabIndex); }; /** * Initialises options * @private */ initOptions = function() { // auto-update original textbox on blur if option set to true if(options.autoUpdate) { $wysiwygBody.bind('blur', base.updateOriginal); $sourceEditor.bind('blur', base.updateOriginal); } if(options.rtl === null) options.rtl = $sourceEditor.css('direction') === 'rtl'; base.rtl(!!options.rtl); if(options.autoExpand) $wysiwygDoc.bind('keyup', base.expandToContent); if(options.resizeEnabled) initResize(); $editorContainer.attr('id', options.id); base.emoticons(options.emoticonsEnabled); }; /** * Initialises events * @private */ initEvents = function() { $(document).click(handleDocumentClick); $(original.form) .bind('reset', handleFormReset) .submit(base.updateOriginal); $(window).bind('resize orientationChanged', handleWindowResize); $wysiwygBody .keypress(handleKeyPress) .keydown(handleKeyDown) .keydown(handleBackSpace) .keyup(appendNewLine) .bind('paste', handlePasteEvt) .bind($.sceditor.ie ? 'selectionchange' : 'keyup focus blur contextmenu mouseup touchend click', checkSelectionChanged) .bind('keydown keyup keypress focus blur contextmenu', handleEvent); if(options.emoticonsCompat && window.getSelection) $wysiwygBody.keyup(emoticonsCheckWhitespace); $sourceEditor.bind('keydown keyup keypress focus blur contextmenu', handleEvent).keydown(handleKeyDown); $wysiwygDoc .keypress(handleKeyPress) .mousedown(handleMouseDown) .bind($.sceditor.ie ? 'selectionchange' : 'focus blur contextmenu mouseup click', checkSelectionChanged) .bind('beforedeactivate keyup', saveRange) .keyup(appendNewLine) .focus(function() { lastRange = null; }); $editorContainer .bind('selectionchanged', checkNodeChanged) .bind('selectionchanged', updateActiveButtons) .bind('selectionchanged', handleEvent) .bind('nodechanged', handleEvent); }; /** * Creates the toolbar and appends it to the container * @private */ initToolBar = function () { var $group, $button, exclude = (options.toolbarExclude || '').split(','), groups = options.toolbar.split('|'); $toolbar = $('
'); $.each(groups, function(idx, group) { $group = $('
'); $.each(group.split(','), function(idx, button) { // The button must be a valid command and not excluded if(!base.commands[button] || $.inArray(button, exclude) > -1) return; $button = _tmpl('toolbarButton', { name: button, dispName: base._(base.commands[button].tooltip || button) }, true); $button.data('sceditor-txtmode', !!base.commands[button].txtExec); $button.data('sceditor-wysiwygmode', !!base.commands[button].exec); $button.click(function() { var $this = $(this); if(!$this.hasClass('disabled')) handleCommand($this, base.commands[button]); updateActiveButtons(); return false; }); if(base.commands[button].tooltip) $button.attr('title', base._(base.commands[button].tooltip)); if(!base.commands[button].exec) $button.addClass('disabled'); if(base.commands[button].shortcut) base.addShortcut(base.commands[button].shortcut, button); $group.append($button); }); // Exclude empty groups if($group[0].firstChild) $toolbar.append($group); }); // append the toolbar to the toolbarContainer option if given $(options.toolbarContainer || $editorContainer).append($toolbar); }; /** * Creates an array of all the key press functions * like emoticons, ect. * @private */ initCommands = function () { $.each(base.commands, function (name, cmd) { if(cmd.keyPress) keyPressFuncs.push(cmd.keyPress); if(cmd.forceNewLineAfter && $.isArray(cmd.forceNewLineAfter)) requireNewLineFix = $.merge(requireNewLineFix, cmd.forceNewLineAfter); if(cmd.state) btnStateHandlers.push({ name: name, state: cmd.state }); // exec string commands can be passed to queryCommandState else if(typeof cmd.exec === 'string') btnStateHandlers.push({ name: name, state: cmd.exec }); }); appendNewLine(); }; /** * Creates the resizer. * @private */ initResize = function () { var minHeight, maxHeight, minWidth, maxWidth, mouseMoveFunc, mouseUpFunc, $grip = $('
'), // cover is used to cover the editor iframe so document still gets mouse move events $cover = $('
'), startX = 0, startY = 0, startWidth = 0, startHeight = 0, origWidth = $editorContainer.width(), origHeight = $editorContainer.height(), dragging = false, rtl = base.rtl(); minHeight = options.resizeMinHeight || origHeight / 1.5; maxHeight = options.resizeMaxHeight || origHeight * 2.5; minWidth = options.resizeMinWidth || origWidth / 1.25; maxWidth = options.resizeMaxWidth || origWidth * 1.25; mouseMoveFunc = function (e) { // iOS must use window.event if(e.type === 'touchmove') e = window.event; var newHeight = startHeight + (e.pageY - startY), newWidth = rtl ? startWidth - (e.pageX - startX) : startWidth + (e.pageX - startX); if(maxWidth > 0 && newWidth > maxWidth) newWidth = maxWidth; if(maxHeight > 0 && newHeight > maxHeight) newHeight = maxHeight; if(!options.resizeWidth || newWidth < minWidth || (maxWidth > 0 && newWidth > maxWidth)) newWidth = false; if(!options.resizeHeight || newHeight < minHeight || (maxHeight > 0 && newHeight > maxHeight)) newHeight = false; if(newWidth || newHeight) { base.dimensions(newWidth, newHeight); // The resize cover will not fill the container in IE6 unless a height is specified. if($.sceditor.ie < 7) $editorContainer.height(newHeight); } e.preventDefault(); }; mouseUpFunc = function (e) { if(!dragging) return; dragging = false; $cover.hide(); $editorContainer.removeClass('resizing').height('auto'); $(document).unbind('touchmove mousemove', mouseMoveFunc); $(document).unbind('touchend mouseup', mouseUpFunc); e.preventDefault(); }; $editorContainer.append($grip); $editorContainer.append($cover.hide()); $grip.bind('touchstart mousedown', function (e) { // iOS must use window.event if(e.type === 'touchstart') e = window.event; startX = e.pageX; startY = e.pageY; startWidth = $editorContainer.width(); startHeight = $editorContainer.height(); dragging = true; $editorContainer.addClass('resizing'); $cover.show(); $(document).bind('touchmove mousemove', mouseMoveFunc); $(document).bind('touchend mouseup', mouseUpFunc); // The resize cover will not fill the container in IE6 unless a height is specified. if($.sceditor.ie < 7) $editorContainer.height(startHeight); e.preventDefault(); }); }; /** * Prefixes and preloads the emoticon images * @private */ initEmoticons = function () { var emoticon, emoticons = options.emoticons, root = options.emoticonsRoot; if(!$.isPlainObject(emoticons) || !options.emoticonsEnabled) return; $.each(emoticons, function (idx, val) { $.each(val, function (key, url) { // @SMF code: In SMF an empty entry means a new line if (url == '') emoticon = document.createElement('br'); // Prefix emoticon root to emoticon urls if(root) { url = { url: root + (url.url || url), tooltip: url.tooltip || key }; emoticons[idx][key] = url; } // Preload the emoticon // Idea from: http://engineeredweb.com/blog/09/12/preloading-images-jquery-and-javascript emoticon = document.createElement('img'); emoticon.src = url.url || url; preLoadCache.push(emoticon); }); }); }; /** * Autofocus the editor * @private */ autofocus = function() { var rng, elm, txtPos, doc = $wysiwygDoc[0], body = $wysiwygBody[0], focusEnd = !!options.autofocusEnd; // Can't focus invisible elements if(!$editorContainer.is(':visible')) return; if(base.sourceMode()) { txtPos = sourceEditor.value.length; if(sourceEditor.setSelectionRange) sourceEditor.setSelectionRange(txtPos, txtPos); else if (sourceEditor.createTextRange) { rng = sourceEditor.createTextRange(); rng.moveEnd('character', txtPos); rng.moveStart('character', txtPos); rangeHelper.selectRange(rng); } } else // WYSIWYG mode { $.sceditor.dom.removeWhiteSpace(body); if(focusEnd) { if(!(elm = body.lastChild)) $wysiwygBody.append((elm = doc.createElement('div'))); while(elm.lastChild) { elm = elm.lastChild; if(/br/i.test(elm.nodeName) && elm.previousSibling) elm = elm.previousSibling; } } else elm = body.firstChild; if(doc.createRange) { rng = doc.createRange(); if(/br/i.test(elm.nodeName)) rng.setStartBefore(elm); else rng.selectNodeContents(elm); rng.collapse(false); } else { rng = body.createTextRange(); rng.moveToElementText(elm.nodeType !== 3 ? elm : elm.parentNode); rng.collapse(false); } rangeHelper.selectRange(rng); if(focusEnd) { $wysiwygDoc.scrollTop(body.scrollHeight); $wysiwygBody.scrollTop(body.scrollHeight); } } base.focus(); }; /** * Gets if the editor is read only * * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name readOnly * @return {Boolean} */ /** * Sets if the editor is read only * * @param {boolean} readOnly * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name readOnly^2 * @return {this} */ base.readOnly = function(readOnly) { if(typeof readOnly !== 'boolean') return $sourceEditor.attr('readonly') === 'readonly'; $wysiwygBody[0].contentEditable = !readOnly; if(!readOnly) $sourceEditor.removeAttr('readonly'); else $sourceEditor.attr('readonly', 'readonly'); updateToolBar(readOnly); return this; }; /** * Gets if the editor is in RTL mode * * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name rtl * @return {Boolean} */ /** * Sets if the editor is in RTL mode * * @param {boolean} rtl * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name rtl^2 * @return {this} */ base.rtl = function(rtl) { var dir = rtl ? 'rtl' : 'ltr'; if(typeof rtl !== 'boolean') return $sourceEditor.attr('dir') === 'rtl'; $wysiwygBody.attr('dir', dir); $sourceEditor.attr('dir', dir); $editorContainer .removeClass('rtl') .removeClass('ltr') .addClass(dir); return this; }; /** * Updates the toolbar to disable/enable the appropriate buttons * @private */ updateToolBar = function(disable) { var inSourceMode = base.inSourceMode(); $toolbar.find('.sceditor-button').removeClass('disabled').each(function () { var button = $(this); if(disable === true || (inSourceMode && !button.data('sceditor-txtmode'))) button.addClass('disabled'); else if (!inSourceMode && !button.data('sceditor-wysiwygmode')) button.addClass('disabled'); }); }; /** * Gets the width of the editor in pixels * * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name width * @return {int} */ /** * Sets the width of the editor * * @param {int} width Width in pixels * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name width^2 * @return {this} */ /** * Sets the width of the editor * * The saveWidth specifies if to save the width. The stored width can be * used for things like restoring from maximized state. * * @param {int} height Width in pixels * @param {boolean} [saveWidth=true] If to store the width * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name width^3 * @return {this} */ base.width = function (width, saveWidth) { if(!width && width !== 0) return $editorContainer.width(); base.dimensions(width, null, saveWidth); return this; }; /** * Returns an object with the properties width and height * which are the width and height of the editor in px. * * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name dimensions * @return {object} */ /** *

Sets the width and/or height of the editor.

* *

If width or height is not numeric it is ignored.

* * @param {int} width Width in px * @param {int} height Height in px * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name dimensions^2 * @return {this} */ /** *

Sets the width and/or height of the editor.

* *

If width or height is not numeric it is ignored.

* *

The save argument specifies if to save the new sizes. * The saved sizes can be used for things like restoring from * maximized state. This should normally be left as true.

* * @param {int} width Width in px * @param {int} height Height in px * @param {boolean} [save=true] If to store the new sizes * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name dimensions^3 * @return {this} */ base.dimensions = function(width, height, save) { // IE6 & IE7 add 2 pixels to the source mode textarea height which must be ignored. // Doesn't seem to be any way to fix it with only CSS var ieBorderBox = $.sceditor.ie < 8 || document.documentMode < 8 ? 2 : 0; // set undefined width/height to boolean false width = (!width && width !== 0) ? false : width; height = (!height && height !== 0) ? false : height; if(width === false && height === false) return { width: base.width(), height: base.height() }; if(typeof $wysiwygEditor.data('outerWidthOffset') === 'undefined') base.updateStyleCache(); if(width !== false) { if(save !== false) options.width = width; if(height === false) { height = $editorContainer.height(); save = false; } $editorContainer.width(width); if(width && width.toString().indexOf('%') > -1) width = $editorContainer.width(); $wysiwygEditor.width(width - $wysiwygEditor.data('outerWidthOffset')); $sourceEditor.width(width - $sourceEditor.data('outerWidthOffset')); // Fix overflow issue with iOS not breaking words unless a width is set if($.sceditor.ios && $wysiwygBody) $wysiwygBody.width(width - $wysiwygEditor.data('outerWidthOffset') - ($wysiwygBody.outerWidth(true) - $wysiwygBody.width())); } if(height !== false) { if(save !== false) options.height = height; // Convert % based heights to px if(height && height.toString().indexOf('%') > -1) { height = $editorContainer.height(height).height(); $editorContainer.height('auto'); } height -= !options.toolbarContainer ? $toolbar.outerHeight(true) : 0; $wysiwygEditor.height(height - $wysiwygEditor.data('outerHeightOffset')); $sourceEditor.height(height - ieBorderBox - $sourceEditor.data('outerHeightOffset')); } return this; }; /** * Updates the CSS styles cache. Shouldn't be needed unless changing the editors theme. * * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name updateStyleCache * @return {int} */ base.updateStyleCache = function() { // caching these improves FF resize performance $wysiwygEditor.data('outerWidthOffset', $wysiwygEditor.outerWidth(true) - $wysiwygEditor.width()); $sourceEditor.data('outerWidthOffset', $sourceEditor.outerWidth(true) - $sourceEditor.width()); $wysiwygEditor.data('outerHeightOffset', $wysiwygEditor.outerHeight(true) - $wysiwygEditor.height()); $sourceEditor.data('outerHeightOffset', $sourceEditor.outerHeight(true) - $sourceEditor.height()); }; /** * Gets the height of the editor in px * * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name height * @return {int} */ /** * Sets the height of the editor * * @param {int} height Height in px * @since 1.3.5 * @function * @memberOf jQuery.sceditor.prototype * @name height^2 * @return {this} */ /** * Sets the height of the editor * * The saveHeight specifies if to save the height. The stored height can be * used for things like restoring from maximized state. * * @param {int} height Height in px * @param {boolean} [saveHeight=true] If to store the height * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name height^3 * @return {this} */ base.height = function (height, saveHeight) { if(!height && height !== 0) return $editorContainer.height(); base.dimensions(null, height, saveHeight); return this; }; /** * Gets if the editor is maximised or not * * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name maximize * @return {boolean} */ /** * Sets if the editor is maximised or not * * @param {boolean} maximize If to maximise the editor * @since 1.4.1 * @function * @memberOf jQuery.sceditor.prototype * @name maximize^2 * @return {this} */ base.maximize = function(maximize) { if(typeof maximize === 'undefined') return $editorContainer.is('.sceditor-maximize'); maximize = !!maximize; // IE 6 fix if($.sceditor.ie < 7) $('html, body').toggleClass('sceditor-maximize', maximize); $editorContainer.toggleClass('sceditor-maximize', maximize); base.width(maximize ? '100%' : options.width, false); base.height(maximize ? '100%' : options.height, false); return this; }; /** * Expands the editors height to the height of it's content * * Unless ignoreMaxHeight is set to true it will not expand * higher than the maxHeight option. * * @since 1.3.5 * @param {Boolean} [ignoreMaxHeight=false] * @function * @name expandToContent * @memberOf jQuery.sceditor.prototype * @see #resizeToContent */ base.expandToContent = function(ignoreMaxHeight) { var currentHeight = $editorContainer.height(), height = $wysiwygBody[0].scrollHeight || $wysiwygDoc[0].documentElement.scrollHeight, padding = (currentHeight - $wysiwygEditor.height()), maxHeight = options.resizeMaxHeight || ((options.height || $original.height()) * 2); height += padding; if(ignoreMaxHeight !== true && height > maxHeight) height = maxHeight; if(height > currentHeight) base.height(height); }; /** * Destroys the editor, removing all elements and * event handlers. * * Leaves only the original textarea. * * @function * @name destroy * @memberOf jQuery.sceditor.prototype */ base.destroy = function () { pluginManager.destroy(); rangeHelper = null; lastRange = null; pluginManager = null; $(document).unbind('click', handleDocumentClick); $(window).unbind('resize orientationChanged', handleWindowResize); $(original.form) .unbind('reset', handleFormReset) .unbind('submit', base.updateOriginal); $wysiwygBody.unbind(); $wysiwygDoc.unbind().find('*').remove(); $sourceEditor.unbind().remove(); $toolbar.remove(); $editorContainer.unbind().find('*').unbind().remove(); $editorContainer.remove(); $original .removeData('sceditor') .removeData('sceditorbbcode') .show(); if(isRequired) $original.attr('required', 'required'); }; /** * Creates a menu item drop down * * @param {HTMLElement} menuItem The button to align the drop down with * @param {string} dropDownName Used for styling the dropown, will be a class sceditor-dropDownName * @param {HTMLElement} content The HTML content of the dropdown * @param {bool} [ieUnselectable=true] If to add the unselectable attribute to all the contents elements. Stops IE from deselecting the text in the editor * @function * @name createDropDown * @memberOf jQuery.sceditor.prototype */ base.createDropDown = function (menuItem, dropDownName, content, ieUnselectable) { // first click for create second click for close var css, onlyclose = $dropdown && $dropdown.is('.sceditor-' + dropDownName); base.closeDropDown(); if (onlyclose) return; // IE needs unselectable attr to stop it from unselecting the text in the editor. // The editor can cope if IE does unselect the text it's just not nice. if (ieUnselectable !== false) { $(content) .find(':not(input,textarea)') .filter(function() { return this.nodeType===1; }) .attr('unselectable', 'on'); } css = { top: menuItem.offset().top, left: menuItem.offset().left, marginTop: menuItem.outerHeight() }; $.extend(css, options.dropDownCss); $dropdown = $('