/******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_RESULT__;/** * SCEditor * http://www.sceditor.com/ * * Copyright (C) 2014, 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 */ !(__WEBPACK_AMD_DEFINE_RESULT__ = function (require) { 'use strict'; var $ = __webpack_require__(1); var SCEditor = __webpack_require__(2); var PluginManager = __webpack_require__(3); var browser = __webpack_require__(6); var escape = __webpack_require__(7); // For backwards compatibility $.sceditor = SCEditor; SCEditor.commands = __webpack_require__(9); SCEditor.defaultOptions = __webpack_require__(10); SCEditor.RangeHelper = __webpack_require__(4); SCEditor.dom = __webpack_require__(5); SCEditor.ie = browser.ie; SCEditor.ios = browser.ios; SCEditor.isWysiwygSupported = browser.isWysiwygSupported; SCEditor.regexEscape = escape.regex; SCEditor.escapeEntities = escape.entities; SCEditor.escapeUriScheme = escape.uriScheme; SCEditor.PluginManager = PluginManager; SCEditor.plugins = PluginManager.plugins; /** * Creates an instance of sceditor on all textareas * matched by the jQuery selector. * * If options is set to "state" it will return bool value * indicating if the editor has been initialised on the * matched textarea(s). If there is only one textarea * it will return the bool value for that textarea. * If more than one textarea is matched it will * return an array of bool values for each textarea. * * If options is set to "instance" it will return the * current editor instance for the textarea(s). Like the * state option, if only one textarea is matched this will * return just the instance for that textarea. If more than * one textarea is matched it will return an array of * instances each textarea. * * @param {Object|String} options Should either be an Object of options or * the strings "state" or "instance" * @return {this|Array|jQuery.sceditor|Bool} */ $.fn.sceditor = function (options) { var $this, instance, ret = []; options = options || {}; if (!options.runWithoutWysiwygSupport && !browser.isWysiwygSupported) { return; } this.each(function () { $this = this.jquery ? this : $(this); instance = $this.data('sceditor'); // Don't allow the editor to be initialised // on it's own source editor if ($this.parents('.sceditor-container').length > 0) { return; } // Add state of instance to ret if that is what options is set to if (options === 'state') { ret.push(!!instance); } else if (options === 'instance') { ret.push(instance); } else if (!instance) { /*jshint -W031*/ (new SCEditor(this, options)); } }); // If nothing in the ret array then must be init so return this if (!ret.length) { return this; } return ret.length === 1 ? ret[0] : $(ret); }; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); /***/ }, /* 1 */ /***/ function(module, exports) { module.exports = jQuery; /***/ }, /* 2 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require) { 'use strict'; var $ = __webpack_require__(1); var PluginManager = __webpack_require__(3); var RangeHelper = __webpack_require__(4); var dom = __webpack_require__(5); var escape = __webpack_require__(7); var browser = __webpack_require__(6); var _tmpl = __webpack_require__(8); var globalWin = window; var globalDoc = document; var $globalWin = $(globalWin); var $globalDoc = $(globalDoc); var IE_VER = browser.ie; // In IE < 11 a BR at the end of a block level element // causes a line break. In all other browsers it's collapsed. var IE_BR_FIX = IE_VER && IE_VER < 11; /** * SCEditor - A lightweight WYSIWYG editor * * @param {Element} el The textarea to be converted * @return {Object} options * @class sceditor * @name jQuery.sceditor */ var 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; /** * 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 = []; /** * 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 = []; /** * Cache of the current toolbar buttons * * @type {Object} * @private */ var toolbarButtons = {}; /** * If the current autoUpdate action is canceled. * * @type {Boolean} * @private */ var autoUpdateCanceled; /** * 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, triggerValueChanged, valueChangedBlur, valueChangedKeyUp, autoUpdate; /** * 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 (IE_VER) { $editorContainer.addClass('ie ie' + IE_VER); } isRequired = !!$original.attr('required'); $original.removeAttr('required'); // create the editor initPlugins(); initEmoticons(); initToolBar(); initEditor(!!options.startInSourceMode); initCommands(); initOptions(); initEvents(); // force into source mode if is a browser that can't handle // full editing if (!browser.isWysiwygSupported) { base.toggleSourceMode(); } updateActiveButtons(); var loaded = function () { $globalWin.off('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(); pluginManager.call('ready'); }; $globalWin.on('load', loaded); if (globalDoc.readyState && globalDoc.readyState === 'complete') { loaded(); } }; initPlugins = function () { var plugins = options.plugins; plugins = plugins ? plugins.toString().split(',') : []; pluginManager = new 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; locale = SCEditor.locale[options.locale]; if (!locale) { lang = options.locale.split('-'); locale = SCEditor.locale[lang[0]]; } // Locale DateTime format overrides any specified in the options if (locale && locale.dateFormat) { options.dateFormat = locale.dateFormat; } }; /** * Creates the editor iframe and textarea * @param {boolean} startInSourceMode Force loading the editor in this * mode * @private */ initEditor = function (startInSourceMode) { var doc, tabIndex; $sourceEditor = $(''); $wysiwygEditor = $( '' ); /* This needs to be done right after they are created because, * for any reason, the user may not want the value to be tinkered * by any filters. */ if (startInSourceMode) { $editorContainer.addClass('sourceMode'); $wysiwygEditor.hide(); } else { $editorContainer.addClass('wysiwygMode'); $sourceEditor.hide(); } if (!options.spellcheck) { $sourceEditor.attr('spellcheck', 'false'); } /*jshint scripturl: true*/ if (globalWin.location.protocol === 'https:') { $wysiwygEditor.attr('src', 'javascript:false'); } // Add the editor to the container $editorContainer.append($wysiwygEditor).append($sourceEditor); wysiwygEditor = $wysiwygEditor[0]; sourceEditor = $sourceEditor[0]; base.dimensions( options.width || $original.width(), options.height || $original.height() ); doc = getWysiwygDoc(); doc.open(); doc.write(_tmpl('html', { // Add IE version class to the HTML element so can apply // conditional styling without CSS hacks attrs: IE_VER ? ' class="ie ie' + IE_VER + '"' : '', spellcheck: options.spellcheck ? '' : 'spellcheck="false"', charset: options.charset, style: options.style })); doc.close(); $wysiwygDoc = $(doc); $wysiwygBody = $(doc.body); base.readOnly(!!options.readOnly); // iframe overflow fix for iOS, also fixes an IE issue with the // editor not getting focus when clicking inside if (browser.ios || IE_VER) { $wysiwygBody.height('100%'); if (!IE_VER) { $wysiwygBody.on('touchend', base.focus); } } tabIndex = $original.attr('tabindex'); $sourceEditor.attr('tabindex', tabIndex); $wysiwygEditor.attr('tabindex', tabIndex); rangeHelper = new RangeHelper(wysiwygEditor.contentWindow); // load any textarea value into the editor base.val($original.hide().val()); }; /** * Initialises options * @private */ initOptions = function () { // auto-update original textbox on blur if option set to true if (options.autoUpdate) { $wysiwygBody.on('blur', autoUpdate); $sourceEditor.on('blur', autoUpdate); } if (options.rtl === null) { options.rtl = $sourceEditor.css('direction') === 'rtl'; } base.rtl(!!options.rtl); if (options.autoExpand) { $wysiwygDoc.on('keyup', base.expandToContent); } if (options.resizeEnabled) { initResize(); } $editorContainer.attr('id', options.id); base.emoticons(options.emoticonsEnabled); }; /** * Initialises events * @private */ initEvents = function () { var CHECK_SELECTION_EVENTS = IE_VER ? 'selectionchange' : 'keyup focus blur contextmenu mouseup touchend click'; var EVENTS_TO_FORWARD = 'keydown keyup keypress ' + 'focus blur contextmenu'; $globalDoc.click(handleDocumentClick); $(original.form) .on('reset', handleFormReset) .submit(base.updateOriginal); $globalWin.on('resize orientationChanged', handleWindowResize); $wysiwygBody .keypress(handleKeyPress) .keydown(handleKeyDown) .keydown(handleBackSpace) .keyup(appendNewLine) .blur(valueChangedBlur) .keyup(valueChangedKeyUp) .on('paste', handlePasteEvt) .on(CHECK_SELECTION_EVENTS, checkSelectionChanged) .on(EVENTS_TO_FORWARD, handleEvent); if (options.emoticonsCompat && globalWin.getSelection) { $wysiwygBody.keyup(emoticonsCheckWhitespace); } $sourceEditor .blur(valueChangedBlur) .keyup(valueChangedKeyUp) .keydown(handleKeyDown) .on(EVENTS_TO_FORWARD, handleEvent); $wysiwygDoc .mousedown(handleMouseDown) .blur(valueChangedBlur) .on(CHECK_SELECTION_EVENTS, checkSelectionChanged) .on('beforedeactivate keyup mouseup', saveRange) .keyup(appendNewLine) .focus(function () { lastRange = null; }); $editorContainer .on('selectionchanged', checkNodeChanged) .on('selectionchanged', updateActiveButtons) .on('selectionchanged valuechanged nodechanged', handleEvent); }; /** * Creates the toolbar and appends it to the container * @private */ initToolBar = function () { var $group, commands = base.commands, exclude = (options.toolbarExclude || '').split(','), groups = options.toolbar.split('|'); $toolbar = $('
'); $.each(groups, function (idx, group) { $group = $('
'); $.each(group.split(','), function (idx, commandName) { var $button, shortcut, command = commands[commandName]; // The commandName must be a valid command and not excluded if (!command || $.inArray(commandName, exclude) > -1) { return; } shortcut = command.shortcut; $button = _tmpl('toolbarButton', { name: commandName, dispName: base._(command.name || command.tooltip || commandName) }, true); $button .data('sceditor-txtmode', !!command.txtExec) .data('sceditor-wysiwygmode', !!command.exec) .toggleClass('disabled', !command.exec) .mousedown(function () { // IE < 8 supports unselectable attribute // so don't need this if (!IE_VER || IE_VER < 9) { autoUpdateCanceled = true; } }) .click(function () { var $this = $(this); if (!$this.hasClass('disabled')) { handleCommand($this, command); } updateActiveButtons(); return false; }); if (command.tooltip) { $button.attr( 'title', base._(command.tooltip) + (shortcut ? ' (' + shortcut + ')' : '') ); } if (shortcut) { base.addShortcut(shortcut, commandName); } if (command.state) { btnStateHandlers.push({ name: commandName, state: command.state }); // exec string commands can be passed to queryCommandState } else if (typeof command.exec === 'string') { btnStateHandlers.push({ name: commandName, state: command.exec }); } $group.append($button); toolbarButtons[commandName] = $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.forceNewLineAfter && $.isArray(cmd.forceNewLineAfter)) { requireNewLineFix = $.merge( requireNewLineFix, cmd.forceNewLineAfter ); } }); 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 = $('
'), moveEvents = 'touchmove mousemove', endEvents = 'touchcancel touchend mouseup', startX = 0, startY = 0, newX = 0, newY = 0, startWidth = 0, startHeight = 0, origWidth = $editorContainer.width(), origHeight = $editorContainer.height(), isDragging = 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 uses window.event if (e.type === 'touchmove') { e = globalWin.event; newX = e.changedTouches[0].pageX; newY = e.changedTouches[0].pageY; } else { newX = e.pageX; newY = e.pageY; } var newHeight = startHeight + (newY - startY), newWidth = rtl ? startWidth - (newX - startX) : startWidth + (newX - startX); if (maxWidth > 0 && newWidth > maxWidth) { newWidth = maxWidth; } if (minWidth > 0 && newWidth < minWidth) { newWidth = minWidth; } if (!options.resizeWidth) { newWidth = false; } if (maxHeight > 0 && newHeight > maxHeight) { newHeight = maxHeight; } if (minHeight > 0 && newHeight < minHeight) { newHeight = minHeight; } if (!options.resizeHeight) { 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 (IE_VER < 7) { $editorContainer.height(newHeight); } } e.preventDefault(); }; mouseUpFunc = function (e) { if (!isDragging) { return; } isDragging = false; $cover.hide(); $editorContainer.removeClass('resizing').height('auto'); $globalDoc.off(moveEvents, mouseMoveFunc); $globalDoc.off(endEvents, mouseUpFunc); e.preventDefault(); }; $editorContainer.append($grip); $editorContainer.append($cover.hide()); $grip.on('touchstart mousedown', function (e) { // iOS uses window.event if (e.type === 'touchstart') { e = globalWin.event; startX = e.touches[0].pageX; startY = e.touches[0].pageY; } else { startX = e.pageX; startY = e.pageY; } startWidth = $editorContainer.width(); startHeight = $editorContainer.height(); isDragging = true; $editorContainer.addClass('resizing'); $cover.show(); $globalDoc.on(moveEvents, mouseMoveFunc); $globalDoc.on(endEvents, mouseUpFunc); // The resize cover will not fill the container in // IE6 unless a height is specified. if (IE_VER < 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) { // 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 emoticon = globalDoc.createElement('img'); emoticon.src = url.url || url; preLoadCache.push(emoticon); }); }); }; /** * Autofocus the editor * @private */ autofocus = function () { var range, txtPos, doc = $wysiwygDoc[0], body = $wysiwygBody[0], node = body.firstChild, focusEnd = !!options.autofocusEnd; // Can't focus invisible elements if (!$editorContainer.is(':visible')) { return; } if (base.sourceMode()) { txtPos = focusEnd ? sourceEditor.value.length : 0; if (sourceEditor.setSelectionRange) { sourceEditor.setSelectionRange(txtPos, txtPos); } else { range = sourceEditor.createTextRange(); range.moveEnd('character', txtPos); range.collapse(false); range.select(); } return; } dom.removeWhiteSpace(body); if (focusEnd) { if (!(node = body.lastChild)) { node = doc.createElement('p'); $wysiwygBody.append(node); } while (node.lastChild) { node = node.lastChild; // IE < 11 should place the cursor after the
as // it will show it as a newline. IE >= 11 and all // other browsers should place the cursor before. if (!IE_BR_FIX && $(node).is('br') && node.previousSibling) { node = node.previousSibling; } } } if (doc.createRange) { range = doc.createRange(); if (!dom.canHaveChildren(node)) { range.setStartBefore(node); if (focusEnd) { range.setStartAfter(node); } } else { range.selectNodeContents(node); } } else { range = body.createTextRange(); range.moveToElementText(node.nodeType !== 3 ? node : node.parentNode); } range.collapse(!focusEnd); rangeHelper.selectRange(range); currentSelection = range; 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 base; }; /** * 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 base; }; /** * Updates the toolbar to disable/enable the appropriate buttons * @private */ updateToolBar = function (disable) { var mode = base.inSourceMode() ? 'txtmode' : 'wysiwygmode'; $.each(toolbarButtons, function (idx, $button) { if (disable === true || !$button.data('sceditor-' + mode)) { $button.addClass('disabled'); } else { $button.removeClass('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} width 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 base; }; /** * 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 ieBorder = IE_VER < 8 || globalDoc.documentMode < 8 ? 2 : 0; var undef; // 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 ($wysiwygEditor.data('outerWidthOffset') === undef) { base.updateStyleCache(); } if (width !== false) { if (save !== false) { options.width = width; } // This is the problem 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 (browser.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 - ieBorder - $sourceEditor.data('outerHeightOffset') ); } return base; }; /** * Updates the CSS styles cache. * * This shouldn't be needed unless changing the editors theme. *F * @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 base; }; /** * 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 (IE_VER < 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 base; }; /** * 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(), padding = (currentHeight - $wysiwygEditor.height()), height = $wysiwygBody[0].scrollHeight || $wysiwygDoc[0].documentElement.scrollHeight, maxHeight = options.resizeMaxHeight || ((options.height || $original.height()) * 2); height += padding; if ((ignoreMaxHeight === true || height <= maxHeight) && 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 () { // Don't destroy if the editor has already been destroyed if (!pluginManager) { return; } pluginManager.destroy(); rangeHelper = null; lastRange = null; pluginManager = null; if ($dropdown) { $dropdown.off().remove(); } $globalDoc.off('click', handleDocumentClick); $globalWin.off('resize orientationChanged', handleWindowResize); $(original.form) .off('reset', handleFormReset) .off('submit', base.updateOriginal); $wysiwygBody.off(); $wysiwygDoc.off().find('*').remove(); $sourceEditor.off().remove(); $toolbar.remove(); $editorContainer.off().find('*').off().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 dropdown with * @param {string} name Used for styling the dropdown, will be * a class sceditor-name * @param {HTMLElement} content The HTML content of the dropdown * @param {bool} ieFix 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, name, content, ieFix) { // first click for create second click for close var dropDownCss, cssClass = 'sceditor-' + name, onlyclose = $dropdown && $dropdown.is('.' + cssClass); // Will re-focus the editor. This is needed for IE // as it has special logic to save/restore the selection base.closeDropDown(true); if (onlyclose) { return; } // IE needs unselectable attr to stop it from // unselecting the text in the editor. // SCEditor can cope if IE does unselect the // text it's just not nice. if (ieFix !== false) { $(content) .find(':not(input,textarea)') .filter(function () { return this.nodeType === 1; }) .attr('unselectable', 'on'); } dropDownCss = { top: menuItem.offset().top, left: menuItem.offset().left, marginTop: menuItem.outerHeight() }; $.extend(dropDownCss, options.dropDownCss); $dropdown = $('
') .css(dropDownCss) .append(content) .appendTo($('body')) .on('click focusin', function (e) { // stop clicks within the dropdown from being handled e.stopPropagation(); }); // If try to focus the first input immediately IE will // place the cursor at the start of the editor instead // of focusing on the input. setTimeout(function () { if ($dropdown) { $dropdown.find('input,textarea').first().focus(); } }); }; /** * Handles any document click and closes the dropdown if open * @private */ handleDocumentClick = function (e) { // ignore right clicks if (e.which !== 3 && $dropdown) { autoUpdate(); base.closeDropDown(); } }; /** * Handles the WYSIWYG editors paste event * @private */ handlePasteEvt = function (e) { var html, handlePaste, scrollTop, elm = $wysiwygBody[0], doc = $wysiwygDoc[0], checkCount = 0, pastearea = globalDoc.createElement('div'), prePasteContent = doc.createDocumentFragment(), clipboardData = e ? e.clipboardData : false; if (options.disablePasting) { return false; } if (!options.enablePasteFiltering) { return; } rangeHelper.saveRange(); globalDoc.body.appendChild(pastearea); if (clipboardData && clipboardData.getData) { if ((html = clipboardData.getData('text/html')) || (html = clipboardData.getData('text/plain'))) { pastearea.innerHTML = html; handlePasteData(elm, pastearea); return false; } } // Save the scroll position so can be restored // when contents is restored scrollTop = $wysiwygBody.scrollTop() || $wysiwygDoc.scrollTop(); while (elm.firstChild) { prePasteContent.appendChild(elm.firstChild); } // try make pastearea contenteditable and redirect to that? Might work. // Check the tests if still exist, if not re-0create handlePaste = function (elm, pastearea) { if (elm.childNodes.length > 0 || checkCount > 25) { while (elm.firstChild) { pastearea.appendChild(elm.firstChild); } while (prePasteContent.firstChild) { elm.appendChild(prePasteContent.firstChild); } $wysiwygBody.scrollTop(scrollTop); $wysiwygDoc.scrollTop(scrollTop); if (pastearea.childNodes.length > 0) { handlePasteData(elm, pastearea); } else { rangeHelper.restoreRange(); } } else { // Allow max 25 checks before giving up. // Needed in case an empty string is pasted or // something goes wrong. checkCount++; setTimeout(function () { handlePaste(elm, pastearea); }, 20); } }; handlePaste(elm, pastearea); base.focus(); return true; }; /** * Gets the pasted data, filters it and then inserts it. * @param {Element} elm * @param {Element} pastearea * @private */ handlePasteData = function (elm, pastearea) { // fix any invalid nesting dom.fixNesting(pastearea); // TODO: Trigger custom paste event to allow filtering // (pre and post converstion?) var pasteddata = pastearea.innerHTML; if (pluginManager.hasHandler('toSource')) { pasteddata = pluginManager.callOnlyFirst( 'toSource', pasteddata, $(pastearea) ); } pastearea.parentNode.removeChild(pastearea); if (pluginManager.hasHandler('toWysiwyg')) { pasteddata = pluginManager.callOnlyFirst( 'toWysiwyg', pasteddata, true ); } rangeHelper.restoreRange(); base.wysiwygEditorInsertHtml(pasteddata, null, true); }; /** * Closes any currently open drop down * * @param {bool} [focus=false] If to focus the editor * after closing the drop down * @function * @name closeDropDown * @memberOf jQuery.sceditor.prototype */ base.closeDropDown = function (focus) { if ($dropdown) { $dropdown.off().remove(); $dropdown = null; } if (focus === true) { base.focus(); } }; /** * Gets the WYSIWYG editors document * @private */ getWysiwygDoc = function () { if (wysiwygEditor.contentDocument) { return wysiwygEditor.contentDocument; } if (wysiwygEditor.contentWindow && wysiwygEditor.contentWindow.document) { return wysiwygEditor.contentWindow.document; } return wysiwygEditor.document; }; /** *

Inserts HTML into WYSIWYG editor.

* *

If endHtml is specified, any selected text will be placed * between html and endHtml. If there is no selected text html * and endHtml will just be concatenate together.

* * @param {string} html * @param {string} [endHtml=null] * @param {boolean} [overrideCodeBlocking=false] If to insert the html * into code tags, by * default code tags only * support text. * @function * @name wysiwygEditorInsertHtml * @memberOf jQuery.sceditor.prototype */ base.wysiwygEditorInsertHtml = function ( html, endHtml, overrideCodeBlocking ) { var $marker, scrollTop, scrollTo, editorHeight = $wysiwygEditor.height(); base.focus(); // TODO: This code tag should be configurable and // should maybe convert the HTML into text instead // Don't apply to code elements if (!overrideCodeBlocking && ($(currentBlockNode).is('code') || $(currentBlockNode).parents('code').length !== 0)) { return; } // Insert the HTML and save the range so the editor can be scrolled // to the end of the selection. Also allows emoticons to be replaced // without affecting the cursor position rangeHelper.insertHTML(html, endHtml); rangeHelper.saveRange(); replaceEmoticons($wysiwygBody[0]); // Scroll the editor after the end of the selection $marker = $wysiwygBody.find('#sceditor-end-marker').show(); scrollTop = $wysiwygBody.scrollTop() || $wysiwygDoc.scrollTop(); scrollTo = (dom.getOffset($marker[0]).top + ($marker.outerHeight(true) * 1.5)) - editorHeight; $marker.hide(); // Only scroll if marker isn't already visible if (scrollTo > scrollTop || scrollTo + editorHeight < scrollTop) { $wysiwygBody.scrollTop(scrollTo); $wysiwygDoc.scrollTop(scrollTo); } triggerValueChanged(false); rangeHelper.restoreRange(); // Add a new line after the last block element // so can always add text after it appendNewLine(); }; /** * Like wysiwygEditorInsertHtml except it will convert any HTML * into text before inserting it. * * @param {String} text * @param {String} [endText=null] * @function * @name wysiwygEditorInsertText * @memberOf jQuery.sceditor.prototype */ base.wysiwygEditorInsertText = function (text, endText) { base.wysiwygEditorInsertHtml( escape.entities(text), escape.entities(endText) ); }; /** *

Inserts text into the WYSIWYG or source editor depending on which * mode the editor is in.

* *

If endText is specified any selected text will be placed between * text and endText. If no text is selected text and endText will * just be concatenate together.

* * @param {String} text * @param {String} [endText=null] * @since 1.3.5 * @function * @name insertText * @memberOf jQuery.sceditor.prototype */ base.insertText = function (text, endText) { if (base.inSourceMode()) { base.sourceEditorInsertText(text, endText); } else { base.wysiwygEditorInsertText(text, endText); } return base; }; /** *

Like wysiwygEditorInsertHtml but inserts text into the * source mode editor instead.

* *

If endText is specified any selected text will be placed between * text and endText. If no text is selected text and endText will * just be concatenate together.

* *

The cursor will be placed after the text param. If endText is * specified the cursor will be placed before endText, so passing:
* * '[b]', '[/b]'

* *

Would cause the cursor to be placed:
* * [b]Selected text|[/b]

* * @param {String} text * @param {String} [endText=null] * @since 1.4.0 * @function * @name sourceEditorInsertText * @memberOf jQuery.sceditor.prototype */ base.sourceEditorInsertText = function (text, endText) { var range, scrollTop, currentValue, startPos = sourceEditor.selectionStart, endPos = sourceEditor.selectionEnd; scrollTop = sourceEditor.scrollTop; sourceEditor.focus(); // All browsers except IE < 9 if (typeof startPos !== 'undefined') { currentValue = sourceEditor.value; if (endText) { text += currentValue.substring(startPos, endPos) + endText; } sourceEditor.value = currentValue.substring(0, startPos) + text + currentValue.substring(endPos, currentValue.length); sourceEditor.selectionStart = (startPos + text.length) - (endText ? endText.length : 0); sourceEditor.selectionEnd = sourceEditor.selectionStart; // IE < 9 } else { range = globalDoc.selection.createRange(); if (endText) { text += range.text + endText; } range.text = text; if (endText) { range.moveEnd('character', 0 - endText.length); } range.moveStart('character', range.End - range.Start); range.select(); } sourceEditor.scrollTop = scrollTop; sourceEditor.focus(); triggerValueChanged(); }; /** * Gets the current instance of the rangeHelper class * for the editor. * * @return jQuery.sceditor.rangeHelper * @function * @name getRangeHelper * @memberOf jQuery.sceditor.prototype */ base.getRangeHelper = function () { return rangeHelper; }; /** * Gets the source editors textarea. * * This shouldn't be used to insert text * * @return {jQuery} * @function * @since 1.4.5 * @name sourceEditorCaret * @memberOf jQuery.sceditor.prototype */ base.sourceEditorCaret = function (position) { var range, ret = {}; sourceEditor.focus(); // All browsers except IE <= 8 if (typeof sourceEditor.selectionStart !== 'undefined') { if (position) { sourceEditor.selectionStart = position.start; sourceEditor.selectionEnd = position.end; } else { ret.start = sourceEditor.selectionStart; ret.end = sourceEditor.selectionEnd; } // IE8 and below } else { range = globalDoc.selection.createRange(); if (position) { range.moveEnd('character', position.end); range.moveStart('character', position.start); range.select(); } else { ret.start = range.Start; ret.end = range.End; } } return position ? this : ret; }; /** *

Gets the value of the editor.

* *

If the editor is in WYSIWYG mode it will return the filtered * HTML from it (converted to BBCode if using the BBCode plugin). * It it's in Source Mode it will return the unfiltered contents * of the source editor (if using the BBCode plugin this will be * BBCode again).

* * @since 1.3.5 * @return {string} * @function * @name val * @memberOf jQuery.sceditor.prototype */ /** *

Sets the value of the editor.

* *

If filter set true the val will be passed through the filter * function. If using the BBCode plugin it will pass the val to * the BBCode filter to convert any BBCode into HTML.

* * @param {String} val * @param {Boolean} [filter=true] * @return {this} * @since 1.3.5 * @function * @name val^2 * @memberOf jQuery.sceditor.prototype */ base.val = function (val, filter) { if (typeof val !== 'string') { return base.inSourceMode() ? base.getSourceEditorValue(false) : base.getWysiwygEditorValue(filter); } if (!base.inSourceMode()) { if (filter !== false && pluginManager.hasHandler('toWysiwyg')) { val = pluginManager.callOnlyFirst('toWysiwyg', val); } base.setWysiwygEditorValue(val); } else { base.setSourceEditorValue(val); } return base; }; /** *

Inserts HTML/BBCode into the editor

* *

If end is supplied any selected text will be placed between * start and end. If there is no selected text start and end * will be concatenate together.

* *

If the filter param is set to true, the HTML/BBCode will be * passed through any plugin filters. If using the BBCode plugin * this will convert any BBCode into HTML.

* * @param {String} start * @param {String} [end=null] * @param {Boolean} [filter=true] * @param {Boolean} [convertEmoticons=true] If to convert emoticons * @return {this} * @since 1.3.5 * @function * @name insert * @memberOf jQuery.sceditor.prototype */ /** *

Inserts HTML/BBCode into the editor

* *

If end is supplied any selected text will be placed between * start and end. If there is no selected text start and end * will be concatenate together.

* *

If the filter param is set to true, the HTML/BBCode will be * passed through any plugin filters. If using the BBCode plugin * this will convert any BBCode into HTML.

* *

If the allowMixed param is set to true, HTML any will not be * escaped

* * @param {String} start * @param {String} [end=null] * @param {Boolean} [filter=true] * @param {Boolean} [convertEmoticons=true] If to convert emoticons * @param {Boolean} [allowMixed=false] * @return {this} * @since 1.4.3 * @function * @name insert^2 * @memberOf jQuery.sceditor.prototype */ base.insert = function ( /*jshint maxparams: false */ start, end, filter, convertEmoticons, allowMixed ) { if (base.inSourceMode()) { base.sourceEditorInsertText(start, end); return base; } // Add the selection between start and end if (end) { var html = rangeHelper.selectedHtml(), $div = $('
').appendTo($('body')).hide().html(html); if (filter !== false && pluginManager.hasHandler('toSource')) { html = pluginManager.callOnlyFirst('toSource', html, $div); } $div.remove(); start += html + end; } // TODO: This filter should allow empty tags as it's inserting. if (filter !== false && pluginManager.hasHandler('toWysiwyg')) { start = pluginManager.callOnlyFirst('toWysiwyg', start, true); } // Convert any escaped HTML back into HTML if mixed is allowed if (filter !== false && allowMixed === true) { start = start.replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&'); } base.wysiwygEditorInsertHtml(start); return base; }; /** * Gets the WYSIWYG editors HTML value. * * If using a plugin that filters the Ht Ml like the BBCode plugin * it will return the result of the filtering (BBCode) unless the * filter param is set to false. * * @param {bool} [filter=true] * @return {string} * @function * @name getWysiwygEditorValue * @memberOf jQuery.sceditor.prototype */ base.getWysiwygEditorValue = function (filter) { var html; // Create a tmp node to store contents so it can be modified // without affecting anything else. var $tmp = $('
') .appendTo(document.body) .append($($wysiwygBody[0].childNodes).clone()); dom.fixNesting($tmp[0]); html = $tmp.html(); // filter the HTML and DOM through any plugins if (filter !== false && pluginManager.hasHandler('toSource')) { html = pluginManager.callOnlyFirst('toSource', html, $tmp); } $tmp.remove(); return html; }; /** * Gets the WYSIWYG editor's iFrame Body. * * @return {jQuery} * @function * @since 1.4.3 * @name getBody * @memberOf jQuery.sceditor.prototype */ base.getBody = function () { return $wysiwygBody; }; /** * Gets the WYSIWYG editors container area (whole iFrame). * * @return {Node} * @function * @since 1.4.3 * @name getContentAreaContainer * @memberOf jQuery.sceditor.prototype */ base.getContentAreaContainer = function () { return $wysiwygEditor; }; /** * Gets the text editor value * * If using a plugin that filters the text like the BBCode plugin * it will return the result of the filtering which is BBCode to * HTML so it will return HTML. If filter is set to false it will * just return the contents of the source editor (BBCode). * * @param {bool} [filter=true] * @return {string} * @function * @since 1.4.0 * @name getSourceEditorValue * @memberOf jQuery.sceditor.prototype */ base.getSourceEditorValue = function (filter) { var val = $sourceEditor.val(); if (filter !== false && pluginManager.hasHandler('toWysiwyg')) { val = pluginManager.callOnlyFirst('toWysiwyg', val); } return val; }; /** * Sets the WYSIWYG HTML editor value. Should only be the HTML * contained within the body tags * * @param {string} value * @function * @name setWysiwygEditorValue * @memberOf jQuery.sceditor.prototype */ base.setWysiwygEditorValue = function (value) { if (!value) { value = '

' + (IE_VER ? '' : '
') + '

'; } $wysiwygBody[0].innerHTML = value; replaceEmoticons($wysiwygBody[0]); appendNewLine(); triggerValueChanged(); }; /** * Sets the text editor value * * @param {string} value * @function * @name setSourceEditorValue * @memberOf jQuery.sceditor.prototype */ base.setSourceEditorValue = function (value) { $sourceEditor.val(value); triggerValueChanged(); }; /** * Updates the textarea that the editor is replacing * with the value currently inside the editor. * * @function * @name updateOriginal * @since 1.4.0 * @memberOf jQuery.sceditor.prototype */ base.updateOriginal = function () { $original.val(base.val()); }; /** * Replaces any emoticon codes in the passed HTML * with their emoticon images * @private */ replaceEmoticons = function (node) { // TODO: Make this tag configurable. if (!options.emoticonsEnabled || $(node).parents('code').length) { return; } var doc = node.ownerDocument, whitespace = '\\s|\xA0|\u2002|\u2003|\u2009| ', emoticonCodes = [], emoticonRegex = [], emoticons = $.extend( {}, options.emoticons.more, options.emoticons.dropdown, options.emoticons.hidden ); // TODO: cache the emoticonCodes and emoticonCodes objects and share them with // the AYT converstion $.each(emoticons, function (key) { if (options.emoticonsCompat) { emoticonRegex[key] = new RegExp( '(>|^|' + whitespace + ')' + escape.regex(key) + '($|<|' + whitespace + ')' ); } emoticonCodes.push(key); }); // TODO: tidy below var convertEmoticons = function (node) { node = node.firstChild; while (node) { var parts, key, emoticon, parsedHtml, emoticonIdx, nextSibling, matchPos, nodeParent = node.parentNode, nodeValue = node.nodeValue; // All none textnodes if (node.nodeType !== 3) { // TODO: Make this tag configurable. if (!$(node).is('code')) { convertEmoticons(node); } } else if (nodeValue) { emoticonIdx = emoticonCodes.length; while (emoticonIdx--) { key = emoticonCodes[emoticonIdx]; matchPos = options.emoticonsCompat ? nodeValue.search(emoticonRegex[key]) : nodeValue.indexOf(key); if (matchPos > -1) { nextSibling = node.nextSibling; emoticon = emoticons[key]; parts = nodeValue .substr(matchPos).split(key); nodeValue = nodeValue .substr(0, matchPos) + parts.shift(); node.nodeValue = nodeValue; parsedHtml = dom.parseHTML(_tmpl('emoticon', { key: key, url: emoticon.url || emoticon, tooltip: emoticon.tooltip || key }), doc); nodeParent.insertBefore( parsedHtml[0], nextSibling ); nodeParent.insertBefore( doc.createTextNode(parts.join(key)), nextSibling ); } } } node = node.nextSibling; } }; convertEmoticons(node); if (options.emoticonsCompat) { currentEmoticons = $wysiwygBody .find('img[data-sceditor-emoticon]'); } }; /** * If the editor is in source code mode * * @return {bool} * @function * @name inSourceMode * @memberOf jQuery.sceditor.prototype */ base.inSourceMode = function () { return $editorContainer.hasClass('sourceMode'); }; /** * Gets if the editor is in sourceMode * * @return boolean * @function * @name sourceMode * @memberOf jQuery.sceditor.prototype */ /** * Sets if the editor is in sourceMode * * @param {bool} enable * @return {this} * @function * @name sourceMode^2 * @memberOf jQuery.sceditor.prototype */ base.sourceMode = function (enable) { var inSourceMode = base.inSourceMode(); if (typeof enable !== 'boolean') { return inSourceMode; } if ((inSourceMode && !enable) || (!inSourceMode && enable)) { base.toggleSourceMode(); } return base; }; /** * Switches between the WYSIWYG and source modes * * @function * @name toggleSourceMode * @since 1.4.0 * @memberOf jQuery.sceditor.prototype */ base.toggleSourceMode = function () { var sourceMode = base.inSourceMode(); // don't allow switching to WYSIWYG if doesn't support it if (!browser.isWysiwygSupported && sourceMode) { return; } if (!sourceMode) { rangeHelper.saveRange(); rangeHelper.clear(); } base.blur(); if (sourceMode) { base.setWysiwygEditorValue(base.getSourceEditorValue()); } else { base.setSourceEditorValue(base.getWysiwygEditorValue()); } lastRange = null; $sourceEditor.toggle(); $wysiwygEditor.toggle(); $editorContainer .toggleClass('wysiwygMode', sourceMode) .toggleClass('sourceMode', !sourceMode); updateToolBar(); updateActiveButtons(); }; /** * Gets the selected text of the source editor * @return {String} * @private */ sourceEditorSelectedText = function () { sourceEditor.focus(); if (typeof sourceEditor.selectionStart !== 'undefined') { return sourceEditor.value.substring( sourceEditor.selectionStart, sourceEditor.selectionEnd ); } else { return globalDoc.selection.createRange().text; } }; /** * Handles the passed command * @private */ handleCommand = function (caller, cmd) { // check if in text mode and handle text commands if (base.inSourceMode()) { if (cmd.txtExec) { if ($.isArray(cmd.txtExec)) { base.sourceEditorInsertText.apply(base, cmd.txtExec); } else { cmd.txtExec.call( base, caller, sourceEditorSelectedText() ); } } } else if (cmd.exec) { if ($.isFunction(cmd.exec)) { cmd.exec.call(base, caller); } else { base.execCommand( cmd.exec, cmd.hasOwnProperty('execParam') ? cmd.execParam : null ); } } }; /** * Saves the current range. Needed for IE because it forgets * where the cursor was and what was selected * @private */ saveRange = function () { /* this is only needed for IE */ if (IE_VER) { lastRange = rangeHelper.selectedRange(); } }; /** * Executes a command on the WYSIWYG editor * * @param {String} command * @param {String|Boolean} [param] * @function * @name execCommand * @memberOf jQuery.sceditor.prototype */ base.execCommand = function (command, param) { var executed = false, commandObj = base.commands[command], $parentNode = $(rangeHelper.parentNode()); base.focus(); // TODO: make configurable // don't apply any commands to code elements if ($parentNode.is('code') || $parentNode.parents('code').length !== 0) { return; } try { executed = $wysiwygDoc[0].execCommand(command, false, param); } catch (ex) {} // show error if execution failed and an error message exists if (!executed && commandObj && commandObj.errorMessage) { /*global alert:false*/ alert(base._(commandObj.errorMessage)); } updateActiveButtons(); }; /** * Checks if the current selection has changed and triggers * the selectionchanged event if it has. * * In browsers other than IE, it will check at most once every 100ms. * This is because only IE has a selection changed event. * @private */ checkSelectionChanged = function () { function check () { // rangeHelper could be null if editor was destroyed // before the timeout had finished if (rangeHelper && !rangeHelper.compare(currentSelection)) { currentSelection = rangeHelper.cloneSelected(); $editorContainer.trigger($.Event('selectionchanged')); } isSelectionCheckPending = false; } if (isSelectionCheckPending) { return; } isSelectionCheckPending = true; // In IE, this is only called on the selectionchange event so no // need to limit checking as it should always be valid to do. if (IE_VER) { check(); } else { setTimeout(check, 100); } }; /** * Checks if the current node has changed and triggers * the nodechanged event if it has * @private */ checkNodeChanged = function () { // check if node has changed var oldNode, node = rangeHelper.parentNode(); if (currentNode !== node) { oldNode = currentNode; currentNode = node; currentBlockNode = rangeHelper.getFirstBlockParent(node); $editorContainer.trigger($.Event('nodechanged', { oldNode: oldNode, newNode: currentNode })); } }; /** *

Gets the current node that contains the selection/caret in * WYSIWYG mode.

* *

Will be null in sourceMode or if there is no selection.

* @return {Node} * @function * @name currentNode * @memberOf jQuery.sceditor.prototype */ base.currentNode = function () { return currentNode; }; /** *

Gets the first block level node that contains the * selection/caret in WYSIWYG mode.

* *

Will be null in sourceMode or if there is no selection.

* @return {Node} * @function * @name currentBlockNode * @memberOf jQuery.sceditor.prototype * @since 1.4.4 */ base.currentBlockNode = function () { return currentBlockNode; }; /** * Updates if buttons are active or not * @private */ updateActiveButtons = function (e) { var firstBlock, parent; var activeClass = 'active'; var doc = $wysiwygDoc[0]; var isSource = base.sourceMode(); if (base.readOnly()) { $toolbar.find(activeClass).removeClass(activeClass); return; } if (!isSource) { parent = e ? e.newNode : rangeHelper.parentNode(); firstBlock = rangeHelper.getFirstBlockParent(parent); } for (var i = 0; i < btnStateHandlers.length; i++) { var state = 0; var $btn = toolbarButtons[btnStateHandlers[i].name]; var stateFn = btnStateHandlers[i].state; var isDisabled = (isSource && !$btn.data('sceditor-txtmode')) || (!isSource && !$btn.data('sceditor-wysiwygmode')); if (typeof stateFn === 'string') { if (!isSource) { try { state = doc.queryCommandEnabled(stateFn) ? 0 : -1; /*jshint maxdepth: false*/ if (state > -1) { state = doc.queryCommandState(stateFn) ? 1 : 0; } } catch (ex) {} } } else if (!isDisabled) { state = stateFn.call(base, parent, firstBlock); } $btn .toggleClass('disabled', isDisabled || state < 0) .toggleClass(activeClass, state > 0); } }; /** * Handles any key press in the WYSIWYG editor * * @private */ handleKeyPress = function (e) { var $closestTag, br, brParent, lastChild; // TODO: improve this so isn't set list, probably should just use // dom.hasStyling to all block parents and if one does insert a br var DUPLICATED_TAGS = 'code,blockquote,pre'; var LIST_TAGS = 'li,ul,ol'; // FF bug: https://bugzilla.mozilla.org/show_bug.cgi?id=501496 if (e.originalEvent.defaultPrevented) { return; } base.closeDropDown(); $closestTag = $(currentBlockNode) .closest(DUPLICATED_TAGS + ',' + LIST_TAGS) .first(); // "Fix" (OK it's a cludge) for blocklevel elements being // duplicated in some browsers when enter is pressed instead // of inserting a newline if (e.which === 13 && $closestTag.length && !$closestTag.is(LIST_TAGS)) { lastRange = null; br = $wysiwygDoc[0].createElement('br'); rangeHelper.insertNode(br); // Last
of a block will be collapsed unless it is // IE < 11 so need to make sure the
that was inserted // isn't the last node of a block. if (!IE_BR_FIX) { brParent = br.parentNode; lastChild = brParent.lastChild; // Sometimes an empty next node is created after the
if (lastChild && lastChild.nodeType === 3 && lastChild.nodeValue === '') { brParent.removeChild(lastChild); lastChild = brParent.lastChild; } // If this is the last BR of a block and the previous // sibling is inline then will need an extra BR. This // is needed because the last BR of a block will be // collapsed. Fixes issue #248 if (!dom.isInline(brParent, true) && lastChild === br && dom.isInline(br.previousSibling)) { rangeHelper.insertHTML('
'); } } return false; } }; /** * Makes sure that if there is a code or quote tag at the * end of the editor, that there is a new line after it. * * If there wasn't a new line at the end you wouldn't be able * to enter any text after a code/quote tag * @return {void} * @private */ appendNewLine = function () { var name, requiresNewLine, paragraph, body = $wysiwygBody[0]; dom.rTraverse(body, function (node) { name = node.nodeName.toLowerCase(); // TODO: Replace requireNewLineFix with just a block level fix for any // block that has styling and any block that isn't a plain

or

if ($.inArray(name, requireNewLineFix) > -1) { requiresNewLine = true; } // TODO: tidy this up // find the last non-empty text node or line break. if ((node.nodeType === 3 && !/^\s*$/.test(node.nodeValue)) || name === 'br' || (IE_BR_FIX && !node.firstChild && !dom.isInline(node, false))) { // this is the last text or br node, if its in a code or // quote tag then add a newline to the end of the editor if (requiresNewLine) { paragraph = $wysiwygDoc[0].createElement('p'); paragraph.className = 'sceditor-nlf'; paragraph.innerHTML = !IE_BR_FIX ? '
' : ''; body.appendChild(paragraph); } return false; } }); }; /** * Handles form reset event * @private */ handleFormReset = function () { base.val($original.val()); }; /** * Handles any mousedown press in the WYSIWYG editor * @private */ handleMouseDown = function () { base.closeDropDown(); lastRange = null; }; /** * Handles the window resize event. Needed to resize then editor * when the window size changes in fluid designs. * @ignore */ handleWindowResize = function () { var height = options.height, width = options.width; if (!base.maximize()) { if ((height && height.toString().indexOf('%') > -1) || (width && width.toString().indexOf('%') > -1)) { base.dimensions(width, height); } } else { base.dimensions('100%', '100%', false); } }; /** * Translates the string into the locale language. * * Replaces any {0}, {1}, {2}, ect. with the params provided. * * @param {string} str * @param {...String} args * @return {string} * @function * @name _ * @memberOf jQuery.sceditor.prototype */ base._ = function () { var undef, args = arguments; if (locale && locale[args[0]]) { args[0] = locale[args[0]]; } return args[0].replace(/\{(\d+)\}/g, function (str, p1) { return args[p1 - 0 + 1] !== undef ? args[p1 - 0 + 1] : '{' + p1 + '}'; }); }; /** * Passes events on to any handlers * @private * @return void */ handleEvent = function (e) { // Send event to all plugins pluginManager.call(e.type + 'Event', e, base); // convert the event into a custom event to send var prefix = e.target === sourceEditor ? 'scesrc' : 'scewys'; var customEvent = $.Event(e); customEvent.type = prefix + e.type; $editorContainer.trigger(customEvent, base); }; /** *

Binds a handler to the specified events

* *

This function only binds to a limited list of * supported events.
* The supported events are: *

    *
  • keyup
  • *
  • keydown
  • *
  • Keypress
  • *
  • blur
  • *
  • focus
  • *
  • nodechanged
    * When the current node containing the selection changes * in WYSIWYG mode
  • *
  • contextmenu
  • *
  • selectionchanged
  • *
  • valuechanged
  • *
*

* *

The events param should be a string containing the event(s) * to bind this handler to. If multiple, they should be separated * by spaces.

* * @param {String} events * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource if to exclude adding this handler * to the source editor * @return {this} * @function * @name bind * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.bind = function (events, handler, excludeWysiwyg, excludeSource) { events = events.split(' '); var i = events.length; while (i--) { if ($.isFunction(handler)) { // Use custom events to allow passing the instance as the // 2nd argument. // Also allows unbinding without unbinding the editors own // event handlers. if (!excludeWysiwyg) { $editorContainer.on('scewys' + events[i], handler); } if (!excludeSource) { $editorContainer.on('scesrc' + events[i], handler); } // Start sending value changed events if (events[i] === 'valuechanged') { triggerValueChanged.hasHandler = true; } } } return base; }; /** * Unbinds an event that was bound using bind(). * * @param {String} events * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude unbinding this * handler from the WYSIWYG editor * @param {Boolean} excludeSource if to exclude unbinding this * handler from the source editor * @return {this} * @function * @name unbind * @memberOf jQuery.sceditor.prototype * @since 1.4.1 * @see bind */ base.unbind = function ( events, handler, excludeWysiwyg, excludeSource ) { events = events.split(' '); var i = events.length; while (i--) { if ($.isFunction(handler)) { if (!excludeWysiwyg) { $editorContainer.off('scewys' + events[i], handler); } if (!excludeSource) { $editorContainer.off('scesrc' + events[i], handler); } } } return base; }; /** * Blurs the editors input area * * @return {this} * @function * @name blur * @memberOf jQuery.sceditor.prototype * @since 1.3.6 */ /** * Adds a handler to the editors blur event * * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource if to exclude adding this handler * to the source editor * @return {this} * @function * @name blur^2 * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.blur = function (handler, excludeWysiwyg, excludeSource) { if ($.isFunction(handler)) { base.bind('blur', handler, excludeWysiwyg, excludeSource); } else if (!base.sourceMode()) { $wysiwygBody.blur(); } else { $sourceEditor.blur(); } return base; }; /** * Focuses the editors input area * * @return {this} * @function * @name focus * @memberOf jQuery.sceditor.prototype */ /** * Adds an event handler to the focus event * * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource if to exclude adding this handler * to the source editor * @return {this} * @function * @name focus^2 * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.focus = function (handler, excludeWysiwyg, excludeSource) { if ($.isFunction(handler)) { base.bind('focus', handler, excludeWysiwyg, excludeSource); } else if (!base.inSourceMode()) { var container, rng = rangeHelper.selectedRange(); // Fix FF bug where it shows the cursor in the wrong place // if the editor hasn't had focus before. See issue #393 if (!currentSelection && !rangeHelper.hasSelection()) { autofocus(); } // Check if cursor is set after a BR when the BR is the only // child of the parent. In Firefox this causes a line break // to occur when something is typed. See issue #321 if (!IE_BR_FIX && rng && rng.endOffset === 1 && rng.collapsed) { container = rng.endContainer; if (container && container.childNodes.length === 1 && $(container.firstChild).is('br')) { rng.setStartBefore(container.firstChild); rng.collapse(true); rangeHelper.selectRange(rng); } } wysiwygEditor.contentWindow.focus(); $wysiwygBody[0].focus(); // Needed for IE < 9 if (lastRange) { rangeHelper.selectRange(lastRange); // remove the stored range after being set. // If the editor loses focus it should be // saved again. lastRange = null; } } else { sourceEditor.focus(); } updateActiveButtons(); return base; }; /** * Adds a handler to the key down event * * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource If to exclude adding this handler * to the source editor * @return {this} * @function * @name keyDown * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.keyDown = function (handler, excludeWysiwyg, excludeSource) { return base.bind('keydown', handler, excludeWysiwyg, excludeSource); }; /** * Adds a handler to the key press event * * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource If to exclude adding this handler * to the source editor * @return {this} * @function * @name keyPress * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.keyPress = function (handler, excludeWysiwyg, excludeSource) { return base .bind('keypress', handler, excludeWysiwyg, excludeSource); }; /** * Adds a handler to the key up event * * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource If to exclude adding this handler * to the source editor * @return {this} * @function * @name keyUp * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.keyUp = function (handler, excludeWysiwyg, excludeSource) { return base.bind('keyup', handler, excludeWysiwyg, excludeSource); }; /** *

Adds a handler to the node changed event.

* *

Happens whenever the node containing the selection/caret * changes in WYSIWYG mode.

* * @param {Function} handler * @return {this} * @function * @name nodeChanged * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.nodeChanged = function (handler) { return base.bind('nodechanged', handler, false, true); }; /** *

Adds a handler to the selection changed event

* *

Happens whenever the selection changes in WYSIWYG mode.

* * @param {Function} handler * @return {this} * @function * @name selectionChanged * @memberOf jQuery.sceditor.prototype * @since 1.4.1 */ base.selectionChanged = function (handler) { return base.bind('selectionchanged', handler, false, true); }; /** *

Adds a handler to the value changed event

* *

Happens whenever the current editor value changes.

* *

Whenever anything is inserted, the value changed or * 1.5 secs after text is typed. If a space is typed it will * cause the event to be triggered immediately instead of * after 1.5 seconds

* * @param {Function} handler * @param {Boolean} excludeWysiwyg If to exclude adding this handler * to the WYSIWYG editor * @param {Boolean} excludeSource If to exclude adding this handler * to the source editor * @return {this} * @function * @name valueChanged * @memberOf jQuery.sceditor.prototype * @since 1.4.5 */ base.valueChanged = function (handler, excludeWysiwyg, excludeSource) { return base .bind('valuechanged', handler, excludeWysiwyg, excludeSource); }; /** * Emoticons keypress handler * @private */ emoticonsKeyPress = function (e) { var replacedEmoticon, cachePos = 0, emoticonsCache = base.emoticonsCache, curChar = String.fromCharCode(e.which); // TODO: Make configurable if ($(currentBlockNode).is('code') || $(currentBlockNode).parents('code').length) { return; } if (!emoticonsCache) { emoticonsCache = []; $.each($.extend( {}, options.emoticons.more, options.emoticons.dropdown, options.emoticons.hidden ), function (key, url) { emoticonsCache[cachePos++] = [ key, _tmpl('emoticon', { key: key, url: url.url || url, tooltip: url.tooltip || key }) ]; }); emoticonsCache.sort(function (a, b) { return a[0].length - b[0].length; }); base.emoticonsCache = emoticonsCache; base.longestEmoticonCode = emoticonsCache[emoticonsCache.length - 1][0].length; } replacedEmoticon = rangeHelper.replaceKeyword( base.emoticonsCache, true, true, base.longestEmoticonCode, options.emoticonsCompat, curChar ); if (replacedEmoticon && options.emoticonsCompat) { currentEmoticons = $wysiwygBody .find('img[data-sceditor-emoticon]'); return /^\s$/.test(curChar); } return !replacedEmoticon; }; /** * Makes sure emoticons are surrounded by whitespace * @private */ emoticonsCheckWhitespace = function () { if (!currentEmoticons.length) { return; } var prev, next, parent, range, previousText, rangeStartContainer, currentBlock = base.currentBlockNode(), rangeStart = false, noneWsRegex = /[^\s\xA0\u2002\u2003\u2009\u00a0]+/; currentEmoticons = $.map(currentEmoticons, function (emoticon) { // Ignore emoticons that have been removed from DOM if (!emoticon || !emoticon.parentNode) { return null; } if (!$.contains(currentBlock, emoticon)) { return emoticon; } prev = emoticon.previousSibling; next = emoticon.nextSibling; previousText = prev.nodeValue; // For IE's HTMLPhraseElement if (previousText === null) { previousText = prev.innerText || ''; } if ((!prev || !noneWsRegex.test(prev.nodeValue.slice(-1))) && (!next || !noneWsRegex.test((next.nodeValue || '')[0]))) { return emoticon; } parent = emoticon.parentNode; range = rangeHelper.cloneSelected(); rangeStartContainer = range.startContainer; previousText = previousText + $(emoticon).data('sceditor-emoticon'); // Store current caret position if (rangeStartContainer === next) { rangeStart = previousText.length + range.startOffset; } else if (rangeStartContainer === currentBlock && currentBlock.childNodes[range.startOffset] === next) { rangeStart = previousText.length; } else if (rangeStartContainer === prev) { rangeStart = range.startOffset; } if (!next || next.nodeType !== 3) { next = parent.insertBefore( parent.ownerDocument.createTextNode(''), next ); } next.insertData(0, previousText); parent.removeChild(prev); parent.removeChild(emoticon); // Need to update the range starting // position if it has been modified if (rangeStart !== false) { range.setStart(next, rangeStart); range.collapse(true); rangeHelper.selectRange(range); } return null; }); }; /** * Gets if emoticons are currently enabled * @return {boolean} * @function * @name emoticons * @memberOf jQuery.sceditor.prototype * @since 1.4.2 */ /** * Enables/disables emoticons * * @param {boolean} enable * @return {this} * @function * @name emoticons^2 * @memberOf jQuery.sceditor.prototype * @since 1.4.2 */ base.emoticons = function (enable) { if (!enable && enable !== false) { return options.emoticonsEnabled; } options.emoticonsEnabled = enable; if (enable) { $wysiwygBody.keypress(emoticonsKeyPress); if (!base.sourceMode()) { rangeHelper.saveRange(); replaceEmoticons($wysiwygBody[0]); currentEmoticons = $wysiwygBody .find('img[data-sceditor-emoticon]'); triggerValueChanged(false); rangeHelper.restoreRange(); } } else { $wysiwygBody .find('img[data-sceditor-emoticon]') .replaceWith(function () { return $(this).data('sceditor-emoticon'); }); currentEmoticons = []; $wysiwygBody.off('keypress', emoticonsKeyPress); triggerValueChanged(); } return base; }; /** * Gets the current WYSIWYG editors inline CSS * * @return {string} * @function * @name css * @memberOf jQuery.sceditor.prototype * @since 1.4.3 */ /** * Sets inline CSS for the WYSIWYG editor * * @param {string} css * @return {this} * @function * @name css^2 * @memberOf jQuery.sceditor.prototype * @since 1.4.3 */ base.css = function (css) { if (!inlineCss) { inlineCss = $('' + '' + '' + '' + '

' + '', toolbarButton: '' + '
{dispName}
', emoticon: '', 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} name * @param {Object} params * @param {Boolean} createHtml * @private */ return 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; }; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); /***/ }, /* 9 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require) { 'use strict'; var $ = __webpack_require__(1); var IE_VER = __webpack_require__(6).ie; var _tmpl = __webpack_require__(8); // In IE < 11 a BR at the end of a block level element // causes a line break. In all other browsers it's collapsed. var IE_BR_FIX = IE_VER && IE_VER < 11; /** * Map of all the commands for SCEditor * @type {Object} * @name commands * @memberOf jQuery.sceditor */ var defaultCommnds = { // START_COMMAND: Bold bold: { exec: 'bold', tooltip: 'Bold', shortcut: 'Ctrl+B' }, // END_COMMAND // START_COMMAND: Italic italic: { exec: 'italic', tooltip: 'Italic', shortcut: 'Ctrl+I' }, // END_COMMAND // START_COMMAND: Underline underline: { exec: 'underline', tooltip: 'Underline', shortcut: 'Ctrl+U' }, // END_COMMAND // START_COMMAND: Strikethrough strike: { exec: 'strikethrough', tooltip: 'Strikethrough' }, // END_COMMAND // START_COMMAND: Subscript subscript: { exec: 'subscript', tooltip: 'Subscript' }, // END_COMMAND // START_COMMAND: Superscript superscript: { exec: 'superscript', tooltip: 'Superscript' }, // END_COMMAND // START_COMMAND: Left left: { exec: 'justifyleft', tooltip: 'Align left' }, // END_COMMAND // START_COMMAND: Centre center: { exec: 'justifycenter', tooltip: 'Center' }, // END_COMMAND // START_COMMAND: Right right: { exec: 'justifyright', tooltip: 'Align right' }, // END_COMMAND // START_COMMAND: Justify justify: { exec: 'justifyfull', tooltip: 'Justify' }, // END_COMMAND // START_COMMAND: Font font: { _dropDown: function (editor, caller, callback) { var fontIdx = 0, fonts = editor.opts.fonts.split(','), content = $('
'), /** @private */ clickFunc = function () { callback($(this).data('font')); editor.closeDropDown(true); return false; }; for (; fontIdx < fonts.length; fontIdx++) { content.append( _tmpl('fontOpt', { font: fonts[fontIdx] }, true).click(clickFunc) ); } editor.createDropDown(caller, 'font-picker', content); }, exec: function (caller) { var editor = this; defaultCommnds.font._dropDown( editor, caller, function (fontName) { editor.execCommand('fontname', fontName); } ); }, tooltip: 'Font Name' }, // END_COMMAND // START_COMMAND: Size size: { _dropDown: function (editor, caller, callback) { var content = $('
'), /** @private */ clickFunc = function (e) { callback($(this).data('size')); editor.closeDropDown(true); e.preventDefault(); }; for (var i = 1; i <= 7; i++) { content.append(_tmpl('sizeOpt', { size: i }, true).click(clickFunc)); } editor.createDropDown(caller, 'fontsize-picker', content); }, exec: function (caller) { var editor = this; defaultCommnds.size._dropDown( editor, caller, function (fontSize) { editor.execCommand('fontsize', fontSize); } ); }, tooltip: 'Font Size' }, // END_COMMAND // START_COMMAND: Colour color: { _dropDown: function (editor, caller, callback) { var i, x, color, colors, genColor = {r: 255, g: 255, b: 255}, content = $('
'), colorColumns = editor.opts.colors ? editor.opts.colors.split('|') : new Array(21), // IE is slow at string concatenation so use an array html = [], cmd = defaultCommnds.color; if (!cmd._htmlCache) { for (i = 0; i < colorColumns.length; ++i) { colors = colorColumns[i] ? colorColumns[i].split(',') : new Array(21); html.push('
'); for (x = 0; x < colors.length; ++x) { // use pre defined colour if can otherwise use the // generated color color = colors[x] || '#' + genColor.r.toString(16) + genColor.g.toString(16) + genColor.b.toString(16); html.push( '' ); if (x % 5 === 0) { genColor.g -= 51; genColor.b = 255; } else { genColor.b -= 51; } } html.push('
'); if (i % 5 === 0) { genColor.r -= 51; genColor.g = 255; genColor.b = 255; } else { genColor.g = 255; genColor.b = 255; } } cmd._htmlCache = html.join(''); } content.append(cmd._htmlCache) .find('a') .click(function (e) { callback($(this).attr('data-color')); editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'color-picker', content); }, exec: function (caller) { var editor = this; defaultCommnds.color._dropDown( editor, caller, function (color) { editor.execCommand('forecolor', color); } ); }, tooltip: 'Font Color' }, // END_COMMAND // START_COMMAND: Remove Format removeformat: { exec: 'removeformat', tooltip: 'Remove Formatting' }, // END_COMMAND // START_COMMAND: Cut cut: { exec: 'cut', tooltip: 'Cut', errorMessage: 'Your browser does not allow the cut command. ' + 'Please use the keyboard shortcut Ctrl/Cmd-X' }, // END_COMMAND // START_COMMAND: Copy copy: { exec: 'copy', tooltip: 'Copy', errorMessage: 'Your browser does not allow the copy command. ' + 'Please use the keyboard shortcut Ctrl/Cmd-C' }, // END_COMMAND // START_COMMAND: Paste paste: { exec: 'paste', tooltip: 'Paste', errorMessage: 'Your browser does not allow the paste command. ' + 'Please use the keyboard shortcut Ctrl/Cmd-V' }, // END_COMMAND // START_COMMAND: Paste Text pastetext: { exec: function (caller) { var val, content, editor = this; content = _tmpl('pastetext', { label: editor._( 'Paste your text inside the following box:' ), insert: editor._('Insert') }, true); content.find('.button').click(function (e) { val = content.find('#txt').val(); if (val) { editor.wysiwygEditorInsertText(val); } editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'pastetext', content); }, tooltip: 'Paste Text' }, // END_COMMAND // START_COMMAND: Bullet List bulletlist: { exec: 'insertunorderedlist', tooltip: 'Bullet list' }, // END_COMMAND // START_COMMAND: Ordered List orderedlist: { exec: 'insertorderedlist', tooltip: 'Numbered list' }, // END_COMMAND // START_COMMAND: Indent indent: { state: function (parents, firstBlock) { // Only works with lists, for now // This is a nested list, so it will always work var range, startParent, endParent, $firstBlock = $(firstBlock), parentLists = $firstBlock.parents('ul,ol,menu'), parentList = parentLists.first(); // in case it's a list with only a single
  • if (parentLists.length > 1 || parentList.children().length > 1) { return 0; } if ($firstBlock.is('ul,ol,menu')) { // if the whole list is selected, then this must be // invalidated because the browser will place a //
    there range = this.getRangeHelper().selectedRange(); if (window.Range && range instanceof Range) { startParent = range.startContainer.parentNode; endParent = range.endContainer.parentNode; // TODO: could use nodeType for this? // Maybe just check the firstBlock contains both the start and end containers // Select the tag, not the textNode // (that's why the parentNode) if (startParent !== startParent.parentNode.firstElementChild || // work around a bug in FF ($(endParent).is('li') && endParent !== endParent.parentNode.lastElementChild)) { return 0; } // it's IE... As it is impossible to know well when to // accept, better safe than sorry } else { return $firstBlock.is('li,ul,ol,menu') ? 0 : -1; } } return -1; }, exec: function () { var editor = this, $elm = $(editor.getRangeHelper().getFirstBlockParent()); editor.focus(); // An indent system is quite complicated as there are loads // of complications and issues around how to indent text // As default, let's just stay with indenting the lists, // at least, for now. if ($elm.parents('ul,ol,menu')) { editor.execCommand('indent'); } }, tooltip: 'Add indent' }, // END_COMMAND // START_COMMAND: Outdent outdent: { state: function (parents, firstBlock) { return $(firstBlock).is('ul,ol,menu') || $(firstBlock).parents('ul,ol,menu').length > 0 ? 0 : -1; }, exec: function () { var editor = this, $elm = $(editor.getRangeHelper().getFirstBlockParent()); if ($elm.parents('ul,ol,menu')) { editor.execCommand('outdent'); } }, tooltip: 'Remove one indent' }, // END_COMMAND // START_COMMAND: Table table: { forceNewLineAfter: ['table'], exec: function (caller) { var editor = this, content = _tmpl('table', { rows: editor._('Rows:'), cols: editor._('Cols:'), insert: editor._('Insert') }, true); content.find('.button').click(function (e) { var row, col, rows = content.find('#rows').val() - 0, cols = content.find('#cols').val() - 0, html = ''; if (rows < 1 || cols < 1) { return; } for (row = 0; row < rows; row++) { html += ''; for (col = 0; col < cols; col++) { html += ''; } html += ''; } html += '
    ' + (IE_BR_FIX ? '' : '
    ') + '
    '; editor.wysiwygEditorInsertHtml(html); editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'inserttable', content); }, tooltip: 'Insert a table' }, // END_COMMAND // START_COMMAND: Horizontal Rule horizontalrule: { exec: 'inserthorizontalrule', tooltip: 'Insert a horizontal rule' }, // END_COMMAND // START_COMMAND: Code code: { forceNewLineAfter: ['code'], exec: function () { this.wysiwygEditorInsertHtml( '', (IE_BR_FIX ? '' : '
    ') + '
    ' ); }, tooltip: 'Code' }, // END_COMMAND // START_COMMAND: Image image: { exec: function (caller) { var editor = this, content = _tmpl('image', { url: editor._('URL:'), width: editor._('Width (optional):'), height: editor._('Height (optional):'), insert: editor._('Insert') }, true); content.find('.button').click(function (e) { var val = content.find('#image').val(), width = content.find('#width').val(), height = content.find('#height').val(), attrs = ''; if (width) { attrs += ' width="' + width + '"'; } if (height) { attrs += ' height="' + height + '"'; } if (val) { editor.wysiwygEditorInsertHtml( '' ); } editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'insertimage', content); }, tooltip: 'Insert an image' }, // END_COMMAND // START_COMMAND: E-mail email: { exec: function (caller) { var editor = this, content = _tmpl('email', { label: editor._('E-mail:'), desc: editor._('Description (optional):'), insert: editor._('Insert') }, true); content.find('.button').click(function (e) { var val = content.find('#email').val(), description = content.find('#des').val(); if (val) { // needed for IE to reset the last range editor.focus(); if (!editor.getRangeHelper().selectedHtml() || description) { description = description || val; editor.wysiwygEditorInsertHtml( '' + description + '' ); } else { editor.execCommand('createlink', 'mailto:' + val); } } editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'insertemail', content); }, tooltip: 'Insert an email' }, // END_COMMAND // START_COMMAND: Link link: { exec: function (caller) { var editor = this, content = _tmpl('link', { url: editor._('URL:'), desc: editor._('Description (optional):'), ins: editor._('Insert') }, true); content.find('.button').click(function (e) { var val = content.find('#link').val(), description = content.find('#des').val(); if (val) { // needed for IE to restore the last range editor.focus(); // If there is no selected text then must set the URL as // the text. Most browsers do this automatically, sadly // IE doesn't. if (!editor.getRangeHelper().selectedHtml() || description) { description = description || val; editor.wysiwygEditorInsertHtml( '' + description + '' ); } else { editor.execCommand('createlink', val); } } editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'insertlink', content); }, tooltip: 'Insert a link' }, // END_COMMAND // START_COMMAND: Unlink unlink: { state: function () { var $current = $(this.currentNode()); return $current.is('a') || $current.parents('a').length > 0 ? 0 : -1; }, exec: function () { var $current = $(this.currentNode()), $anchor = $current.is('a') ? $current : $current.parents('a').first(); if ($anchor.length) { $anchor.replaceWith($anchor.contents()); } }, tooltip: 'Unlink' }, // END_COMMAND // START_COMMAND: Quote quote: { forceNewLineAfter: ['blockquote'], exec: function (caller, html, author) { var before = '
    ', end = '
    '; // if there is HTML passed set end to null so any selected // text is replaced if (html) { author = (author ? '' + author + '' : ''); before = before + author + html + end; end = null; // if not add a newline to the end of the inserted quote } else if (this.getRangeHelper().selectedHtml() === '') { end = (IE_BR_FIX ? '' : '
    ') + end; } this.wysiwygEditorInsertHtml(before, end); }, tooltip: 'Insert a Quote' }, // END_COMMAND // START_COMMAND: Emoticons emoticon: { exec: function (caller) { var editor = this; var createContent = function (includeMore) { var $moreLink, emoticonsCompat = editor.opts.emoticonsCompat, rangeHelper = editor.getRangeHelper(), startSpace = emoticonsCompat && rangeHelper.getOuterText(true, 1) !== ' ' ? ' ' : '', endSpace = emoticonsCompat && rangeHelper.getOuterText(false, 1) !== ' ' ? ' ' : '', $content = $('
    '), $line = $('
    ').appendTo($content), perLine = 0, emoticons = $.extend( {}, editor.opts.emoticons.dropdown, includeMore ? editor.opts.emoticons.more : {} ); $.each(emoticons, function () { perLine++; }); perLine = Math.sqrt(perLine); $.each(emoticons, function (code, emoticon) { $line.append( $('').attr({ src: emoticon.url || emoticon, alt: code, title: emoticon.tooltip || code }).click(function () { editor.insert(startSpace + $(this).attr('alt') + endSpace, null, false).closeDropDown(true); return false; }) ); if ($line.children().length >= perLine) { $line = $('
    ').appendTo($content); } }); if (!includeMore && editor.opts.emoticons.more) { $moreLink = $( '' + editor._('More') + '' ).click(function () { editor.createDropDown( caller, 'more-emoticons', createContent(true) ); return false; }); $content.append($moreLink); } return $content; }; editor.createDropDown( caller, 'emoticons', createContent(false) ); }, txtExec: function (caller) { defaultCommnds.emoticon.exec.call(this, caller); }, tooltip: 'Insert an emoticon' }, // END_COMMAND // START_COMMAND: YouTube youtube: { _dropDown: function (editor, caller, handleIdFunc) { var matches, content = _tmpl('youtubeMenu', { label: editor._('Video URL:'), insert: editor._('Insert') }, true); content.find('.button').click(function (e) { var val = content .find('#link') .val(); if (val) { matches = val.match( /(?:v=|v\/|embed\/|youtu.be\/)(.{11})/ ); if (matches) { val = matches[1]; } if (/^[a-zA-Z0-9_\-]{11}$/.test(val)) { handleIdFunc(val); } else { /*global alert:false*/ alert('Invalid YouTube video'); } } editor.closeDropDown(true); e.preventDefault(); }); editor.createDropDown(caller, 'insertlink', content); }, exec: function (caller) { var editor = this; defaultCommnds.youtube._dropDown( editor, caller, function (id) { editor.wysiwygEditorInsertHtml(_tmpl('youtube', { id: id })); } ); }, tooltip: 'Insert a YouTube video' }, // END_COMMAND // START_COMMAND: Date date: { _date: function (editor) { var now = new Date(), year = now.getYear(), month = now.getMonth() + 1, day = now.getDate(); if (year < 2000) { year = 1900 + year; } if (month < 10) { month = '0' + month; } if (day < 10) { day = '0' + day; } return editor.opts.dateFormat .replace(/year/i, year) .replace(/month/i, month) .replace(/day/i, day); }, exec: function () { this.insertText(defaultCommnds.date._date(this)); }, txtExec: function () { this.insertText(defaultCommnds.date._date(this)); }, tooltip: 'Insert current date' }, // END_COMMAND // START_COMMAND: Time time: { _time: function () { var now = new Date(), hours = now.getHours(), mins = now.getMinutes(), secs = now.getSeconds(); if (hours < 10) { hours = '0' + hours; } if (mins < 10) { mins = '0' + mins; } if (secs < 10) { secs = '0' + secs; } return hours + ':' + mins + ':' + secs; }, exec: function () { this.insertText(defaultCommnds.time._time()); }, txtExec: function () { this.insertText(defaultCommnds.time._time()); }, tooltip: 'Insert current time' }, // END_COMMAND // START_COMMAND: Ltr ltr: { state: function (parents, firstBlock) { return firstBlock && firstBlock.style.direction === 'ltr'; }, exec: function () { var editor = this, elm = editor.getRangeHelper().getFirstBlockParent(), $elm = $(elm); editor.focus(); if (!elm || $elm.is('body')) { editor.execCommand('formatBlock', 'p'); elm = editor.getRangeHelper().getFirstBlockParent(); $elm = $(elm); if (!elm || $elm.is('body')) { return; } } if ($elm.css('direction') === 'ltr') { $elm.css('direction', ''); } else { $elm.css('direction', 'ltr'); } }, tooltip: 'Left-to-Right' }, // END_COMMAND // START_COMMAND: Rtl rtl: { state: function (parents, firstBlock) { return firstBlock && firstBlock.style.direction === 'rtl'; }, exec: function () { var editor = this, elm = editor.getRangeHelper().getFirstBlockParent(), $elm = $(elm); editor.focus(); if (!elm || $elm.is('body')) { editor.execCommand('formatBlock', 'p'); elm = editor.getRangeHelper().getFirstBlockParent(); $elm = $(elm); if (!elm || $elm.is('body')) { return; } } if ($elm.css('direction') === 'rtl') { $elm.css('direction', ''); } else { $elm.css('direction', 'rtl'); } }, tooltip: 'Right-to-Left' }, // END_COMMAND // START_COMMAND: Print print: { exec: 'print', tooltip: 'Print' }, // END_COMMAND // START_COMMAND: Maximize maximize: { state: function () { return this.maximize(); }, exec: function () { this.maximize(!this.maximize()); }, txtExec: function () { this.maximize(!this.maximize()); }, tooltip: 'Maximize', shortcut: 'Ctrl+Shift+M' }, // END_COMMAND // START_COMMAND: Source source: { state: function () { return this.sourceMode(); }, exec: function () { this.toggleSourceMode(); }, txtExec: function () { this.toggleSourceMode(); }, tooltip: 'View source', shortcut: 'Ctrl+Shift+S' }, // END_COMMAND // this is here so that commands above can be removed // without having to remove the , after the last one. // Needed for IE. ignore: {} }; return defaultCommnds; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); /***/ }, /* 10 */ /***/ function(module, exports, __webpack_require__) { var __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_RESULT__ = function (require) { 'use strict'; var $ = __webpack_require__(1); /** * Default options for SCEditor * @type {Object} */ return { /** @lends jQuery.sceditor.defaultOptions */ /** * Toolbar buttons order and groups. Should be comma separated and * have a bar | to separate groups * * @type {String} */ toolbar: 'bold,italic,underline,strike,subscript,superscript|' + 'left,center,right,justify|font,size,color,removeformat|' + 'cut,copy,paste,pastetext|bulletlist,orderedlist,indent,outdent|' + 'table|code,quote|horizontalrule,image,email,link,unlink|' + 'emoticon,youtube,date,time|ltr,rtl|print,maximize,source', /** * Comma separated list of commands to excludes from the toolbar * * @type {String} */ toolbarExclude: null, /** * Stylesheet to include in the WYSIWYG editor. This is what will style * the WYSIWYG elements * * @type {String} */ style: 'jquery.sceditor.default.css', /** * Comma separated list of fonts for the font selector * * @type {String} */ fonts: 'Arial,Arial Black,Comic Sans MS,Courier New,Georgia,Impact,' + 'Sans-serif,Serif,Times New Roman,Trebuchet MS,Verdana', /** * Colors should be comma separated and have a bar | to signal a new * column. * * If null the colors will be auto generated. * * @type {string} */ colors: null, /** * The locale to use. * @type {String} */ locale: $('html').attr('lang') || 'en', /** * The Charset to use * @type {String} */ charset: 'utf-8', /** * Compatibility mode for emoticons. * * Helps if you have emoticons such as :/ which would put an emoticon * inside http:// * * This mode requires emoticons to be surrounded by whitespace or end of * line chars. This mode has limited As You Type emoticon conversion * support. It will not replace AYT for end of line chars, only * emoticons surrounded by whitespace. They will still be replaced * correctly when loaded just not AYT. * * @type {Boolean} */ emoticonsCompat: false, /** * If to enable emoticons. Can be changes at runtime using the * emoticons() method. * * @type {Boolean} * @since 1.4.2 */ emoticonsEnabled: true, /** * Emoticon root URL * * @type {String} */ emoticonsRoot: '', emoticons: { dropdown: { ':)': 'emoticons/smile.png', ':angel:': 'emoticons/angel.png', ':angry:': 'emoticons/angry.png', '8-)': 'emoticons/cool.png', ':\'(': 'emoticons/cwy.png', ':ermm:': 'emoticons/ermm.png', ':D': 'emoticons/grin.png', '<3': 'emoticons/heart.png', ':(': 'emoticons/sad.png', ':O': 'emoticons/shocked.png', ':P': 'emoticons/tongue.png', ';)': 'emoticons/wink.png' }, more: { ':alien:': 'emoticons/alien.png', ':blink:': 'emoticons/blink.png', ':blush:': 'emoticons/blush.png', ':cheerful:': 'emoticons/cheerful.png', ':devil:': 'emoticons/devil.png', ':dizzy:': 'emoticons/dizzy.png', ':getlost:': 'emoticons/getlost.png', ':happy:': 'emoticons/happy.png', ':kissing:': 'emoticons/kissing.png', ':ninja:': 'emoticons/ninja.png', ':pinch:': 'emoticons/pinch.png', ':pouty:': 'emoticons/pouty.png', ':sick:': 'emoticons/sick.png', ':sideways:': 'emoticons/sideways.png', ':silly:': 'emoticons/silly.png', ':sleeping:': 'emoticons/sleeping.png', ':unsure:': 'emoticons/unsure.png', ':woot:': 'emoticons/w00t.png', ':wassat:': 'emoticons/wassat.png' }, hidden: { ':whistling:': 'emoticons/whistling.png', ':love:': 'emoticons/wub.png' } }, /** * Width of the editor. Set to null for automatic with * * @type {int} */ width: null, /** * Height of the editor including toolbar. Set to null for automatic * height * * @type {int} */ height: null, /** * If to allow the editor to be resized * * @type {Boolean} */ resizeEnabled: true, /** * Min resize to width, set to null for half textarea width or -1 for * unlimited * * @type {int} */ resizeMinWidth: null, /** * Min resize to height, set to null for half textarea height or -1 for * unlimited * * @type {int} */ resizeMinHeight: null, /** * Max resize to height, set to null for double textarea height or -1 * for unlimited * * @type {int} */ resizeMaxHeight: null, /** * Max resize to width, set to null for double textarea width or -1 for * unlimited * * @type {int} */ resizeMaxWidth: null, /** * If resizing by height is enabled * * @type {Boolean} */ resizeHeight: true, /** * If resizing by width is enabled * * @type {Boolean} */ resizeWidth: true, /** * Date format, will be overridden if locale specifies one. * * The words year, month and day will be replaced with the users current * year, month and day. * * @type {String} */ dateFormat: 'year-month-day', /** * Element to inset the toolbar into. * * @type {HTMLElement} */ toolbarContainer: null, /** * If to enable paste filtering. This is currently experimental, please * report any issues. * * @type {Boolean} */ enablePasteFiltering: false, /** * If to completely disable pasting into the editor * * @type {Boolean} */ disablePasting: false, /** * If the editor is read only. * * @type {Boolean} */ readOnly: false, /** * If to set the editor to right-to-left mode. * * If set to null the direction will be automatically detected. * * @type {Boolean} */ rtl: false, /** * If to auto focus the editor on page load * * @type {Boolean} */ autofocus: false, /** * If to auto focus the editor to the end of the content * * @type {Boolean} */ autofocusEnd: true, /** * If to auto expand the editor to fix the content * * @type {Boolean} */ autoExpand: false, /** * If to auto update original textbox on blur * * @type {Boolean} */ autoUpdate: false, /** * If to enable the browsers built in spell checker * * @type {Boolean} */ spellcheck: true, /** * If to run the source editor when there is no WYSIWYG support. Only * really applies to mobile OS's. * * @type {Boolean} */ runWithoutWysiwygSupport: false, /** * If to load the editor in source mode and still allow switching * between WYSIWYG and source mode * * @type {Boolean} */ startInSourceMode: false, /** * Optional ID to give the editor. * * @type {String} */ id: null, /** * Comma separated list of plugins * * @type {String} */ plugins: '', /** * z-index to set the editor container to. Needed for jQuery UI dialog. * * @type {Int} */ zIndex: null, /** * If to trim the BBCode. Removes any spaces at the start and end of the * BBCode string. * * @type {Boolean} */ bbcodeTrim: false, /** * If to disable removing block level elements by pressing backspace at * the start of them * * @type {Boolean} */ disableBlockRemove: false, /** * BBCode parser options, only applies if using the editor in BBCode * mode. * * See SCEditor.BBCodeParser.defaults for list of valid options * * @type {Object} */ parserOptions: { }, /** * CSS that will be added to the to dropdown menu (eg. z-index) * * @type {Object} */ dropDownCss: { } }; }.call(exports, __webpack_require__, exports, module), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); /***/ } /******/ ]);;/** * SCEditor XHTML Plugin * 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 * * @author Sam Clarke * @requires jQuery */ /*global prompt: true*/ (function ($) { 'use strict'; var SCEditor = $.sceditor; var sceditorPlugins = SCEditor.plugins; var dom = SCEditor.dom; var defaultCommandsOverrides = { bold: { txtExec: [ '', '' ] }, italic: { txtExec: [ '', '' ] }, underline: { txtExec: [ '', '' ] }, strike: { txtExec: [ '', '' ] }, subscript: { txtExec: [ '', '' ] }, superscript: { txtExec: [ '', '' ] }, left: { txtExec: [ '
    ', '
    ' ] }, center: { txtExec: [ '
    ', '
    ' ] }, right: { txtExec: [ '
    ', '
    ' ] }, justify: { txtExec: [ '
    ', '
    ' ] }, font: { txtExec: function (caller) { var editor = this; SCEditor.command.get('font')._dropDown( editor, caller, function (fontName) { editor.insertText('', ''); } ); } }, size: { txtExec: function (caller) { var editor = this; SCEditor.command.get('size')._dropDown( editor, caller, function (fontSize) { editor.insertText('', ''); } ); } }, color: { txtExec: function (caller) { var editor = this; SCEditor.command.get('color')._dropDown( editor, caller, function (color) { editor.insertText('', ''); } ); } }, bulletlist: { txtExec: [ '
    • ', '
    ' ] }, orderedlist: { txtExec: [ '
    1. ', '
    ' ] }, table: { txtExec: [ '
    ', '
    ' ] }, horizontalrule: { txtExec: [ '
    ' ] }, code: { txtExec: [ '', '' ] }, image: { txtExec: function (caller, selected) { var url = prompt(this._('Enter the image URL:'), selected); if (url) { this.insertText(''); } } }, email: { txtExec: function (caller, sel) { var email, text, display = sel && sel.indexOf('@') > -1 ? null : sel; email = prompt( this._('Enter the e-mail address:'), (display ? '' : sel) ); text = prompt( this._('Enter the displayed text:'), display || email ) || email; if (email) { this.insertText( '' + text + '' ); } } }, link: { txtExec: function (caller, sel) { var display = sel && sel.indexOf('http://') > -1 ? null : sel, url = prompt(this._('Enter URL:'), (display ? 'http://' : sel)), text = prompt(this._('Enter the displayed text:'), display || url) || url; if (url) { this.insertText( '' + text + '' ); } } }, quote: { txtExec: [ '
    ', '
    ' ] }, youtube: { txtExec: function (caller) { var editor = this; SCEditor.command.get('youtube')._dropDown( editor, caller, function (id) { editor.insertText( '' ); } ); } }, rtl: { txtExec: [ '
    ', '
    ' ] }, ltr: { txtExec: [ '
    ', '
    ' ] } }; /** * XHTMLSerializer part of the XHTML plugin. * * @class XHTMLSerializer * @name jQuery.sceditor.XHTMLSerializer * @since v1.4.1 */ SCEditor.XHTMLSerializer = function () { var base = this; var opts = { indentStr: '\t' }; /** * Array containing the output, used as it's faster * than string concatenation in slow browsers. * @type {Array} * @private */ var outputStringBuilder = []; /** * Current indention level * @type {Number} * @private */ var currentIndent = 0; /** * @private */ var escapeEntites, trim, serializeNode, handleDoc, handleElement, handleCdata, handleComment, handleText, output, canIndent; // TODO: use escape.entities /** * Escapes XHTML entities * * @param {String} str * @return {String} * @private */ escapeEntites = function (str) { var entites = { '&': '&', '<': '<', '>': '>', '"': '"' }; return !str ? '' : str.replace(/[&<>"]/g, function (entity) { return entites[entity] || entity; }); }; /** * @param {string} str * @return {string} * @private */ trim = function (str) { return str // New lines will be shown as spaces so just convert to spaces. .replace(/[\r\n]/, ' ') .replace(/[^\S|\u00A0]+/g, ' '); }; /** * Serializes a node to XHTML * * @param {Node} node Node to serialize * @param {Boolean} onlyChildren If to only serialize the nodes * children and not the node * itself * @return {String} The serialized node * @name serialize * @memberOf jQuery.sceditor.XHTMLSerializer.prototype * @since v1.4.1 */ base.serialize = function (node, onlyChildren) { outputStringBuilder = []; if (onlyChildren) { node = node.firstChild; while (node) { serializeNode(node); node = node.nextSibling; } } else { serializeNode(node); } return outputStringBuilder.join(''); }; /** * Serializes a node to the outputStringBuilder * * @param {Node} node * @return {Void} * @private */ serializeNode = function (node, parentIsPre) { switch (node.nodeType) { case 1: // element var tagName = node.nodeName.toLowerCase(); // IE comment if (tagName === '!') { handleComment(node); } else { handleElement(node, parentIsPre); } break; case 3: // text handleText(node, parentIsPre); break; case 4: // cdata section handleCdata(node); break; case 8: // comment handleComment(node); break; case 9: // document case 11: // document fragment handleDoc(node); break; // Ignored types case 2: // attribute case 5: // entity ref case 6: // entity case 7: // processing instruction case 10: // document type case 12: // notation break; } }; /** * Handles doc node * @param {Node} node * @return {void} * @private */ handleDoc = function (node) { var child = node.firstChild; while (child) { serializeNode(child); child = child.nextSibling; } }; /** * Handles element nodes * @param {Node} node * @return {void} * @private */ handleElement = function (node, parentIsPre) { var child, attr, attrValue, tagName = node.nodeName.toLowerCase(), isIframe = tagName === 'iframe', attrIdx = node.attributes.length, firstChild = node.firstChild, // pre || pre-wrap with any vendor prefix isPre = parentIsPre || /pre(?:\-wrap)?$/i.test($(node).css('whiteSpace')), selfClosing = !node.firstChild && !dom.canHaveChildren(node) && !isIframe; if ($(node).hasClass('sceditor-ignore')) { return; } output('<' + tagName, !parentIsPre && canIndent(node)); while (attrIdx--) { attr = node.attributes[attrIdx]; // IE < 8 returns all possible attributes not just specified // ones. IE < 8 also doesn't say value on input is specified // so just assume it is. if (!SCEditor.ie || attr.specified || (tagName === 'input' && attr.name === 'value')) { // IE < 8 doesn't return the CSS for the style attribute if (SCEditor.ie < 8 && /style/i.test(attr.name)) { attrValue = node.style.cssText; } else { attrValue = attr.value; } output(' ' + attr.name.toLowerCase() + '="' + escapeEntites(attrValue) + '"', false); } } output(selfClosing ? ' />' : '>', false); if (!isIframe) { child = firstChild; } while (child) { currentIndent++; serializeNode(child, isPre); child = child.nextSibling; currentIndent--; } if (!selfClosing) { output( '', !isPre && !isIframe && canIndent(node) && firstChild && canIndent(firstChild) ); } }; /** * Handles CDATA nodes * @param {Node} node * @return {void} * @private */ handleCdata = function (node) { output(''); }; /** * Handles comment nodes * @param {Node} node * @return {void} * @private */ handleComment = function (node) { output(''); }; /** * Handles text nodes * @param {Node} node * @return {void} * @private */ handleText = function (node, parentIsPre) { var text = node.nodeValue; if (!parentIsPre) { text = trim(text); } if (text) { output(escapeEntites(text), !parentIsPre && canIndent(node)); } }; /** * Adds a string to the outputStringBuilder. * * The string will be indented unless indent is set to boolean false. * @param {String} str * @param {Boolean} indent * @return {void} * @private */ output = function (str, indent) { var i = currentIndent; if (indent !== false) { // Don't add a new line if it's the first element if (outputStringBuilder.length) { outputStringBuilder.push('\n'); } while (i--) { outputStringBuilder.push(opts.indentStr); } } outputStringBuilder.push(str); }; /** * Checks if should indent the node or not * @param {Node} node * @return {boolean} * @private */ canIndent = function (node) { var prev = node.previousSibling; if (node.nodeType !== 1 && prev) { return !dom.isInline(prev); } // first child of a block element if (!prev && !dom.isInline(node.parentNode)) { return true; } return !dom.isInline(node); }; }; /** * SCEditor XHTML plugin * @class xhtml * @name jQuery.sceditor.plugins.xhtml * @since v1.4.1 */ sceditorPlugins.xhtml = function () { var base = this; /** * Tag converstions cache * @type {Object} * @private */ var tagConvertersCache = {}; /** * Attributes filter cache * @type {Object} * @private */ var attrsCache = {}; /** * Private methods * @private */ var convertTags, convertNode, isEmpty, removeTags, mergeAttribsFilters, removeAttribs, wrapInlines; /** * Init * @return {void} */ base.init = function () { if (!$.isEmptyObject(sceditorPlugins.xhtml.converters || {})) { $.each( sceditorPlugins.xhtml.converters, function (idx, converter) { $.each(converter.tags, function (tagname) { if (!tagConvertersCache[tagname]) { tagConvertersCache[tagname] = []; } tagConvertersCache[tagname].push(converter); }); } ); } this.commands = $.extend(true, {}, defaultCommandsOverrides, this.commands); }; /** * Converts the WYSIWYG content to XHTML * @param {String} html * @param {Node} domBody * @return {String} * @memberOf jQuery.sceditor.plugins.xhtml.prototype */ base.signalToSource = function (html, domBody) { domBody = domBody.jquery ? domBody[0] : domBody; convertTags(domBody); removeTags(domBody); removeAttribs(domBody); wrapInlines(domBody); return (new SCEditor.XHTMLSerializer()).serialize(domBody, true); }; /** * Converts the XHTML to WYSIWYG content. * * This doesn't currently do anything as XHTML * is valid WYSIWYG content. * @param {String} text * @return {String} * @memberOf jQuery.sceditor.plugins.xhtml.prototype */ base.signalToWysiwyg = function (text) { return text; }; /** * Deprecated, use dom.convertElement() instead. * @deprecated */ base.convertTagTo = dom.convertElement; /** * Runs all converters for the specified tagName * against the DOM node. * @param {String} tagName * @param {jQuery} $node * @return {Node} node * @private */ convertNode = function (tagName, $node, node) { if (!tagConvertersCache[tagName]) { return; } $.each(tagConvertersCache[tagName], function (idx, converter) { if (converter.tags[tagName]) { $.each(converter.tags[tagName], function (attr, values) { if (!node.getAttributeNode) { return; } attr = node.getAttributeNode(attr); // IE < 8 always returns an attribute regardless of if // it has been specified so must check it. if (!attr || (SCEditor.ie < 8 && !attr.specified)) { return; } if (values && $.inArray(attr.value, values) < 0) { return; } converter.conv.call(base, node, $node); }); } else if (converter.conv) { converter.conv.call(base, node, $node); } }); }; /** * Converts any tags/attributes to their XHTML equivalents * @param {Node} node * @return {Void} * @private */ convertTags = function (node) { dom.traverse(node, function (node) { var $node = $(node), tagName = node.nodeName.toLowerCase(); convertNode('*', $node, node); convertNode(tagName, $node, node); }, true); }; /** * Tests if a node is empty and can be removed. * * @param {Node} node * @return {Boolean} * @private */ isEmpty = function (node, excludeBr) { var childNodes = node.childNodes, tagName = node.nodeName.toLowerCase(), nodeValue = node.nodeValue, childrenLength = childNodes.length; if (excludeBr && tagName === 'br') { return true; } if ($(node).hasClass('sceditor-ignore')) { return true; } if (!dom.canHaveChildren(node)) { return false; } // \S|\u00A0 = any non space char if (nodeValue && /\S|\u00A0/.test(nodeValue)) { return false; } while (childrenLength--) { if (!isEmpty(childNodes[childrenLength], excludeBr && !node.previousSibling && !node.nextSibling)) { return false; } } return true; }; /** * Removes any tags that are not white listed or if no * tags are white listed it will remove any tags that * are black listed. * * @param {Node} rootNode * @return {Void} * @private */ removeTags = function (rootNode) { dom.traverse(rootNode, function (node) { var remove, tagName = node.nodeName.toLowerCase(), parentNode = node.parentNode, nodeType = node.nodeType, isBlock = !dom.isInline(node), previousSibling = node.previousSibling, nextSibling = node.nextSibling, isTopLevel = parentNode === rootNode, noSiblings = !previousSibling && !nextSibling, empty = tagName !== 'iframe' && isEmpty(node, isTopLevel && noSiblings && tagName !== 'br'), document = node.ownerDocument, allowedtags = sceditorPlugins.xhtml.allowedTags, disallowedTags = sceditorPlugins.xhtml.disallowedTags; // 3 = text node if (nodeType === 3) { return; } if (nodeType === 4) { tagName = '!cdata'; } else if (tagName === '!' || nodeType === 8) { tagName = '!comment'; } if (empty) { remove = true; // 3 is text node which do not get filtered } else if (allowedtags && allowedtags.length) { remove = ($.inArray(tagName, allowedtags) < 0); } else if (disallowedTags && disallowedTags.length) { remove = ($.inArray(tagName, disallowedTags) > -1); } if (remove) { if (!empty) { if (isBlock && previousSibling && dom.isInline(previousSibling)) { parentNode.insertBefore( document.createTextNode(' '), node); } // Insert all the childen after node while (node.firstChild) { parentNode.insertBefore(node.firstChild, nextSibling); } if (isBlock && nextSibling && dom.isInline(nextSibling)) { parentNode.insertBefore( document.createTextNode(' '), nextSibling); } } parentNode.removeChild(node); } }, true); }; /** * Merges two sets of attribute filters into one * * @param {Object} filtersA * @param {Object} filtersB * @return {Object} * @private */ mergeAttribsFilters = function (filtersA, filtersB) { var ret = {}; if (filtersA) { $.extend(ret, filtersA); } if (!filtersB) { return ret; } $.each(filtersB, function (attrName, values) { if ($.isArray(values)) { ret[attrName] = $.merge(ret[attrName] || [], values); } else if (!ret[attrName]) { ret[attrName] = null; } }); return ret; }; /** * Wraps adjacent inline child nodes of root * in paragraphs. * * @param {Node} root * @private */ wrapInlines = function (root) { var adjacentInlines = []; var wrapAdjacents = function () { if (adjacentInlines.length) { $('

    ', root.ownerDocument) .insertBefore(adjacentInlines[0]) .append(adjacentInlines); adjacentInlines = []; } }; // Strip empty text nodes so they don't get wrapped. dom.removeWhiteSpace(root); var node = root.firstChild; while (node) { if (dom.isInline(node) && !$(node).is('.sceditor-ignore')) { adjacentInlines.push(node); } else { wrapAdjacents(); } node = node.nextSibling; } wrapAdjacents(); }; /** * Removes any attributes that are not white listed or * if no attributes are white listed it will remove * any attributes that are black listed. * @param {Node} node * @return {Void} * @private */ removeAttribs = function (node) { var tagName, attr, attrName, attrsLength, validValues, remove, allowedAttribs = sceditorPlugins.xhtml.allowedAttribs, isAllowed = allowedAttribs && !$.isEmptyObject(allowedAttribs), disallowedAttribs = sceditorPlugins.xhtml.disallowedAttribs, isDisallowed = disallowedAttribs && !$.isEmptyObject(disallowedAttribs); attrsCache = {}; dom.traverse(node, function (node) { if (!node.attributes) { return; } tagName = node.nodeName.toLowerCase(); attrsLength = node.attributes.length; if (attrsLength) { if (!attrsCache[tagName]) { if (isAllowed) { attrsCache[tagName] = mergeAttribsFilters( allowedAttribs['*'], allowedAttribs[tagName] ); } else { attrsCache[tagName] = mergeAttribsFilters( disallowedAttribs['*'], disallowedAttribs[tagName] ); } } while (attrsLength--) { attr = node.attributes[attrsLength]; attrName = attr.name; validValues = attrsCache[tagName][attrName]; remove = false; if (isAllowed) { remove = validValues !== null && (!$.isArray(validValues) || $.inArray(attr.value, validValues) < 0); } else if (isDisallowed) { remove = validValues === null || ($.isArray(validValues) && $.inArray(attr.value, validValues) > -1); } if (remove) { node.removeAttribute(attrName); } } } }); }; }; /** * Tag conveters, a converter is applied to all * tags that match the criteria. * @type {Array} * @name jQuery.sceditor.plugins.xhtml.converters * @since v1.4.1 */ sceditorPlugins.xhtml.converters = [ { tags: { '*': { width: null } }, conv: function (node, $node) { $node.css('width', $node.attr('width')).removeAttr('width'); } }, { tags: { '*': { height: null } }, conv: function (node, $node) { $node.css('height', $node.attr('height')).removeAttr('height'); } }, { tags: { 'li': { value: null } }, conv: function (node, $node) { if (SCEditor.ie < 8) { node.removeAttribute('value'); } else { $node.removeAttr('value'); } } }, { tags: { '*': { text: null } }, conv: function (node, $node) { $node.css('color', $node.attr('text')).removeAttr('text'); } }, { tags: { '*': { color: null } }, conv: function (node, $node) { $node.css('color', $node.attr('color')).removeAttr('color'); } }, { tags: { '*': { face: null } }, conv: function (node, $node) { $node.css('fontFamily', $node.attr('face')).removeAttr('face'); } }, { tags: { '*': { align: null } }, conv: function (node, $node) { $node.css('textAlign', $node.attr('align')).removeAttr('align'); } }, { tags: { '*': { border: null } }, conv: function (node, $node) { $node .css('borderWidth',$node.attr('border')) .removeAttr('border'); } }, { tags: { applet: { name: null }, img: { name: null }, layer: { name: null }, map: { name: null }, object: { name: null }, param: { name: null } }, conv: function (node, $node) { if (!$node.attr('id')) { $node.attr('id', $node.attr('name')); } $node.removeAttr('name'); } }, { tags: { '*': { vspace: null } }, conv: function (node, $node) { $node .css('marginTop', $node.attr('vspace') - 0) .css('marginBottom', $node.attr('vspace') - 0) .removeAttr('vspace'); } }, { tags: { '*': { hspace: null } }, conv: function (node, $node) { $node .css('marginLeft', $node.attr('hspace') - 0) .css('marginRight', $node.attr('hspace') - 0) .removeAttr('hspace'); } }, { tags: { 'hr': { noshade: null } }, conv: function (node, $node) { $node.css('borderStyle', 'solid').removeAttr('noshade'); } }, { tags: { '*': { nowrap: null } }, conv: function (node, $node) { $node.css('white-space', 'nowrap').removeAttr('nowrap'); } }, { tags: { big: null }, conv: function (node) { $(this.convertTagTo(node, 'span')).css('fontSize', 'larger'); } }, { tags: { small: null }, conv: function (node) { $(this.convertTagTo(node, 'span')).css('fontSize', 'smaller'); } }, { tags: { b: null }, conv: function (node) { $(this.convertTagTo(node, 'strong')); } }, { tags: { u: null }, conv: function (node) { $(this.convertTagTo(node, 'span')) .css('textDecoration', 'underline'); } }, { tags: { i: null }, conv: function (node) { $(this.convertTagTo(node, 'em')); } }, { tags: { s: null, strike: null }, conv: function (node) { $(this.convertTagTo(node, 'span')) .css('textDecoration', 'line-through'); } }, { tags: { dir: null }, conv: function (node) { this.convertTagTo(node, 'ul'); } }, { tags: { center: null }, conv: function (node) { $(this.convertTagTo(node, 'div')) .css('textAlign', 'center'); } }, { tags: { font: { size: null } }, conv: function (node, $node) { var size = $node.css('fontSize'), fontSize = size; // IE < 8 sets a font tag with no size to +0 so // should just skip it. if (fontSize !== '+0') { // IE 8 and below incorrectly returns the value of the size // attribute instead of the px value so must convert it if (SCEditor.ie < 9) { fontSize = 10; if (size > 1) { fontSize = 13; } if (size > 2) { fontSize = 16; } if (size > 3) { fontSize = 18; } if (size > 4) { fontSize = 24; } if (size > 5) { fontSize = 32; } if (size > 6) { fontSize = 48; } } $node.css('fontSize', fontSize); } $node.removeAttr('size'); } }, { tags: { font: null }, conv: function (node) { // All it's attributes will be converted // by the attribute converters this.convertTagTo(node, 'span'); } }, { tags: { '*': { type: ['_moz'] } }, conv: function (node, $node) { $node.removeAttr('type'); } }, { tags: { '*': { '_moz_dirty': null } }, conv: function (node, $node) { $node.removeAttr('_moz_dirty'); } }, { tags: { '*': { '_moz_editor_bogus_node': null } }, conv: function (node, $node) { $node.remove(); } } ]; /** * Allowed attributes map. * * To allow an attribute for all tags use * as the tag name. * * Leave empty or null to allow all attributes. (the disallow * list will be used to filter them instead) * @type {Object} * @name jQuery.sceditor.plugins.xhtml.allowedAttribs * @since v1.4.1 */ sceditorPlugins.xhtml.allowedAttribs = {}; /** * Attributes that are not allowed. * * Only used if allowed attributes is null or empty. * @type {Object} * @name jQuery.sceditor.plugins.xhtml.disallowedAttribs * @since v1.4.1 */ sceditorPlugins.xhtml.disallowedAttribs = {}; /** * Array containing all the allowed tags. * * If null or empty all tags will be allowed. * @type {Array} * @name jQuery.sceditor.plugins.xhtml.allowedTags * @since v1.4.1 */ sceditorPlugins.xhtml.allowedTags = []; /** * Array containing all the disallowed tags. * * Only used if allowed tags is null or empty. * @type {Array} * @name jQuery.sceditor.plugins.xhtml.disallowedTags * @since v1.4.1 */ sceditorPlugins.xhtml.disallowedTags = []; }(jQuery));