{"version":3,"sources":["webpack:///codex-editor.js","webpack:///webpack/bootstrap 804711f99558afed9516","webpack:///./codex.js","webpack:///./modules/core.js","webpack:///./modules/tools.js","webpack:///./modules/ui.js","webpack:///./modules/transport.js","webpack:///./modules/renderer.js","webpack:///./modules/saver.js","webpack:///./modules/content.js","webpack:///./modules/toolbar/toolbar.js","webpack:///./modules/toolbar/settings.js","webpack:///./modules/toolbar/inline.js","webpack:///./modules/toolbar/toolbox.js","webpack:///./modules/callbacks.js","webpack:///./modules/draw.js","webpack:///./modules/caret.js","webpack:///./modules/notifications.js","webpack:///./modules/parser.js","webpack:///./modules/sanitizer.js","webpack:///./~/html-janitor/src/html-janitor.js","webpack:///./modules/listeners.js","webpack:///./modules/destroyer.js","webpack:///./modules/paste.js"],"names":["codex","modules","__webpack_require__","moduleId","installedModules","exports","module","id","loaded","call","m","c","p","editor","version","scriptPrefix","init","core","tools","ui","transport","renderer","saver","content","toolbar","callback","draw","caret","notifications","parser","sanitizer","listeners","destroyer","paste","settings","holderId","initialBlockPlugin","nodes","holder","wrapper","inlineToolbar","buttons","actions","toolbox","plusButton","showSettingsButton","showTrashButton","blockSettings","pluginSettings","defaultSettings","toolbarButtons","redactor","state","jsonOutput","blocks","inputs","start","userSettings","prepare","then","makeBlocksFromData","saveInputs","catch","error","log","_typeof","Symbol","iterator","obj","constructor","prototype","Promise","resolve","reject","data","hideToolbar","placeholder","document","getElementById","undefined","Error","msg","type","arg","window","console","e","insertAfter","target","element","parentNode","insertBefore","nextSibling","nodeTypes","TAG","TEXT","COMMENT","DOCUMENT_FRAGMENT","keys","BACKSPACE","TAB","ENTER","SHIFT","CTRL","ALT","ESC","SPACE","LEFT","UP","DOWN","RIGHT","DELETE","META","isDomNode","el","nodeType","this","isEmpty","Object","length","ajax","url","encodedString","isFormData","prop","XMLHTTP","XMLHttpRequest","ActiveXObject","async","test","encodeURIComponent","withCredentials","responseContext","beforeSendResult","beforeSend","open","isFormData_","setRequestHeader","progress","upload","onprogress","bind","onreadystatechange","readyState","status","success","responseText","send","importScript","scriptPath","instanceName","script","createElement","defer","onload","onerror","src","head","appendChild","object","FormData","isNativeInput","nativeInputAreas","indexOf","tagName","isBlockEmpty","block","EXCEPTION_TAGS","nativeInputs","querySelectorAll","nativeInputsAreEmpty","textContentIsEmpty","textContent","trim","Array","forEach","input","value","includes","resolve_","reject_","pluginsRequiresPreparation","allPlugins","pluginName","plugin","push","waitAllPluginsPreparation_","plugins","allPluginsProcessed__","reduce","previousValue","iteration","pluginIsReady__","callPluginsPrepareMethod_","available","loadingMessage","config","className","BLOCK_CLASSNAME","BLOCK_CONTENT","BLOCK_STRETCHED","BLOCK_HIGHLIGHTED","SETTINGS_ITEM","makeToolBar_","addTools_","makeInlineToolbar_","addInlineToolbarTools_","makeNotificationHolder_","bindEvents_","container","inlineToolbarButtons","inlineToolbarActions","blockButtons","makeToolbarSettings_","toolbarContent","makeToolbarContent_","settingsButton","makeRemoveBlockButton","pluginsSettings","createHolder","tool","toolName","toolButton","iconClassname","displayInToolbox","render","toolbarButton","bold","icon","command","italic","link","name","toolbarButtonInline","setInlineToolbarButtonBehaviour","add","globalKeydown","redactorKeyDown","globalKeyup","redactorClicked","plusButtonClicked","showSettingsButtonClicked","button","toolbarButtonClicked","addBlockHandlers","blockKeydown","blockPasteCallback","inline","show","map","current","addInitialBlock","initialBlock","initialBlockType","setAttribute","insertBlock","workingNodeChanged","event","toolClicked","currentRequest","arguments","node","fileSelected","clearInput","i","files","formData","multiple","append","selectAndUpload","args","accept","click","abort","items","appendBlocks","nodeSequence","index","appendNodeAtIndex","getNodeAsync","createBlockFromData","blockData","blocksList","position","toolData","unavailableBlock","innerHTML","dataset","inputPosition","stretched","isStretched","save","html","saveBlocks","childNodes","getBlockData","all","makeOutput","saveBlockData","validateBlockData","blockContent","pluginsContent","_ref","validate","result","savedData","filter","time","Date","currentNode","editorAreaHightlighted","sync","markBlock","classList","clearMark","remove","getFirstLevelBlock","body","contains","targetNode","replaceBlock","targetBlock","newBlock","replaceChild","needPlaceCaret","workingBlock","newBlockContent","blockType","composeNewBlock_","currentInputIndex","getCurrentInputIndex","editableElement","querySelector","emptyText","createTextNode","set","move","showPlusButton","setTimeout","setToNextBlock","switchBlock","blockToReplace","newBlockComposed","getDeepestTextNodeFromPosition","text","blockChilds","removeChild","lookingFromStart","getRange","selection","getSelection","getRangeAt","splitBlock","inputIndex","textBeforeCaret","textNodeBeforeCaret","textAfterCaret","textNodeAfterCaret","anchorNode","anchorNodeText","caretOffset","anchorOffset","currentBlock","substring","previousChilds","nextChilds","reachedCurrent","child","previousChildsLength","nextChildsLength","newNode","NEW_BLOCK_TYPE","mergeBlocks","targetInputIndex","targetInput","currentInputContent","isLastNode","allChecked","allSiblingsEmpty_","sibling","wrapTextWithParagraphs","htmlData","plainData","wrapPlainTextWithParagraphs","paragraph","blockTyped","newWrapper","firstLevelBlocks","cloneNode","plainText","split","join","getEditableParent","contentEditable","clear","load","articleData","currentContent","assign","concat","defaultToolbarHeight","defaultOffset","opened","toolType","makeSettings","close","toggle","hidePlusButton","newYCoordinate","offsetTop","style","transform","Math","floor","hideRemoveActions","setting","settingsBlock","removeBlockWrapper","settingButton","actionWrapper","confirmAction","cancelAction","removeButtonClicked","confirmRemovingRequest","cancelRemovingRequest","action","showRemoveActions","firstLevelBlocksCount","buttonsOpened","actionsOpened","wrappersOffset","storedSelection","showInlineToolbar","selectedText","getSelectionText","showButtons","getWrappersOffset","newCoordinateX","newCoordinateY","coords","getSelectionCoords","offsetHeight","x","left","y","scrollY","top","closeButtons","closeAction","createLinkAction","defaultToolAction","hightlight","offset","getOffset","_x","_y","isNaN","offsetLeft","clientLeft","clientTop","offsetParent","range","sel","createRange","collapse","boundingLeft","boundingTop","rangeCount","cloneRange","getClientRects","rect","toString","showActions","inlineToolbarAnchorInputKeydown_","keyCode","editable","restoreSelection","setAnchor","preventDefault","stopImmediatePropagation","clearRange","isActive","isLinkActive","saveSelection","inputForLink","focus","dataType","execCommand","containerEl","preSelectionRange","selectNodeContents","setEnd","startContainer","startOffset","end","savedSel","charIndex","setStart","nextCharIndex","nodeStack","foundStart","stop","pop","removeAllRanges","addRange","queryCommandState","setButtonHighlighted","removeButtonsHighLight","tag","openedOnBlock","leaf","currentTool","barButtons","nextToolIndex","toolToSelect","visibleTool","appendCallback","UNREPLACEBLE_TOOLS","workingNode","setToBlock","callbacks","enterKeyPressed_","tabKeyPressedOnRedactorsZone_","enterKeyPressedOnRedactorsZone_","escapeKeyPressedOnRedactorsZone_","defaultKeyPressedOnRedactorsZone_","arrowKeyPressed_","enterPressedOnBlock_","saveCurrentInputIndex","isEnterPressedOnToolbar","enableLineBreaks","stopPropagation","shiftKey","currentSelection","currentSelectedNode","caretAtTheEndOfText","atTheEnd","isTextNodeHasParentBetweenContenteditable","enterPressedOnBlock","islastNode","detectWhenClickedOnFirstLevelBlockArea_","firstLevelBlock","indexOfLastInput","inputIsEmpty","currentNodeType","isInitialType","flag","blockRightOrDownArrowPressed_","backspacePressed_","blockLeftOrUpArrowPressed_","focusedNodeHolder","focusedNode","editableElementIndex","lastChild","deepestTextnode","caretInLastChild","firstChild","caretInFirstChild","caretAtTheBeginning","setToPreviousBlock","selectionLength","endOffset","atStart","currentToolType","ceBlock","bar","div","toggler","classname","toolIcon","toolTitle","properties","focusedNodeIndex","nodeToSet","childs","nextInput","emptyTextElement","lastChildNode","lengthOfLastChildNode","previousInput","pluginsRender","isFirstNode","isOffsetZero","insertNode","lastNode","deleteContents","setStartAfter","queue","addToQueue","splice","errorThrown","errorMsg","notification","message","constructorSettings","create","okBtn","cancelBtn","okMsg","cancelMsg","confirmHandler","cancelHandler","confirm","cancel","inputField","insertPastedContent","isFirstLevelBlock","janitor","Config","CUSTOM","BASIC","tags","a","href","rel","init_","userCustomConfig","configuration","clean","dirtyString","customConfig","janitorInstance","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_RESULT__","root","factory","HTMLJanitor","tagDefinitions","validConfigValues","k","every","isBlockElement","blockElementNames","nodeName","isInlineElement","inlineElementNames","createTreeWalker","NodeFilter","SHOW_TEXT","SHOW_ELEMENT","SHOW_COMMENT","getAllowedAttrs","shouldRejectNode","allowedAttrs","shouldRejectAttr","attr","attrName","toLowerCase","sandbox","_sanitize","treeWalker","_sanitized","Node","TEXT_NODE","COMMENT_NODE","containsBlockElement","isInline","some","isNotTopContainer","isNestedBlockElement","isInvalid","keepNestedBlockElements","attributes","removeAttribute","previousElementSibling","nextElementSibling","allListeners","search","byElement","context","listenersOnElement","listener","byType","eventType","listenersWithType","byHandler","handler","listenersWithHandler","one","isCapture","addEventListener","alreadyAddedListener","removeEventListener","existingListeners","removeAll","get","removeNodes","destroyPlugins","destroy","destroyScripts","scripts","getElementsByTagName","patterns","renderOnPastePatterns","isArray","pattern","pasted","clipBoardData","clipboardData","getData","analize","string","execArray","regex","exec","match","pasteToNewBlock_","needsToHandlePasteEvent","cleanData","wrappedData","paragraphs","emulateUserAgentBehaviour","insertPastedParagraphs","editableParent","childElementCount","createDocumentFragment"],"mappings":"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","file":"codex-editor.js","sourcesContent":["var codex = codex || {}; codex[\"editor\"] =\n/******/ (function(modules) { // webpackBootstrap\n/******/ \t// The module cache\n/******/ \tvar installedModules = {};\n/******/\n/******/ \t// The require function\n/******/ \tfunction __webpack_require__(moduleId) {\n/******/\n/******/ \t\t// Check if module is in cache\n/******/ \t\tif(installedModules[moduleId])\n/******/ \t\t\treturn installedModules[moduleId].exports;\n/******/\n/******/ \t\t// Create a new module (and put it into the cache)\n/******/ \t\tvar module = installedModules[moduleId] = {\n/******/ \t\t\texports: {},\n/******/ \t\t\tid: moduleId,\n/******/ \t\t\tloaded: false\n/******/ \t\t};\n/******/\n/******/ \t\t// Execute the module function\n/******/ \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n/******/\n/******/ \t\t// Flag the module as loaded\n/******/ \t\tmodule.loaded = true;\n/******/\n/******/ \t\t// Return the exports of the module\n/******/ \t\treturn module.exports;\n/******/ \t}\n/******/\n/******/\n/******/ \t// expose the modules object (__webpack_modules__)\n/******/ \t__webpack_require__.m = modules;\n/******/\n/******/ \t// expose the module cache\n/******/ \t__webpack_require__.c = installedModules;\n/******/\n/******/ \t// __webpack_public_path__\n/******/ \t__webpack_require__.p = \"\";\n/******/\n/******/ \t// Load entry module and return exports\n/******/ \treturn __webpack_require__(0);\n/******/ })\n/************************************************************************/\n/******/ ([\n/* 0 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\t/**\n\t *\n\t * Codex Editor\n\t *\n\t * @author Codex Team\n\t */\n\t\n\tmodule.exports = function (editor) {\n\t\n\t 'use strict';\n\t\n\t editor.version = (\"1.7.9\");\n\t editor.scriptPrefix = 'cdx-script-';\n\t\n\t var init = function init() {\n\t\n\t editor.core = __webpack_require__(1);\n\t editor.tools = __webpack_require__(2);\n\t editor.ui = __webpack_require__(3);\n\t editor.transport = __webpack_require__(4);\n\t editor.renderer = __webpack_require__(5);\n\t editor.saver = __webpack_require__(6);\n\t editor.content = __webpack_require__(7);\n\t editor.toolbar = __webpack_require__(8);\n\t editor.callback = __webpack_require__(12);\n\t editor.draw = __webpack_require__(13);\n\t editor.caret = __webpack_require__(14);\n\t editor.notifications = __webpack_require__(15);\n\t editor.parser = __webpack_require__(16);\n\t editor.sanitizer = __webpack_require__(17);\n\t editor.listeners = __webpack_require__(19);\n\t editor.destroyer = __webpack_require__(20);\n\t editor.paste = __webpack_require__(21);\n\t };\n\t\n\t /**\n\t * @public\n\t * holds initial settings\n\t */\n\t editor.settings = {\n\t tools: ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'],\n\t holderId: 'codex-editor',\n\t\n\t // Type of block showing on empty editor\n\t initialBlockPlugin: 'paragraph'\n\t };\n\t\n\t /**\n\t * public\n\t *\n\t * Static nodes\n\t */\n\t editor.nodes = {\n\t holder: null,\n\t wrapper: null,\n\t toolbar: null,\n\t inlineToolbar: {\n\t wrapper: null,\n\t buttons: null,\n\t actions: null\n\t },\n\t toolbox: null,\n\t notifications: null,\n\t plusButton: null,\n\t showSettingsButton: null,\n\t showTrashButton: null,\n\t blockSettings: null,\n\t pluginSettings: null,\n\t defaultSettings: null,\n\t toolbarButtons: {}, // { type : DomEl, ... }\n\t redactor: null\n\t };\n\t\n\t /**\n\t * @public\n\t *\n\t * Output state\n\t */\n\t editor.state = {\n\t jsonOutput: [],\n\t blocks: [],\n\t inputs: []\n\t };\n\t\n\t /**\n\t * @public\n\t * Editor plugins\n\t */\n\t editor.tools = {};\n\t\n\t /**\n\t * Initialization\n\t * @uses Promise cEditor.core.prepare\n\t * @param {Object} userSettings\n\t * @param {Array} userSettings.tools list of plugins\n\t * @param {String} userSettings.holderId Element's id to append editor\n\t *\n\t * Load user defined tools\n\t * Tools must contain this important objects :\n\t * @param {String} type - this is a type of plugin. It can be used as plugin name\n\t * @param {String} iconClassname - this a icon in toolbar\n\t * @param {Object} make - what should plugin do, when it is clicked\n\t * @param {Object} appendCallback - callback after clicking\n\t * @param {Element} settings - what settings does it have\n\t * @param {Object} render - plugin get JSON, and should return HTML\n\t * @param {Object} save - plugin gets HTML content, returns JSON\n\t * @param {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE\n\t * @param {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE\n\t *\n\t * @example\n\t * - type : 'header',\n\t * - iconClassname : 'ce-icon-header',\n\t * - make : headerTool.make,\n\t * - appendCallback : headerTool.appendCallback,\n\t * - settings : headerTool.makeSettings(),\n\t * - render : headerTool.render,\n\t * - save : headerTool.save,\n\t * - displayInToolbox : true,\n\t * - enableLineBreaks : false\n\t */\n\t editor.start = function (userSettings) {\n\t\n\t init();\n\t\n\t editor.core.prepare(userSettings)\n\t\n\t // If all ok, make UI, bind events and parse initial-content\n\t .then(editor.ui.prepare).then(editor.tools.prepare).then(editor.sanitizer.prepare).then(editor.paste.prepare).then(editor.transport.prepare).then(editor.renderer.makeBlocksFromData).then(editor.ui.saveInputs).catch(function (error) {\n\t\n\t editor.core.log('Initialization failed with error: %o', 'warn', error);\n\t });\n\t };\n\t\n\t return editor;\n\t}({});\n\n/***/ }),\n/* 1 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\tvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\t\n\t/**\n\t * Codex Editor Core\n\t *\n\t * @author Codex Team\n\t * @version 1.1.3\n\t */\n\t\n\tmodule.exports = function (core) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * @public\n\t *\n\t * Editor preparing method\n\t * @return Promise\n\t */\n\t core.prepare = function (userSettings) {\n\t\n\t return new Promise(function (resolve, reject) {\n\t\n\t if (userSettings) {\n\t\n\t editor.settings.tools = userSettings.tools || editor.settings.tools;\n\t }\n\t\n\t if (userSettings.data) {\n\t\n\t editor.state.blocks = userSettings.data;\n\t }\n\t\n\t if (userSettings.initialBlockPlugin) {\n\t\n\t editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin;\n\t }\n\t\n\t if (userSettings.sanitizer) {\n\t\n\t editor.settings.sanitizer = userSettings.sanitizer;\n\t }\n\t\n\t editor.hideToolbar = userSettings.hideToolbar;\n\t\n\t editor.settings.placeholder = userSettings.placeholder || '';\n\t\n\t editor.nodes.holder = document.getElementById(userSettings.holderId || editor.settings.holderId);\n\t\n\t if (_typeof(editor.nodes.holder) === undefined || editor.nodes.holder === null) {\n\t\n\t reject(Error(\"Holder wasn't found by ID: #\" + userSettings.holderId));\n\t } else {\n\t\n\t resolve();\n\t }\n\t });\n\t };\n\t\n\t /**\n\t * Logging method\n\t * @param type = ['log', 'info', 'warn']\n\t */\n\t core.log = function (msg, type, arg) {\n\t\n\t type = type || 'log';\n\t\n\t if (!arg) {\n\t\n\t arg = msg || 'undefined';\n\t msg = '[codex-editor]: %o';\n\t } else {\n\t\n\t msg = '[codex-editor]: ' + msg;\n\t }\n\t\n\t try {\n\t\n\t if ('console' in window && window.console[type]) {\n\t\n\t if (arg) window.console[type](msg, arg);else window.console[type](msg);\n\t }\n\t } catch (e) {}\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Helper for insert one element after another\n\t */\n\t core.insertAfter = function (target, element) {\n\t\n\t target.parentNode.insertBefore(element, target.nextSibling);\n\t };\n\t\n\t /**\n\t * @const\n\t *\n\t * Readable DOM-node types map\n\t */\n\t core.nodeTypes = {\n\t TAG: 1,\n\t TEXT: 3,\n\t COMMENT: 8,\n\t DOCUMENT_FRAGMENT: 11\n\t };\n\t\n\t /**\n\t * @const\n\t * Readable keys map\n\t */\n\t core.keys = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91 };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Check object for DOM node\n\t */\n\t core.isDomNode = function (el) {\n\t\n\t return el && (typeof el === 'undefined' ? 'undefined' : _typeof(el)) === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG;\n\t };\n\t\n\t /**\n\t * Checks passed object for emptiness\n\t * @require ES5 - Object.keys\n\t * @param {object}\n\t */\n\t core.isEmpty = function (obj) {\n\t\n\t return Object.keys(obj).length === 0;\n\t };\n\t\n\t /**\n\t * Native Ajax\n\t * @param {String} settings.url - request URL\n\t * @param {function} settings.beforeSend - returned value will be passed as context to the Success, Error and Progress callbacks\n\t * @param {function} settings.success\n\t * @param {function} settings.progress\n\t */\n\t core.ajax = function (settings) {\n\t\n\t if (!settings || !settings.url) {\n\t\n\t return;\n\t }\n\t\n\t var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),\n\t encodedString,\n\t isFormData,\n\t prop;\n\t\n\t settings.async = true;\n\t settings.type = settings.type || 'GET';\n\t settings.data = settings.data || '';\n\t settings['content-type'] = settings['content-type'] || 'application/json; charset=utf-8';\n\t\n\t if (settings.type == 'GET' && settings.data) {\n\t\n\t settings.url = /\\?/.test(settings.url) ? settings.url + '&' + settings.data : settings.url + '?' + settings.data;\n\t } else {\n\t\n\t encodedString = '';\n\t for (prop in settings.data) {\n\t\n\t encodedString += prop + '=' + encodeURIComponent(settings.data[prop]) + '&';\n\t }\n\t }\n\t\n\t if (settings.withCredentials) {\n\t\n\t XMLHTTP.withCredentials = true;\n\t }\n\t\n\t /**\n\t * Value returned in beforeSend funtion will be passed as context to the other response callbacks\n\t * If beforeSend returns false, AJAX will be blocked\n\t */\n\t var responseContext = void 0,\n\t beforeSendResult = void 0;\n\t\n\t if (typeof settings.beforeSend === 'function') {\n\t\n\t beforeSendResult = settings.beforeSend.call();\n\t\n\t if (beforeSendResult === false) {\n\t\n\t return;\n\t }\n\t }\n\t\n\t XMLHTTP.open(settings.type, settings.url, settings.async);\n\t\n\t /**\n\t * If we send FormData, we need no content-type header\n\t */\n\t isFormData = isFormData_(settings.data);\n\t\n\t if (!isFormData) {\n\t\n\t if (settings.type !== 'POST') {\n\t\n\t XMLHTTP.setRequestHeader('Content-type', settings['content-type']);\n\t } else {\n\t\n\t XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n\t }\n\t }\n\t\n\t XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t\n\t responseContext = beforeSendResult || XMLHTTP;\n\t\n\t if (typeof settings.progress === 'function') {\n\t\n\t XMLHTTP.upload.onprogress = settings.progress.bind(responseContext);\n\t }\n\t\n\t XMLHTTP.onreadystatechange = function () {\n\t\n\t if (XMLHTTP.readyState === 4) {\n\t\n\t if (XMLHTTP.status === 200) {\n\t\n\t if (typeof settings.success === 'function') {\n\t\n\t settings.success.call(responseContext, XMLHTTP.responseText);\n\t }\n\t } else {\n\t\n\t if (typeof settings.error === 'function') {\n\t\n\t settings.error.call(responseContext, XMLHTTP.responseText, XMLHTTP.status);\n\t }\n\t }\n\t }\n\t };\n\t\n\t if (isFormData) {\n\t\n\t // Sending FormData\n\t XMLHTTP.send(settings.data);\n\t } else {\n\t\n\t // POST requests\n\t XMLHTTP.send(encodedString);\n\t }\n\t\n\t return XMLHTTP;\n\t };\n\t\n\t /**\n\t * Appends script to head of document\n\t * @return Promise\n\t */\n\t core.importScript = function (scriptPath, instanceName) {\n\t\n\t return new Promise(function (resolve, reject) {\n\t\n\t var script = void 0;\n\t\n\t /** Script is already loaded */\n\t if (!instanceName) {\n\t\n\t reject('Instance name is missed');\n\t } else if (document.getElementById(editor.scriptPrefix + instanceName)) {\n\t\n\t resolve(scriptPath);\n\t }\n\t\n\t script = document.createElement('SCRIPT');\n\t script.async = true;\n\t script.defer = true;\n\t script.id = editor.scriptPrefix + instanceName;\n\t\n\t script.onload = function () {\n\t\n\t resolve(scriptPath);\n\t };\n\t\n\t script.onerror = function () {\n\t\n\t reject(scriptPath);\n\t };\n\t\n\t script.src = scriptPath;\n\t document.head.appendChild(script);\n\t });\n\t };\n\t\n\t /**\n\t * Function for checking is it FormData object to send.\n\t * @param {Object} object to check\n\t * @return boolean\n\t */\n\t var isFormData_ = function isFormData_(object) {\n\t\n\t return object instanceof FormData;\n\t };\n\t\n\t /**\n\t * Check block\n\t * @param target\n\t * @description Checks target is it native input\n\t */\n\t core.isNativeInput = function (target) {\n\t\n\t var nativeInputAreas = ['INPUT', 'TEXTAREA'];\n\t\n\t return nativeInputAreas.indexOf(target.tagName) != -1;\n\t };\n\t\n\t /**\n\t * Check if block is empty\n\t * We should check block textContent, child native inputs and some exceptions like IMG and IFRAME\n\t *\n\t * @param block\n\t * @returns {boolean}\n\t */\n\t core.isBlockEmpty = function (block) {\n\t\n\t var EXCEPTION_TAGS = ['IMG', 'IFRAME'];\n\t\n\t var nativeInputs = block.querySelectorAll('textarea, input'),\n\t nativeInputsAreEmpty = true,\n\t textContentIsEmpty = !block.textContent.trim();\n\t\n\t Array.prototype.forEach.call(nativeInputs, function (input) {\n\t\n\t if (input.type == 'textarea' || input.type == 'text') {\n\t\n\t nativeInputsAreEmpty = nativeInputsAreEmpty && !input.value.trim();\n\t }\n\t });\n\t\n\t return textContentIsEmpty && nativeInputsAreEmpty && !EXCEPTION_TAGS.includes(block.tagName);\n\t };\n\t\n\t return core;\n\t}({});\n\n/***/ }),\n/* 2 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t* Module working with plugins\n\t*/\n\tmodule.exports = function () {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * Initialize plugins before using\n\t * Ex. Load scripts or call some internal methods\n\t * @return Promise\n\t */\n\t function prepare() {\n\t\n\t return new Promise(function (resolve_, reject_) {\n\t\n\t Promise.resolve()\n\t\n\t /**\n\t * Compose a sequence of plugins that requires preparation\n\t */\n\t .then(function () {\n\t\n\t var pluginsRequiresPreparation = [],\n\t allPlugins = editor.tools;\n\t\n\t for (var pluginName in allPlugins) {\n\t\n\t var plugin = allPlugins[pluginName];\n\t\n\t if (plugin.prepare && typeof plugin.prepare != 'function' || !plugin.prepare) {\n\t\n\t continue;\n\t }\n\t\n\t pluginsRequiresPreparation.push(plugin);\n\t }\n\t\n\t /**\n\t * If no one passed plugins requires preparation, finish prepare() and go ahead\n\t */\n\t if (!pluginsRequiresPreparation.length) {\n\t\n\t resolve_();\n\t }\n\t\n\t return pluginsRequiresPreparation;\n\t })\n\t\n\t /** Wait plugins while they prepares */\n\t .then(waitAllPluginsPreparation_).then(function () {\n\t\n\t editor.core.log('Plugins loaded', 'info');\n\t resolve_();\n\t }).catch(function (error) {\n\t\n\t reject_(error);\n\t });\n\t });\n\t }\n\t\n\t /**\n\t * @param {array} plugins - list of tools that requires preparation\n\t * @return {Promise} resolved while all plugins will be ready or failed\n\t */\n\t function waitAllPluginsPreparation_(plugins) {\n\t\n\t /**\n\t * @calls allPluginsProcessed__ when all plugins prepared or failed\n\t */\n\t return new Promise(function (allPluginsProcessed__) {\n\t\n\t /**\n\t * pluck each element from queue\n\t * First, send resolved Promise as previous value\n\t * Each plugins \"prepare\" method returns a Promise, that's why\n\t * reduce current element will not be able to continue while can't get\n\t * a resolved Promise\n\t *\n\t * If last plugin is \"prepared\" then go to the next stage of initialization\n\t */\n\t plugins.reduce(function (previousValue, plugin, iteration) {\n\t\n\t return previousValue.then(function () {\n\t\n\t /**\n\t * Wait till plugins prepared\n\t * @calls pluginIsReady__ when plugin is ready or failed\n\t */\n\t return new Promise(function (pluginIsReady__) {\n\t\n\t callPluginsPrepareMethod_(plugin).then(pluginIsReady__).then(function () {\n\t\n\t plugin.available = true;\n\t }).catch(function (error) {\n\t\n\t editor.core.log('Plugin \\xAB' + plugin.type + '\\xBB was not loaded. Preparation failed because %o', 'warn', error);\n\t plugin.available = false;\n\t plugin.loadingMessage = error;\n\t\n\t /** Go ahead even some plugin has problems */\n\t pluginIsReady__();\n\t }).then(function () {\n\t\n\t /** If last plugin has problems then just ignore and continue */\n\t if (iteration == plugins.length - 1) {\n\t\n\t allPluginsProcessed__();\n\t }\n\t });\n\t });\n\t });\n\t }, Promise.resolve());\n\t });\n\t }\n\t\n\t var callPluginsPrepareMethod_ = function callPluginsPrepareMethod_(plugin) {\n\t\n\t return plugin.prepare(plugin.config || {});\n\t };\n\t\n\t return {\n\t prepare: prepare\n\t };\n\t}();\n\n/***/ }),\n/* 3 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor UI module\n\t *\n\t * @author Codex Team\n\t * @version 1.2.0\n\t */\n\t\n\tmodule.exports = function (ui) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * Basic editor classnames\n\t */\n\t ui.className = {\n\t\n\t /**\n\t * @const {string} BLOCK_CLASSNAME - redactor blocks name\n\t */\n\t BLOCK_CLASSNAME: 'ce-block',\n\t\n\t /**\n\t * @const {String} wrapper for plugins content\n\t */\n\t BLOCK_CONTENT: 'ce-block__content',\n\t\n\t /**\n\t * @const {String} BLOCK_STRETCHED - makes block stretched\n\t */\n\t BLOCK_STRETCHED: 'ce-block--stretched',\n\t\n\t /**\n\t * @const {String} BLOCK_HIGHLIGHTED - adds background\n\t */\n\t BLOCK_HIGHLIGHTED: 'ce-block--focused',\n\t\n\t /**\n\t * @const {String} - for all default settings\n\t */\n\t SETTINGS_ITEM: 'ce-settings__item'\n\t\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Making main interface\n\t */\n\t ui.prepare = function () {\n\t\n\t return new Promise(function (resolve) {\n\t\n\t var wrapper = editor.draw.wrapper(),\n\t redactor = editor.draw.redactor(),\n\t toolbar = makeToolBar_();\n\t\n\t wrapper.appendChild(toolbar);\n\t wrapper.appendChild(redactor);\n\t\n\t /** Save created ui-elements to static nodes state */\n\t editor.nodes.wrapper = wrapper;\n\t editor.nodes.redactor = redactor;\n\t\n\t /** Append editor wrapper with redactor zone into holder */\n\t editor.nodes.holder.appendChild(wrapper);\n\t\n\t resolve();\n\t })\n\t\n\t /** Add toolbox tools */\n\t .then(addTools_)\n\t\n\t /** Make container for inline toolbar */\n\t .then(makeInlineToolbar_)\n\t\n\t /** Add inline toolbar tools */\n\t .then(addInlineToolbarTools_)\n\t\n\t /** Draw wrapper for notifications */\n\t .then(makeNotificationHolder_)\n\t\n\t /** Add eventlisteners to redactor elements */\n\t .then(bindEvents_).catch(function () {\n\t\n\t editor.core.log(\"Can't draw editor interface\");\n\t });\n\t };\n\t\n\t /**\n\t * @private\n\t * Draws inline toolbar zone\n\t */\n\t var makeInlineToolbar_ = function makeInlineToolbar_() {\n\t\n\t var container = editor.draw.inlineToolbar();\n\t\n\t /** Append to redactor new inline block */\n\t editor.nodes.inlineToolbar.wrapper = container;\n\t\n\t /** Draw toolbar buttons */\n\t editor.nodes.inlineToolbar.buttons = editor.draw.inlineToolbarButtons();\n\t\n\t /** Buttons action or settings */\n\t editor.nodes.inlineToolbar.actions = editor.draw.inlineToolbarActions();\n\t\n\t /** Append to inline toolbar buttons as part of it */\n\t editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.buttons);\n\t editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.actions);\n\t\n\t editor.nodes.wrapper.appendChild(editor.nodes.inlineToolbar.wrapper);\n\t };\n\t\n\t var makeToolBar_ = function makeToolBar_() {\n\t\n\t var toolbar = editor.draw.toolbar(),\n\t blockButtons = makeToolbarSettings_(),\n\t toolbarContent = makeToolbarContent_();\n\t\n\t /** Appending first-level block buttons */\n\t toolbar.appendChild(blockButtons);\n\t\n\t /** Append toolbarContent to toolbar */\n\t toolbar.appendChild(toolbarContent);\n\t\n\t /** Make toolbar global */\n\t editor.nodes.toolbar = toolbar;\n\t\n\t return toolbar;\n\t };\n\t\n\t var makeToolbarContent_ = function makeToolbarContent_() {\n\t\n\t var toolbarContent = editor.draw.toolbarContent(),\n\t toolbox = editor.draw.toolbox(),\n\t plusButton = editor.draw.plusButton();\n\t\n\t /** Append plus button */\n\t toolbarContent.appendChild(plusButton);\n\t\n\t /** Appending toolbar tools */\n\t toolbarContent.appendChild(toolbox);\n\t\n\t /** Make Toolbox and plusButton global */\n\t editor.nodes.toolbox = toolbox;\n\t editor.nodes.plusButton = plusButton;\n\t\n\t return toolbarContent;\n\t };\n\t\n\t var makeToolbarSettings_ = function makeToolbarSettings_() {\n\t\n\t var blockSettings = editor.draw.blockSettings(),\n\t blockButtons = editor.draw.blockButtons(),\n\t defaultSettings = editor.draw.defaultSettings(),\n\t showSettingsButton = editor.draw.settingsButton(),\n\t showTrashButton = editor.toolbar.settings.makeRemoveBlockButton(),\n\t pluginSettings = editor.draw.pluginsSettings();\n\t\n\t /** Add default and plugins settings */\n\t blockSettings.appendChild(pluginSettings);\n\t blockSettings.appendChild(defaultSettings);\n\t\n\t /**\n\t * Make blocks buttons\n\t * This block contains settings button and remove block button\n\t */\n\t blockButtons.appendChild(showSettingsButton);\n\t blockButtons.appendChild(showTrashButton);\n\t blockButtons.appendChild(blockSettings);\n\t\n\t /** Make BlockSettings, PluginSettings, DefaultSettings global */\n\t editor.nodes.blockSettings = blockSettings;\n\t editor.nodes.pluginSettings = pluginSettings;\n\t editor.nodes.defaultSettings = defaultSettings;\n\t editor.nodes.showSettingsButton = showSettingsButton;\n\t editor.nodes.showTrashButton = showTrashButton;\n\t\n\t return blockButtons;\n\t };\n\t\n\t /** Draw notifications holder */\n\t var makeNotificationHolder_ = function makeNotificationHolder_() {\n\t\n\t /** Append block with notifications to the document */\n\t editor.nodes.notifications = editor.notifications.createHolder();\n\t };\n\t\n\t /**\n\t * @private\n\t * Append tools passed in editor.tools\n\t */\n\t var addTools_ = function addTools_() {\n\t\n\t var tool, toolName, toolButton;\n\t\n\t for (toolName in editor.settings.tools) {\n\t\n\t tool = editor.settings.tools[toolName];\n\t\n\t editor.tools[toolName] = tool;\n\t\n\t if (!tool.iconClassname && tool.displayInToolbox) {\n\t\n\t editor.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', toolName);\n\t continue;\n\t }\n\t\n\t if (typeof tool.render != 'function') {\n\t\n\t editor.core.log('render method missed. Tool %o skipped', 'warn', toolName);\n\t continue;\n\t }\n\t\n\t if (!tool.displayInToolbox) {\n\t\n\t continue;\n\t } else {\n\t\n\t /** if tools is for toolbox */\n\t toolButton = editor.draw.toolbarButton(toolName, tool.iconClassname);\n\t\n\t editor.nodes.toolbox.appendChild(toolButton);\n\t\n\t editor.nodes.toolbarButtons[toolName] = toolButton;\n\t }\n\t }\n\t };\n\t\n\t var addInlineToolbarTools_ = function addInlineToolbarTools_() {\n\t\n\t var tools = {\n\t\n\t bold: {\n\t icon: 'ce-icon-bold',\n\t command: 'bold'\n\t },\n\t\n\t italic: {\n\t icon: 'ce-icon-italic',\n\t command: 'italic'\n\t },\n\t\n\t link: {\n\t icon: 'ce-icon-link',\n\t command: 'createLink'\n\t }\n\t };\n\t\n\t var toolButton, tool;\n\t\n\t for (var name in tools) {\n\t\n\t tool = tools[name];\n\t\n\t toolButton = editor.draw.toolbarButtonInline(name, tool.icon);\n\t\n\t editor.nodes.inlineToolbar.buttons.appendChild(toolButton);\n\t /**\n\t * Add callbacks to this buttons\n\t */\n\t editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command);\n\t }\n\t };\n\t\n\t /**\n\t * @private\n\t * Bind editor UI events\n\t */\n\t var bindEvents_ = function bindEvents_() {\n\t\n\t editor.core.log('ui.bindEvents fired', 'info');\n\t\n\t // window.addEventListener('error', function (errorMsg, url, lineNumber) {\n\t // editor.notifications.errorThrown(errorMsg, event);\n\t // }, false );\n\t\n\t /** All keydowns on Document */\n\t editor.listeners.add(document, 'keydown', editor.callback.globalKeydown, false);\n\t\n\t /** All keydowns on Redactor zone */\n\t editor.listeners.add(editor.nodes.redactor, 'keydown', editor.callback.redactorKeyDown, false);\n\t\n\t /** All keydowns on Document */\n\t editor.listeners.add(document, 'keyup', editor.callback.globalKeyup, false);\n\t\n\t /**\n\t * Mouse click to radactor\n\t */\n\t editor.listeners.add(editor.nodes.redactor, 'click', editor.callback.redactorClicked, false);\n\t\n\t /**\n\t * Clicks to the Plus button\n\t */\n\t editor.listeners.add(editor.nodes.plusButton, 'click', editor.callback.plusButtonClicked, false);\n\t\n\t /**\n\t * Clicks to SETTINGS button in toolbar\n\t */\n\t editor.listeners.add(editor.nodes.showSettingsButton, 'click', editor.callback.showSettingsButtonClicked, false);\n\t\n\t /** Bind click listeners on toolbar buttons */\n\t for (var button in editor.nodes.toolbarButtons) {\n\t\n\t editor.listeners.add(editor.nodes.toolbarButtons[button], 'click', editor.callback.toolbarButtonClicked, false);\n\t }\n\t };\n\t\n\t ui.addBlockHandlers = function (block) {\n\t\n\t if (!block) return;\n\t\n\t /**\n\t * Block keydowns\n\t */\n\t editor.listeners.add(block, 'keydown', editor.callback.blockKeydown, false);\n\t\n\t /**\n\t * Pasting content from another source\n\t * We have two type of sanitization\n\t * First - uses deep-first search algorithm to get sub nodes,\n\t * sanitizes whole Block_content and replaces cleared nodes\n\t * This method is deprecated\n\t * Method is used in editor.callback.blockPaste(event)\n\t *\n\t * Secont - uses Mutation observer.\n\t * Observer \"observe\" DOM changes and send changings to callback.\n\t * Callback gets changed node, not whole Block_content.\n\t * Inserted or changed node, which we've gotten have been cleared and replaced with diry node\n\t *\n\t * Method is used in editor.callback.blockPasteViaSanitize(event)\n\t *\n\t * @uses html-janitor\n\t * @example editor.callback.blockPasteViaSanitize(event), the second method.\n\t *\n\t */\n\t editor.listeners.add(block, 'paste', editor.paste.blockPasteCallback, false);\n\t\n\t /**\n\t * Show inline toolbar for selected text\n\t */\n\t editor.listeners.add(block, 'mouseup', editor.toolbar.inline.show, false);\n\t editor.listeners.add(block, 'keyup', editor.toolbar.inline.show, false);\n\t };\n\t\n\t /** getting all contenteditable elements */\n\t ui.saveInputs = function () {\n\t\n\t var redactor = editor.nodes.redactor;\n\t\n\t editor.state.inputs = [];\n\t\n\t /** Save all inputs in global variable state */\n\t var inputs = redactor.querySelectorAll('[contenteditable], input, textarea');\n\t\n\t Array.prototype.map.call(inputs, function (current) {\n\t\n\t if (!current.type || current.type == 'text' || current.type == 'textarea') {\n\t\n\t editor.state.inputs.push(current);\n\t }\n\t });\n\t };\n\t\n\t /**\n\t * Adds first initial block on empty redactor\n\t */\n\t ui.addInitialBlock = function () {\n\t\n\t var initialBlockType = editor.settings.initialBlockPlugin,\n\t initialBlock;\n\t\n\t if (!editor.tools[initialBlockType]) {\n\t\n\t editor.core.log('Plugin %o was not implemented and can\\'t be used as initial block', 'warn', initialBlockType);\n\t return;\n\t }\n\t\n\t initialBlock = editor.tools[initialBlockType].render();\n\t\n\t initialBlock.setAttribute('data-placeholder', editor.settings.placeholder);\n\t\n\t editor.content.insertBlock({\n\t type: initialBlockType,\n\t block: initialBlock\n\t });\n\t\n\t editor.content.workingNodeChanged(initialBlock);\n\t };\n\t\n\t ui.setInlineToolbarButtonBehaviour = function (button, type) {\n\t\n\t editor.listeners.add(button, 'mousedown', function (event) {\n\t\n\t editor.toolbar.inline.toolClicked(event, type);\n\t }, false);\n\t };\n\t\n\t return ui;\n\t}({});\n\n/***/ }),\n/* 4 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t *\n\t * Codex.Editor Transport Module\n\t *\n\t * @copyright 2017 Codex-Team\n\t * @version 1.2.0\n\t */\n\t\n\tmodule.exports = function (transport) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * @private {Object} current XmlHttpRequest instance\n\t */\n\t var currentRequest = null;\n\t\n\t /**\n\t * @type {null} | {DOMElement} input - keeps input element in memory\n\t */\n\t transport.input = null;\n\t\n\t /**\n\t * @property {Object} arguments - keep plugin settings and defined callbacks\n\t */\n\t transport.arguments = null;\n\t\n\t /**\n\t * Prepares input element where will be files\n\t */\n\t transport.prepare = function () {\n\t\n\t var input = editor.draw.node('INPUT', '', { type: 'file' });\n\t\n\t editor.listeners.add(input, 'change', editor.transport.fileSelected);\n\t editor.transport.input = input;\n\t };\n\t\n\t /** Clear input when files is uploaded */\n\t transport.clearInput = function () {\n\t\n\t /** Remove old input */\n\t transport.input = null;\n\t\n\t /** Prepare new one */\n\t transport.prepare();\n\t };\n\t\n\t /**\n\t * Callback for file selection\n\t * @param {Event} event\n\t */\n\t transport.fileSelected = function () {\n\t\n\t var input = this,\n\t i,\n\t files = input.files,\n\t formData = new FormData();\n\t\n\t if (editor.transport.arguments.multiple === true) {\n\t\n\t for (i = 0; i < files.length; i++) {\n\t\n\t formData.append('files[]', files[i], files[i].name);\n\t }\n\t } else {\n\t\n\t formData.append('files', files[0], files[0].name);\n\t }\n\t\n\t currentRequest = editor.core.ajax({\n\t type: 'POST',\n\t data: formData,\n\t url: editor.transport.arguments.url,\n\t beforeSend: editor.transport.arguments.beforeSend,\n\t success: editor.transport.arguments.success,\n\t error: editor.transport.arguments.error,\n\t progress: editor.transport.arguments.progress\n\t });\n\t\n\t /** Clear input */\n\t transport.clearInput();\n\t };\n\t\n\t /**\n\t * Use plugin callbacks\n\t * @protected\n\t *\n\t * @param {Object} args - can have :\n\t * @param {String} args.url - fetch URL\n\t * @param {Function} args.beforeSend - function calls before sending ajax\n\t * @param {Function} args.success - success callback\n\t * @param {Function} args.error - on error handler\n\t * @param {Function} args.progress - xhr onprogress handler\n\t * @param {Boolean} args.multiple - allow select several files\n\t * @param {String} args.accept - adds accept attribute\n\t */\n\t transport.selectAndUpload = function (args) {\n\t\n\t transport.arguments = args;\n\t\n\t if (args.multiple === true) {\n\t\n\t transport.input.setAttribute('multiple', 'multiple');\n\t }\n\t\n\t if (args.accept) {\n\t\n\t transport.input.setAttribute('accept', args.accept);\n\t }\n\t\n\t transport.input.click();\n\t };\n\t\n\t transport.abort = function () {\n\t\n\t currentRequest.abort();\n\t\n\t currentRequest = null;\n\t };\n\t\n\t return transport;\n\t}({});\n\n/***/ }),\n/* 5 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Renderer Module\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (renderer) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * Asyncronously parses input JSON to redactor blocks\n\t */\n\t renderer.makeBlocksFromData = function () {\n\t\n\t /**\n\t * If redactor is empty, add first paragraph to start writing\n\t */\n\t if (editor.core.isEmpty(editor.state.blocks) || !editor.state.blocks.items.length) {\n\t\n\t editor.ui.addInitialBlock();\n\t return;\n\t }\n\t\n\t Promise.resolve()\n\t\n\t /** First, get JSON from state */\n\t .then(function () {\n\t\n\t return editor.state.blocks;\n\t })\n\t\n\t /** Then, start to iterate they */\n\t .then(editor.renderer.appendBlocks)\n\t\n\t /** Write log if something goes wrong */\n\t .catch(function (error) {\n\t\n\t editor.core.log('Error while parsing JSON: %o', 'error', error);\n\t });\n\t };\n\t\n\t /**\n\t * Parses JSON to blocks\n\t * @param {object} data\n\t * @return Primise -> nodeList\n\t */\n\t renderer.appendBlocks = function (data) {\n\t\n\t var blocks = data.items;\n\t\n\t /**\n\t * Sequence of one-by-one blocks appending\n\t * Uses to save blocks order after async-handler\n\t */\n\t var nodeSequence = Promise.resolve();\n\t\n\t for (var index = 0; index < blocks.length; index++) {\n\t\n\t /** Add node to sequence at specified index */\n\t editor.renderer.appendNodeAtIndex(nodeSequence, blocks, index);\n\t }\n\t };\n\t\n\t /**\n\t * Append node at specified index\n\t */\n\t renderer.appendNodeAtIndex = function (nodeSequence, blocks, index) {\n\t\n\t /** We need to append node to sequence */\n\t nodeSequence\n\t\n\t /** first, get node async-aware */\n\t .then(function () {\n\t\n\t return editor.renderer.getNodeAsync(blocks, index);\n\t })\n\t\n\t /**\n\t * second, compose editor-block from JSON object\n\t */\n\t .then(editor.renderer.createBlockFromData)\n\t\n\t /**\n\t * now insert block to redactor\n\t */\n\t .then(function (blockData) {\n\t\n\t /**\n\t * blockData has 'block', 'type' and 'stretched' information\n\t */\n\t editor.content.insertBlock(blockData);\n\t\n\t /** Pass created block to next step */\n\t return blockData.block;\n\t })\n\t\n\t /** Log if something wrong with node */\n\t .catch(function (error) {\n\t\n\t editor.core.log('Node skipped while parsing because %o', 'error', error);\n\t });\n\t };\n\t\n\t /**\n\t * Asynchronously returns block data from blocksList by index\n\t * @return Promise to node\n\t */\n\t renderer.getNodeAsync = function (blocksList, index) {\n\t\n\t return Promise.resolve().then(function () {\n\t\n\t return {\n\t tool: blocksList[index],\n\t position: index\n\t };\n\t });\n\t };\n\t\n\t /**\n\t * Creates editor block by JSON-data\n\t *\n\t * @uses render method of each plugin\n\t *\n\t * @param {Object} toolData.tool\n\t * { header : {\n\t * text: '',\n\t * type: 'H3', ...\n\t * }\n\t * }\n\t * @param {Number} toolData.position - index in input-blocks array\n\t * @return {Object} with type and Element\n\t */\n\t renderer.createBlockFromData = function (toolData) {\n\t\n\t /** New parser */\n\t var block,\n\t tool = toolData.tool,\n\t pluginName = tool.type;\n\t\n\t /** Get first key of object that stores plugin name */\n\t // for (var pluginName in blockData) break;\n\t\n\t /** Check for plugin existance */\n\t if (!editor.tools[pluginName]) {\n\t\n\t throw Error('Plugin \\xAB' + pluginName + '\\xBB not found');\n\t }\n\t\n\t /** Check for plugin having render method */\n\t if (typeof editor.tools[pluginName].render != 'function') {\n\t\n\t throw Error('Plugin \\xAB' + pluginName + '\\xBB must have \\xABrender\\xBB method');\n\t }\n\t\n\t if (editor.tools[pluginName].available === false) {\n\t\n\t block = editor.draw.unavailableBlock();\n\t\n\t block.innerHTML = editor.tools[pluginName].loadingMessage;\n\t\n\t /**\n\t * Saver will extract data from initial block data by position in array\n\t */\n\t block.dataset.inputPosition = toolData.position;\n\t } else {\n\t\n\t /** New Parser */\n\t block = editor.tools[pluginName].render(tool.data);\n\t }\n\t\n\t /** is first-level block stretched */\n\t var stretched = editor.tools[pluginName].isStretched || false;\n\t\n\t /** Retrun type and block */\n\t return {\n\t type: pluginName,\n\t block: block,\n\t stretched: stretched\n\t };\n\t };\n\t\n\t return renderer;\n\t}({});\n\n/***/ }),\n/* 6 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Saver\n\t *\n\t * @author Codex Team\n\t * @version 1.1.0\n\t */\n\t\n\tmodule.exports = function (saver) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * @public\n\t * Save blocks\n\t */\n\t saver.save = function () {\n\t\n\t /** Save html content of redactor to memory */\n\t editor.state.html = editor.nodes.redactor.innerHTML;\n\t\n\t /** Clean jsonOutput state */\n\t editor.state.jsonOutput = [];\n\t\n\t return saveBlocks(editor.nodes.redactor.childNodes);\n\t };\n\t\n\t /**\n\t * @private\n\t * Save each block data\n\t *\n\t * @param blocks\n\t * @returns {Promise.}\n\t */\n\t var saveBlocks = function saveBlocks(blocks) {\n\t\n\t var data = [];\n\t\n\t for (var index = 0; index < blocks.length; index++) {\n\t\n\t data.push(getBlockData(blocks[index]));\n\t }\n\t\n\t return Promise.all(data).then(makeOutput).catch(editor.core.log);\n\t };\n\t\n\t /** Save and validate block data */\n\t var getBlockData = function getBlockData(block) {\n\t\n\t return saveBlockData(block).then(validateBlockData).catch(editor.core.log);\n\t };\n\t\n\t /**\n\t * @private\n\t * Call block`s plugin save method and return saved data\n\t *\n\t * @param block\n\t * @returns {Object}\n\t */\n\t var saveBlockData = function saveBlockData(block) {\n\t\n\t var pluginName = block.dataset.tool;\n\t\n\t /** Check for plugin existence */\n\t if (!editor.tools[pluginName]) {\n\t\n\t editor.core.log('Plugin \\xAB' + pluginName + '\\xBB not found', 'error');\n\t return { data: null, pluginName: null };\n\t }\n\t\n\t /** Check for plugin having save method */\n\t if (typeof editor.tools[pluginName].save !== 'function') {\n\t\n\t editor.core.log('Plugin \\xAB' + pluginName + '\\xBB must have save method', 'error');\n\t return { data: null, pluginName: null };\n\t }\n\t\n\t /** Result saver */\n\t var blockContent = block.childNodes[0],\n\t pluginsContent = blockContent.childNodes[0],\n\t position = pluginsContent.dataset.inputPosition;\n\t\n\t /** If plugin wasn't available then return data from cache */\n\t if (editor.tools[pluginName].available === false) {\n\t\n\t return Promise.resolve({ data: codex.editor.state.blocks.items[position].data, pluginName: pluginName });\n\t }\n\t\n\t return Promise.resolve(pluginsContent).then(editor.tools[pluginName].save).then(function (data) {\n\t return Object({ data: data, pluginName: pluginName });\n\t });\n\t };\n\t\n\t /**\n\t * Call plugin`s validate method. Return false if validation failed\n\t *\n\t * @param data\n\t * @param pluginName\n\t * @returns {Object|Boolean}\n\t */\n\t var validateBlockData = function validateBlockData(_ref) {\n\t var data = _ref.data,\n\t pluginName = _ref.pluginName;\n\t\n\t\n\t if (!data || !pluginName) {\n\t\n\t return false;\n\t }\n\t\n\t if (editor.tools[pluginName].validate) {\n\t\n\t var result = editor.tools[pluginName].validate(data);\n\t\n\t /**\n\t * Do not allow invalid data\n\t */\n\t if (!result) {\n\t\n\t return false;\n\t }\n\t }\n\t\n\t return { data: data, pluginName: pluginName };\n\t };\n\t\n\t /**\n\t * Compile article output\n\t *\n\t * @param savedData\n\t * @returns {{time: number, version, items: (*|Array)}}\n\t */\n\t var makeOutput = function makeOutput(savedData) {\n\t\n\t savedData = savedData.filter(function (blockData) {\n\t return blockData;\n\t });\n\t\n\t var items = savedData.map(function (blockData) {\n\t return Object({ type: blockData.pluginName, data: blockData.data });\n\t });\n\t\n\t editor.state.jsonOutput = items;\n\t\n\t return {\n\t id: editor.state.blocks.id || null,\n\t time: +new Date(),\n\t version: editor.version,\n\t items: items\n\t };\n\t };\n\t\n\t return saver;\n\t}({});\n\n/***/ }),\n/* 7 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Content Module\n\t * Works with DOM\n\t *\n\t * @module Codex Editor content module\n\t *\n\t * @author Codex Team\n\t * @version 1.3.13\n\t *\n\t * @description Module works with Elements that have been appended to the main DOM\n\t */\n\t\n\tmodule.exports = function (content) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * Links to current active block\n\t * @type {null | Element}\n\t */\n\t content.currentNode = null;\n\t\n\t /**\n\t * clicked in redactor area\n\t * @type {null | Boolean}\n\t */\n\t content.editorAreaHightlighted = null;\n\t\n\t /**\n\t * @deprecated\n\t * Synchronizes redactor with original textarea\n\t */\n\t content.sync = function () {\n\t\n\t editor.core.log('syncing...');\n\t\n\t /**\n\t * Save redactor content to editor.state\n\t */\n\t editor.state.html = editor.nodes.redactor.innerHTML;\n\t };\n\t\n\t /**\n\t * Appends background to the block\n\t *\n\t * @description add CSS class to highlight visually first-level block area\n\t */\n\t content.markBlock = function () {\n\t\n\t editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED);\n\t };\n\t\n\t /**\n\t * Clear background\n\t *\n\t * @description clears styles that highlights block\n\t */\n\t content.clearMark = function () {\n\t\n\t if (editor.content.currentNode) {\n\t\n\t editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED);\n\t }\n\t };\n\t\n\t /**\n\t * Finds first-level block\n\t *\n\t * @param {Element} node - selected or clicked in redactors area node\n\t * @protected\n\t *\n\t * @description looks for first-level block.\n\t * gets parent while node is not first-level\n\t */\n\t content.getFirstLevelBlock = function (node) {\n\t\n\t if (!editor.core.isDomNode(node)) {\n\t\n\t node = node.parentNode;\n\t }\n\t\n\t if (node === editor.nodes.redactor || node === document.body) {\n\t\n\t return null;\n\t } else {\n\t\n\t while (!node.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\n\t\n\t node = node.parentNode;\n\t }\n\t\n\t return node;\n\t }\n\t };\n\t\n\t /**\n\t * Trigger this event when working node changed\n\t * @param {Element} targetNode - first-level of this node will be current\n\t * @protected\n\t *\n\t * @description If targetNode is first-level then we set it as current else we look for parents to find first-level\n\t */\n\t content.workingNodeChanged = function (targetNode) {\n\t\n\t /** Clear background from previous marked block before we change */\n\t editor.content.clearMark();\n\t\n\t if (!targetNode) {\n\t\n\t return;\n\t }\n\t\n\t content.currentNode = content.getFirstLevelBlock(targetNode);\n\t };\n\t\n\t /**\n\t * Replaces one redactor block with another\n\t * @protected\n\t * @param {Element} targetBlock - block to replace. Mostly currentNode.\n\t * @param {Element} newBlock\n\t * @param {string} newBlockType - type of new block; we need to store it to data-attribute\n\t *\n\t * [!] Function does not saves old block content.\n\t * You can get it manually and pass with newBlock.innerHTML\n\t */\n\t content.replaceBlock = function (targetBlock, newBlock) {\n\t\n\t if (!targetBlock || !newBlock) {\n\t\n\t editor.core.log('replaceBlock: missed params');\n\t return;\n\t }\n\t\n\t /** If target-block is not a frist-level block, then we iterate parents to find it */\n\t while (!targetBlock.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\n\t\n\t targetBlock = targetBlock.parentNode;\n\t }\n\t\n\t /** Replacing */\n\t editor.nodes.redactor.replaceChild(newBlock, targetBlock);\n\t\n\t /**\n\t * Set new node as current\n\t */\n\t editor.content.workingNodeChanged(newBlock);\n\t\n\t /**\n\t * Add block handlers\n\t */\n\t editor.ui.addBlockHandlers(newBlock);\n\t\n\t /**\n\t * Save changes\n\t */\n\t editor.ui.saveInputs();\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Inserts new block to redactor\n\t * Wrapps block into a DIV with BLOCK_CLASSNAME class\n\t *\n\t * @param blockData {object}\n\t * @param blockData.block {Element} element with block content\n\t * @param blockData.type {string} block plugin\n\t * @param needPlaceCaret {bool} pass true to set caret in new block\n\t *\n\t */\n\t content.insertBlock = function (blockData, needPlaceCaret) {\n\t\n\t var workingBlock = editor.content.currentNode,\n\t newBlockContent = blockData.block,\n\t blockType = blockData.type,\n\t isStretched = blockData.stretched;\n\t\n\t var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched);\n\t\n\t if (workingBlock) {\n\t\n\t editor.core.insertAfter(workingBlock, newBlock);\n\t } else {\n\t\n\t /**\n\t * If redactor is empty, append as first child\n\t */\n\t editor.nodes.redactor.appendChild(newBlock);\n\t }\n\t\n\t /**\n\t * Block handler\n\t */\n\t editor.ui.addBlockHandlers(newBlock);\n\t\n\t /**\n\t * Set new node as current\n\t */\n\t editor.content.workingNodeChanged(newBlock);\n\t\n\t /**\n\t * Save changes\n\t */\n\t editor.ui.saveInputs();\n\t\n\t if (needPlaceCaret) {\n\t\n\t /**\n\t * If we don't know input index then we set default value -1\n\t */\n\t var currentInputIndex = editor.caret.getCurrentInputIndex() || -1;\n\t\n\t if (currentInputIndex == -1) {\n\t\n\t var editableElement = newBlock.querySelector('[contenteditable]'),\n\t emptyText = document.createTextNode('');\n\t\n\t editableElement.appendChild(emptyText);\n\t editor.caret.set(editableElement, 0, 0);\n\t\n\t editor.toolbar.move();\n\t editor.toolbar.showPlusButton();\n\t } else {\n\t\n\t if (currentInputIndex === editor.state.inputs.length - 1) return;\n\t\n\t /** Timeout for browsers execution */\n\t window.setTimeout(function () {\n\t\n\t /** Setting to the new input */\n\t editor.caret.setToNextBlock(currentInputIndex);\n\t editor.toolbar.move();\n\t editor.toolbar.open();\n\t }, 10);\n\t }\n\t }\n\t\n\t /**\n\t * Block is inserted, wait for new click that defined focusing on editors area\n\t * @type {boolean}\n\t */\n\t content.editorAreaHightlighted = false;\n\t };\n\t\n\t /**\n\t * Replaces blocks with saving content\n\t * @protected\n\t * @param {Element} noteToReplace\n\t * @param {Element} newNode\n\t * @param {Element} blockType\n\t */\n\t content.switchBlock = function (blockToReplace, newBlock, tool) {\n\t\n\t tool = tool || editor.content.currentNode.dataset.tool;\n\t var newBlockComposed = composeNewBlock_(newBlock, tool);\n\t\n\t /** Replacing */\n\t editor.content.replaceBlock(blockToReplace, newBlockComposed);\n\t\n\t /** Save new Inputs when block is changed */\n\t editor.ui.saveInputs();\n\t };\n\t\n\t /**\n\t * Iterates between child noted and looking for #text node on deepest level\n\t * @protected\n\t *\n\t * @param {Element} block - node where find\n\t * @param {int} postiton - starting postion\n\t * Example: childNodex.length to find from the end\n\t * or 0 to find from the start\n\t * @return {Text} block\n\t * @uses DFS\n\t */\n\t content.getDeepestTextNodeFromPosition = function (block, position) {\n\t\n\t /**\n\t * Clear Block from empty and useless spaces with trim.\n\t * Such nodes we should remove\n\t */\n\t var blockChilds = block.childNodes,\n\t index,\n\t node,\n\t text;\n\t\n\t for (index = 0; index < blockChilds.length; index++) {\n\t\n\t node = blockChilds[index];\n\t\n\t if (node.nodeType == editor.core.nodeTypes.TEXT) {\n\t\n\t text = node.textContent.trim();\n\t\n\t /** Text is empty. We should remove this child from node before we start DFS\n\t * decrease the quantity of childs.\n\t */\n\t if (text === '') {\n\t\n\t block.removeChild(node);\n\t position--;\n\t }\n\t }\n\t }\n\t\n\t if (block.childNodes.length === 0) {\n\t\n\t return document.createTextNode('');\n\t }\n\t\n\t /** Setting default position when we deleted all empty nodes */\n\t if (position < 0) position = 1;\n\t\n\t var lookingFromStart = false;\n\t\n\t /** For looking from START */\n\t if (position === 0) {\n\t\n\t lookingFromStart = true;\n\t position = 1;\n\t }\n\t\n\t while (position) {\n\t\n\t /** initial verticle of node. */\n\t if (lookingFromStart) {\n\t\n\t block = block.childNodes[0];\n\t } else {\n\t\n\t block = block.childNodes[position - 1];\n\t }\n\t\n\t if (block.nodeType == editor.core.nodeTypes.TAG) {\n\t\n\t position = block.childNodes.length;\n\t } else if (block.nodeType == editor.core.nodeTypes.TEXT) {\n\t\n\t position = 0;\n\t }\n\t }\n\t\n\t return block;\n\t };\n\t\n\t /**\n\t * @private\n\t * @param {Element} block - current plugins render\n\t * @param {String} tool - plugins name\n\t * @param {Boolean} isStretched - make stretched block or not\n\t *\n\t * @description adds necessary information to wrap new created block by first-level holder\n\t */\n\t var composeNewBlock_ = function composeNewBlock_(block, tool, isStretched) {\n\t\n\t var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}),\n\t blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {});\n\t\n\t blockContent.appendChild(block);\n\t newBlock.appendChild(blockContent);\n\t\n\t if (isStretched) {\n\t\n\t blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED);\n\t }\n\t\n\t newBlock.dataset.tool = tool;\n\t return newBlock;\n\t };\n\t\n\t /**\n\t * Returns Range object of current selection\n\t * @protected\n\t */\n\t content.getRange = function () {\n\t\n\t var selection = window.getSelection().getRangeAt(0);\n\t\n\t return selection;\n\t };\n\t\n\t /**\n\t * Divides block in two blocks (after and before caret)\n\t *\n\t * @protected\n\t * @param {int} inputIndex - target input index\n\t *\n\t * @description splits current input content to the separate blocks\n\t * When enter is pressed among the words, that text will be splited.\n\t */\n\t content.splitBlock = function (inputIndex) {\n\t\n\t var selection = window.getSelection(),\n\t anchorNode = selection.anchorNode,\n\t anchorNodeText = anchorNode.textContent,\n\t caretOffset = selection.anchorOffset,\n\t textBeforeCaret,\n\t textNodeBeforeCaret,\n\t textAfterCaret,\n\t textNodeAfterCaret;\n\t\n\t var currentBlock = editor.content.currentNode.querySelector('[contentEditable]');\n\t\n\t textBeforeCaret = anchorNodeText.substring(0, caretOffset);\n\t textAfterCaret = anchorNodeText.substring(caretOffset);\n\t\n\t textNodeBeforeCaret = document.createTextNode(textBeforeCaret);\n\t\n\t if (textAfterCaret) {\n\t\n\t textNodeAfterCaret = document.createTextNode(textAfterCaret);\n\t }\n\t\n\t var previousChilds = [],\n\t nextChilds = [],\n\t reachedCurrent = false;\n\t\n\t if (textNodeAfterCaret) {\n\t\n\t nextChilds.push(textNodeAfterCaret);\n\t }\n\t\n\t for (var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) {\n\t\n\t if (child != anchorNode) {\n\t\n\t if (!reachedCurrent) {\n\t\n\t previousChilds.push(child);\n\t } else {\n\t\n\t nextChilds.push(child);\n\t }\n\t } else {\n\t\n\t reachedCurrent = true;\n\t }\n\t }\n\t\n\t /** Clear current input */\n\t editor.state.inputs[inputIndex].innerHTML = '';\n\t\n\t /**\n\t * Append all childs founded before anchorNode\n\t */\n\t var previousChildsLength = previousChilds.length;\n\t\n\t for (i = 0; i < previousChildsLength; i++) {\n\t\n\t editor.state.inputs[inputIndex].appendChild(previousChilds[i]);\n\t }\n\t\n\t editor.state.inputs[inputIndex].appendChild(textNodeBeforeCaret);\n\t\n\t /**\n\t * Append text node which is after caret\n\t */\n\t var nextChildsLength = nextChilds.length,\n\t newNode = document.createElement('div');\n\t\n\t for (i = 0; i < nextChildsLength; i++) {\n\t\n\t newNode.appendChild(nextChilds[i]);\n\t }\n\t\n\t newNode = newNode.innerHTML;\n\t\n\t /** This type of block creates when enter is pressed */\n\t var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\t\n\t /**\n\t * Make new paragraph with text after caret\n\t */\n\t editor.content.insertBlock({\n\t type: NEW_BLOCK_TYPE,\n\t block: editor.tools[NEW_BLOCK_TYPE].render({\n\t text: newNode\n\t })\n\t }, true);\n\t };\n\t\n\t /**\n\t * Merges two blocks — current and target\n\t * If target index is not exist, then previous will be as target\n\t *\n\t * @protected\n\t * @param {int} currentInputIndex\n\t * @param {int} targetInputIndex\n\t *\n\t * @description gets two inputs indexes and merges into one\n\t */\n\t content.mergeBlocks = function (currentInputIndex, targetInputIndex) {\n\t\n\t /** If current input index is zero, then prevent method execution */\n\t if (currentInputIndex === 0) {\n\t\n\t return;\n\t }\n\t\n\t var targetInput,\n\t currentInputContent = editor.state.inputs[currentInputIndex].innerHTML;\n\t\n\t if (!targetInputIndex) {\n\t\n\t targetInput = editor.state.inputs[currentInputIndex - 1];\n\t } else {\n\t\n\t targetInput = editor.state.inputs[targetInputIndex];\n\t }\n\t\n\t targetInput.innerHTML += currentInputContent;\n\t };\n\t\n\t /**\n\t * Iterates all right siblings and parents, which has right siblings\n\t * while it does not reached the first-level block\n\t *\n\t * @param {Element} node\n\t * @return {boolean}\n\t */\n\t content.isLastNode = function (node) {\n\t\n\t // console.log('погнали перебор родителей');\n\t\n\t var allChecked = false;\n\t\n\t while (!allChecked) {\n\t\n\t // console.log('Смотрим на %o', node);\n\t // console.log('Проверим, пустые ли соседи справа');\n\t\n\t if (!allSiblingsEmpty_(node)) {\n\t\n\t // console.log('Есть непустые соседи. Узел не последний. Выходим.');\n\t return false;\n\t }\n\t\n\t node = node.parentNode;\n\t\n\t /**\n\t * Проверяем родителей до тех пор, пока не найдем блок первого уровня\n\t */\n\t if (node.classList.contains(editor.ui.className.BLOCK_CONTENT)) {\n\t\n\t allChecked = true;\n\t }\n\t }\n\t\n\t return true;\n\t };\n\t\n\t /**\n\t * Checks if all element right siblings is empty\n\t * @param node\n\t */\n\t var allSiblingsEmpty_ = function allSiblingsEmpty_(node) {\n\t\n\t /**\n\t * Нужно убедиться, что после пустого соседа ничего нет\n\t */\n\t var sibling = node.nextSibling;\n\t\n\t while (sibling) {\n\t\n\t if (sibling.textContent.length) {\n\t\n\t return false;\n\t }\n\t\n\t sibling = sibling.nextSibling;\n\t }\n\t\n\t return true;\n\t };\n\t\n\t /**\n\t * @public\n\t *\n\t * @param {string} htmlData - html content as string\n\t * @param {string} plainData - plain text\n\t * @return {string} - html content as string\n\t */\n\t content.wrapTextWithParagraphs = function (htmlData, plainData) {\n\t\n\t if (!htmlData.trim()) {\n\t\n\t return wrapPlainTextWithParagraphs(plainData);\n\t }\n\t\n\t var wrapper = document.createElement('DIV'),\n\t newWrapper = document.createElement('DIV'),\n\t i,\n\t paragraph,\n\t firstLevelBlocks = ['DIV', 'P'],\n\t blockTyped,\n\t node;\n\t\n\t /**\n\t * Make HTML Element to Wrap Text\n\t * It allows us to work with input data as HTML content\n\t */\n\t wrapper.innerHTML = htmlData;\n\t paragraph = document.createElement('P');\n\t\n\t for (i = 0; i < wrapper.childNodes.length; i++) {\n\t\n\t node = wrapper.childNodes[i];\n\t\n\t blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1;\n\t\n\t /**\n\t * If node is first-levet\n\t * we add this node to our new wrapper\n\t */\n\t if (blockTyped) {\n\t\n\t /**\n\t * If we had splitted inline nodes to paragraph before\n\t */\n\t if (paragraph.childNodes.length) {\n\t\n\t newWrapper.appendChild(paragraph.cloneNode(true));\n\t\n\t /** empty paragraph */\n\t paragraph = null;\n\t paragraph = document.createElement('P');\n\t }\n\t\n\t newWrapper.appendChild(node.cloneNode(true));\n\t } else {\n\t\n\t /** Collect all inline nodes to one as paragraph */\n\t paragraph.appendChild(node.cloneNode(true));\n\t\n\t /** if node is last we should append this node to paragraph and paragraph to new wrapper */\n\t if (i == wrapper.childNodes.length - 1) {\n\t\n\t newWrapper.appendChild(paragraph.cloneNode(true));\n\t }\n\t }\n\t }\n\t\n\t return newWrapper.innerHTML;\n\t };\n\t\n\t /**\n\t * Splits strings on new line and wraps paragraphs with

tag\n\t * @param plainText\n\t * @returns {string}\n\t */\n\t var wrapPlainTextWithParagraphs = function wrapPlainTextWithParagraphs(plainText) {\n\t\n\t if (!plainText) return '';\n\t\n\t return '

' + plainText.split('\\n\\n').join('

') + '

';\n\t };\n\t\n\t /**\n\t * Finds closest Contenteditable parent from Element\n\t * @param {Element} node element looking from\n\t * @return {Element} node contenteditable\n\t */\n\t content.getEditableParent = function (node) {\n\t\n\t while (node && node.contentEditable != 'true') {\n\t\n\t node = node.parentNode;\n\t }\n\t\n\t return node;\n\t };\n\t\n\t /**\n\t * Clear editors content\n\t *\n\t * @param {Boolean} all — if true, delete all article data (content, id, etc.)\n\t */\n\t content.clear = function (all) {\n\t\n\t editor.nodes.redactor.innerHTML = '';\n\t editor.content.sync();\n\t editor.ui.saveInputs();\n\t if (all) {\n\t\n\t editor.state.blocks = {};\n\t } else if (editor.state.blocks) {\n\t\n\t editor.state.blocks.items = [];\n\t }\n\t\n\t editor.content.currentNode = null;\n\t };\n\t\n\t /**\n\t *\n\t * Load new data to editor\n\t * If editor is not empty, just append articleData.items\n\t *\n\t * @param articleData.items\n\t */\n\t content.load = function (articleData) {\n\t\n\t var currentContent = Object.assign({}, editor.state.blocks);\n\t\n\t editor.content.clear();\n\t\n\t if (!Object.keys(currentContent).length) {\n\t\n\t editor.state.blocks = articleData;\n\t } else if (!currentContent.items) {\n\t\n\t currentContent.items = articleData.items;\n\t editor.state.blocks = currentContent;\n\t } else {\n\t\n\t currentContent.items = currentContent.items.concat(articleData.items);\n\t editor.state.blocks = currentContent;\n\t }\n\t\n\t editor.renderer.makeBlocksFromData();\n\t };\n\t\n\t return content;\n\t}({});\n\n/***/ }),\n/* 8 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor toolbar module\n\t *\n\t * Contains:\n\t * - Inline toolbox\n\t * - Toolbox within plus button\n\t * - Settings section\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (toolbar) {\n\t\n\t var editor = codex.editor;\n\t\n\t toolbar.settings = __webpack_require__(9);\n\t toolbar.inline = __webpack_require__(10);\n\t toolbar.toolbox = __webpack_require__(11);\n\t\n\t /**\n\t * Margin between focused node and toolbar\n\t */\n\t toolbar.defaultToolbarHeight = 49;\n\t\n\t toolbar.defaultOffset = 34;\n\t\n\t toolbar.opened = false;\n\t\n\t toolbar.current = null;\n\t\n\t /**\n\t * @protected\n\t */\n\t toolbar.open = function () {\n\t\n\t if (editor.hideToolbar) {\n\t\n\t return;\n\t }\n\t\n\t var toolType = editor.content.currentNode.dataset.tool;\n\t\n\t if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings) {\n\t\n\t editor.nodes.showSettingsButton.classList.add('hide');\n\t } else {\n\t\n\t editor.nodes.showSettingsButton.classList.remove('hide');\n\t }\n\t\n\t editor.nodes.toolbar.classList.add('opened');\n\t this.opened = true;\n\t };\n\t\n\t /**\n\t * @protected\n\t */\n\t toolbar.close = function () {\n\t\n\t editor.nodes.toolbar.classList.remove('opened');\n\t\n\t toolbar.opened = false;\n\t toolbar.current = null;\n\t\n\t for (var button in editor.nodes.toolbarButtons) {\n\t\n\t editor.nodes.toolbarButtons[button].classList.remove('selected');\n\t }\n\t\n\t /** Close toolbox when toolbar is not displayed */\n\t editor.toolbar.toolbox.close();\n\t editor.toolbar.settings.close();\n\t };\n\t\n\t toolbar.toggle = function () {\n\t\n\t if (!this.opened) {\n\t\n\t this.open();\n\t } else {\n\t\n\t this.close();\n\t }\n\t };\n\t\n\t toolbar.hidePlusButton = function () {\n\t\n\t editor.nodes.plusButton.classList.add('hide');\n\t };\n\t\n\t toolbar.showPlusButton = function () {\n\t\n\t editor.nodes.plusButton.classList.remove('hide');\n\t };\n\t\n\t /**\n\t * Moving toolbar to the specified node\n\t */\n\t toolbar.move = function () {\n\t\n\t /** Close Toolbox when we move toolbar */\n\t editor.toolbar.toolbox.close();\n\t\n\t if (!editor.content.currentNode) {\n\t\n\t return;\n\t }\n\t\n\t var newYCoordinate = editor.content.currentNode.offsetTop - editor.toolbar.defaultToolbarHeight / 2 + editor.toolbar.defaultOffset;\n\t\n\t editor.nodes.toolbar.style.transform = 'translate3D(0, ' + Math.floor(newYCoordinate) + 'px, 0)';\n\t\n\t /** Close trash actions */\n\t editor.toolbar.settings.hideRemoveActions();\n\t };\n\t\n\t return toolbar;\n\t}({});\n\n/***/ }),\n/* 9 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Toolbar settings\n\t *\n\t * @version 1.0.5\n\t */\n\t\n\tmodule.exports = function (settings) {\n\t\n\t var editor = codex.editor;\n\t\n\t settings.opened = false;\n\t\n\t settings.setting = null;\n\t settings.actions = null;\n\t\n\t /**\n\t * Append and open settings\n\t */\n\t settings.open = function (toolType) {\n\t\n\t /**\n\t * Append settings content\n\t * It's stored in tool.settings\n\t */\n\t if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings) {\n\t\n\t return;\n\t }\n\t\n\t /**\n\t * Draw settings block\n\t */\n\t var settingsBlock = editor.tools[toolType].makeSettings();\n\t\n\t editor.nodes.pluginSettings.appendChild(settingsBlock);\n\t\n\t /** Open settings block */\n\t editor.nodes.blockSettings.classList.add('opened');\n\t this.opened = true;\n\t };\n\t\n\t /**\n\t * Close and clear settings\n\t */\n\t settings.close = function () {\n\t\n\t editor.nodes.blockSettings.classList.remove('opened');\n\t editor.nodes.pluginSettings.innerHTML = '';\n\t\n\t this.opened = false;\n\t };\n\t\n\t /**\n\t * @param {string} toolType - plugin type\n\t */\n\t settings.toggle = function (toolType) {\n\t\n\t if (!this.opened) {\n\t\n\t this.open(toolType);\n\t } else {\n\t\n\t this.close();\n\t }\n\t };\n\t\n\t /**\n\t * Here we will draw buttons and add listeners to components\n\t */\n\t settings.makeRemoveBlockButton = function () {\n\t\n\t var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}),\n\t settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML: '' }),\n\t actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}),\n\t confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent: 'Удалить блок' }),\n\t cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent: 'Отмена' });\n\t\n\t editor.listeners.add(settingButton, 'click', editor.toolbar.settings.removeButtonClicked, false);\n\t\n\t editor.listeners.add(confirmAction, 'click', editor.toolbar.settings.confirmRemovingRequest, false);\n\t\n\t editor.listeners.add(cancelAction, 'click', editor.toolbar.settings.cancelRemovingRequest, false);\n\t\n\t actionWrapper.appendChild(confirmAction);\n\t actionWrapper.appendChild(cancelAction);\n\t\n\t removeBlockWrapper.appendChild(settingButton);\n\t removeBlockWrapper.appendChild(actionWrapper);\n\t\n\t /** Save setting */\n\t editor.toolbar.settings.setting = settingButton;\n\t editor.toolbar.settings.actions = actionWrapper;\n\t\n\t return removeBlockWrapper;\n\t };\n\t\n\t settings.removeButtonClicked = function () {\n\t\n\t var action = editor.toolbar.settings.actions;\n\t\n\t if (action.classList.contains('opened')) {\n\t\n\t editor.toolbar.settings.hideRemoveActions();\n\t } else {\n\t\n\t editor.toolbar.settings.showRemoveActions();\n\t }\n\t\n\t editor.toolbar.toolbox.close();\n\t editor.toolbar.settings.close();\n\t };\n\t\n\t settings.cancelRemovingRequest = function () {\n\t\n\t editor.toolbar.settings.actions.classList.remove('opened');\n\t };\n\t\n\t settings.confirmRemovingRequest = function () {\n\t\n\t var currentBlock = editor.content.currentNode,\n\t firstLevelBlocksCount;\n\t\n\t currentBlock.remove();\n\t\n\t firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\n\t\n\t /**\n\t * If all blocks are removed\n\t */\n\t if (firstLevelBlocksCount === 0) {\n\t\n\t /** update currentNode variable */\n\t editor.content.currentNode = null;\n\t\n\t /** Inserting new empty initial block */\n\t editor.ui.addInitialBlock();\n\t }\n\t\n\t editor.ui.saveInputs();\n\t\n\t editor.toolbar.close();\n\t };\n\t\n\t settings.showRemoveActions = function () {\n\t\n\t editor.toolbar.settings.actions.classList.add('opened');\n\t };\n\t\n\t settings.hideRemoveActions = function () {\n\t\n\t editor.toolbar.settings.actions.classList.remove('opened');\n\t };\n\t\n\t return settings;\n\t}({});\n\n/***/ }),\n/* 10 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Inline toolbar\n\t *\n\t * Contains from tools:\n\t * Bold, Italic, Underline and Anchor\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (inline) {\n\t\n\t var editor = codex.editor;\n\t\n\t inline.buttonsOpened = null;\n\t inline.actionsOpened = null;\n\t inline.wrappersOffset = null;\n\t\n\t /**\n\t * saving selection that need for execCommand for styling\n\t *\n\t */\n\t inline.storedSelection = null;\n\t\n\t /**\n\t * @protected\n\t *\n\t * Open inline toobar\n\t */\n\t inline.show = function () {\n\t\n\t var currentNode = editor.content.currentNode,\n\t tool = currentNode.dataset.tool,\n\t plugin;\n\t\n\t /**\n\t * tool allowed to open inline toolbar\n\t */\n\t plugin = editor.tools[tool];\n\t\n\t if (!plugin.showInlineToolbar) return;\n\t\n\t var selectedText = inline.getSelectionText(),\n\t toolbar = editor.nodes.inlineToolbar.wrapper;\n\t\n\t if (selectedText.length > 0) {\n\t\n\t /** Move toolbar and open */\n\t editor.toolbar.inline.move();\n\t\n\t /** Open inline toolbar */\n\t toolbar.classList.add('opened');\n\t\n\t /** show buttons of inline toolbar */\n\t editor.toolbar.inline.showButtons();\n\t }\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Closes inline toolbar\n\t */\n\t inline.close = function () {\n\t\n\t var toolbar = editor.nodes.inlineToolbar.wrapper;\n\t\n\t toolbar.classList.remove('opened');\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Moving toolbar\n\t */\n\t inline.move = function () {\n\t\n\t if (!this.wrappersOffset) {\n\t\n\t this.wrappersOffset = this.getWrappersOffset();\n\t }\n\t\n\t var coords = this.getSelectionCoords(),\n\t defaultOffset = 0,\n\t toolbar = editor.nodes.inlineToolbar.wrapper,\n\t newCoordinateX,\n\t newCoordinateY;\n\t\n\t if (!coords) {\n\t\n\t return;\n\t }\n\t\n\t if (toolbar.offsetHeight === 0) {\n\t\n\t defaultOffset = 40;\n\t }\n\t\n\t newCoordinateX = coords.x - this.wrappersOffset.left;\n\t newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight;\n\t\n\t toolbar.style.transform = 'translate3D(' + Math.floor(newCoordinateX) + 'px, ' + Math.floor(newCoordinateY) + 'px, 0)';\n\t\n\t /** Close everything */\n\t editor.toolbar.inline.closeButtons();\n\t editor.toolbar.inline.closeAction();\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Tool Clicked\n\t */\n\t\n\t inline.toolClicked = function (event, type) {\n\t\n\t /**\n\t * For simple tools we use default browser function\n\t * For more complicated tools, we should write our own behavior\n\t */\n\t switch (type) {\n\t case 'createLink':\n\t editor.toolbar.inline.createLinkAction(event, type);break;\n\t default:\n\t editor.toolbar.inline.defaultToolAction(type);break;\n\t }\n\t\n\t /**\n\t * highlight buttons\n\t * after making some action\n\t */\n\t editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Saving wrappers offset in DOM\n\t */\n\t inline.getWrappersOffset = function () {\n\t\n\t var wrapper = editor.nodes.wrapper,\n\t offset = this.getOffset(wrapper);\n\t\n\t this.wrappersOffset = offset;\n\t return offset;\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Calculates offset of DOM element\n\t *\n\t * @param el\n\t * @returns {{top: number, left: number}}\n\t */\n\t inline.getOffset = function (el) {\n\t\n\t var _x = 0;\n\t var _y = 0;\n\t\n\t while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {\n\t\n\t _x += el.offsetLeft + el.clientLeft;\n\t _y += el.offsetTop + el.clientTop;\n\t el = el.offsetParent;\n\t }\n\t return { top: _y, left: _x };\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Calculates position of selected text\n\t * @returns {{x: number, y: number}}\n\t */\n\t inline.getSelectionCoords = function () {\n\t\n\t var sel = document.selection,\n\t range;\n\t var x = 0,\n\t y = 0;\n\t\n\t if (sel) {\n\t\n\t if (sel.type != 'Control') {\n\t\n\t range = sel.createRange();\n\t range.collapse(true);\n\t x = range.boundingLeft;\n\t y = range.boundingTop;\n\t }\n\t } else if (window.getSelection) {\n\t\n\t sel = window.getSelection();\n\t\n\t if (sel.rangeCount) {\n\t\n\t range = sel.getRangeAt(0).cloneRange();\n\t if (range.getClientRects) {\n\t\n\t range.collapse(true);\n\t var rect = range.getClientRects()[0];\n\t\n\t if (!rect) {\n\t\n\t return;\n\t }\n\t\n\t x = rect.left;\n\t y = rect.top;\n\t }\n\t }\n\t }\n\t return { x: x, y: y };\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Returns selected text as String\n\t * @returns {string}\n\t */\n\t inline.getSelectionText = function () {\n\t\n\t var selectedText = '';\n\t\n\t // all modern browsers and IE9+\n\t if (window.getSelection) {\n\t\n\t selectedText = window.getSelection().toString();\n\t }\n\t\n\t return selectedText;\n\t };\n\t\n\t /** Opens buttons block */\n\t inline.showButtons = function () {\n\t\n\t var buttons = editor.nodes.inlineToolbar.buttons;\n\t\n\t buttons.classList.add('opened');\n\t\n\t editor.toolbar.inline.buttonsOpened = true;\n\t\n\t /** highlight buttons */\n\t editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\n\t };\n\t\n\t /** Makes buttons disappear */\n\t inline.closeButtons = function () {\n\t\n\t var buttons = editor.nodes.inlineToolbar.buttons;\n\t\n\t buttons.classList.remove('opened');\n\t\n\t editor.toolbar.inline.buttonsOpened = false;\n\t };\n\t\n\t /** Open buttons defined action if exist */\n\t inline.showActions = function () {\n\t\n\t var action = editor.nodes.inlineToolbar.actions;\n\t\n\t action.classList.add('opened');\n\t\n\t editor.toolbar.inline.actionsOpened = true;\n\t };\n\t\n\t /** Close actions block */\n\t inline.closeAction = function () {\n\t\n\t var action = editor.nodes.inlineToolbar.actions;\n\t\n\t action.innerHTML = '';\n\t action.classList.remove('opened');\n\t editor.toolbar.inline.actionsOpened = false;\n\t };\n\t\n\t /**\n\t * Callback for keydowns in inline toolbar \"Insert link...\" input\n\t */\n\t var inlineToolbarAnchorInputKeydown_ = function inlineToolbarAnchorInputKeydown_(event) {\n\t\n\t if (event.keyCode != editor.core.keys.ENTER) {\n\t\n\t return;\n\t }\n\t\n\t var editable = editor.content.currentNode,\n\t storedSelection = editor.toolbar.inline.storedSelection;\n\t\n\t editor.toolbar.inline.restoreSelection(editable, storedSelection);\n\t editor.toolbar.inline.setAnchor(this.value);\n\t\n\t /**\n\t * Preventing events that will be able to happen\n\t */\n\t event.preventDefault();\n\t event.stopImmediatePropagation();\n\t\n\t editor.toolbar.inline.clearRange();\n\t };\n\t\n\t /** Action for link creation or for setting anchor */\n\t inline.createLinkAction = function (event) {\n\t\n\t var isActive = this.isLinkActive();\n\t\n\t var editable = editor.content.currentNode,\n\t storedSelection = editor.toolbar.inline.saveSelection(editable);\n\t\n\t /** Save globally selection */\n\t editor.toolbar.inline.storedSelection = storedSelection;\n\t\n\t if (isActive) {\n\t\n\t /**\n\t * Changing stored selection. if we want to remove anchor from word\n\t * we should remove anchor from whole word, not only selected part.\n\t * The solution is than we get the length of current link\n\t * Change start position to - end of selection minus length of anchor\n\t */\n\t editor.toolbar.inline.restoreSelection(editable, storedSelection);\n\t\n\t editor.toolbar.inline.defaultToolAction('unlink');\n\t } else {\n\t\n\t /** Create input and close buttons */\n\t var action = editor.draw.inputForLink();\n\t\n\t editor.nodes.inlineToolbar.actions.appendChild(action);\n\t\n\t editor.toolbar.inline.closeButtons();\n\t editor.toolbar.inline.showActions();\n\t\n\t /**\n\t * focus to input\n\t * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus\n\t * Prevents event after showing input and when we need to focus an input which is in unexisted form\n\t */\n\t action.focus();\n\t event.preventDefault();\n\t\n\t /** Callback to link action */\n\t editor.listeners.add(action, 'keydown', inlineToolbarAnchorInputKeydown_, false);\n\t }\n\t };\n\t\n\t inline.isLinkActive = function () {\n\t\n\t var isActive = false;\n\t\n\t editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) {\n\t\n\t var dataType = tool.dataset.type;\n\t\n\t if (dataType == 'link' && tool.classList.contains('hightlighted')) {\n\t\n\t isActive = true;\n\t }\n\t });\n\t\n\t return isActive;\n\t };\n\t\n\t /** default action behavior of tool */\n\t inline.defaultToolAction = function (type) {\n\t\n\t document.execCommand(type, false, null);\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Sets URL\n\t *\n\t * @param {String} url - URL\n\t */\n\t inline.setAnchor = function (url) {\n\t\n\t document.execCommand('createLink', false, url);\n\t\n\t /** Close after URL inserting */\n\t editor.toolbar.inline.closeAction();\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Saves selection\n\t */\n\t inline.saveSelection = function (containerEl) {\n\t\n\t var range = window.getSelection().getRangeAt(0),\n\t preSelectionRange = range.cloneRange(),\n\t start;\n\t\n\t preSelectionRange.selectNodeContents(containerEl);\n\t preSelectionRange.setEnd(range.startContainer, range.startOffset);\n\t\n\t start = preSelectionRange.toString().length;\n\t\n\t return {\n\t start: start,\n\t end: start + range.toString().length\n\t };\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Sets to previous selection (Range)\n\t *\n\t * @param {Element} containerEl - editable element where we restore range\n\t * @param {Object} savedSel - range basic information to restore\n\t */\n\t inline.restoreSelection = function (containerEl, savedSel) {\n\t\n\t var range = document.createRange(),\n\t charIndex = 0;\n\t\n\t range.setStart(containerEl, 0);\n\t range.collapse(true);\n\t\n\t var nodeStack = [containerEl],\n\t node,\n\t foundStart = false,\n\t stop = false,\n\t nextCharIndex;\n\t\n\t while (!stop && (node = nodeStack.pop())) {\n\t\n\t if (node.nodeType == 3) {\n\t\n\t nextCharIndex = charIndex + node.length;\n\t\n\t if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {\n\t\n\t range.setStart(node, savedSel.start - charIndex);\n\t foundStart = true;\n\t }\n\t if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {\n\t\n\t range.setEnd(node, savedSel.end - charIndex);\n\t stop = true;\n\t }\n\t charIndex = nextCharIndex;\n\t } else {\n\t\n\t var i = node.childNodes.length;\n\t\n\t while (i--) {\n\t\n\t nodeStack.push(node.childNodes[i]);\n\t }\n\t }\n\t }\n\t\n\t var sel = window.getSelection();\n\t\n\t sel.removeAllRanges();\n\t sel.addRange(range);\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Removes all ranges from window selection\n\t */\n\t inline.clearRange = function () {\n\t\n\t var selection = window.getSelection();\n\t\n\t selection.removeAllRanges();\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * sets or removes hightlight\n\t */\n\t inline.hightlight = function (tool) {\n\t\n\t var dataType = tool.dataset.type;\n\t\n\t if (document.queryCommandState(dataType)) {\n\t\n\t editor.toolbar.inline.setButtonHighlighted(tool);\n\t } else {\n\t\n\t editor.toolbar.inline.removeButtonsHighLight(tool);\n\t }\n\t\n\t /**\n\t *\n\t * hightlight for anchors\n\t */\n\t var selection = window.getSelection(),\n\t tag = selection.anchorNode.parentNode;\n\t\n\t if (tag.tagName == 'A' && dataType == 'link') {\n\t\n\t editor.toolbar.inline.setButtonHighlighted(tool);\n\t }\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Mark button if text is already executed\n\t */\n\t inline.setButtonHighlighted = function (button) {\n\t\n\t button.classList.add('hightlighted');\n\t\n\t /** At link tool we also change icon */\n\t if (button.dataset.type == 'link') {\n\t\n\t var icon = button.childNodes[0];\n\t\n\t icon.classList.remove('ce-icon-link');\n\t icon.classList.add('ce-icon-unlink');\n\t }\n\t };\n\t\n\t /**\n\t * @private\n\t *\n\t * Removes hightlight\n\t */\n\t inline.removeButtonsHighLight = function (button) {\n\t\n\t button.classList.remove('hightlighted');\n\t\n\t /** At link tool we also change icon */\n\t if (button.dataset.type == 'link') {\n\t\n\t var icon = button.childNodes[0];\n\t\n\t icon.classList.remove('ce-icon-unlink');\n\t icon.classList.add('ce-icon-link');\n\t }\n\t };\n\t\n\t return inline;\n\t}({});\n\n/***/ }),\n/* 11 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor toolbox\n\t *\n\t * All tools be able to appended here\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (toolbox) {\n\t\n\t var editor = codex.editor;\n\t\n\t toolbox.opened = false;\n\t toolbox.openedOnBlock = null;\n\t\n\t /** Shows toolbox */\n\t toolbox.open = function () {\n\t\n\t /** Close setting if toolbox is opened */\n\t if (editor.toolbar.settings.opened) {\n\t\n\t editor.toolbar.settings.close();\n\t }\n\t\n\t /** Add 'toolbar-opened' class for current block **/\n\t toolbox.openedOnBlock = editor.content.currentNode;\n\t toolbox.openedOnBlock.classList.add('toolbar-opened');\n\t\n\t /** display toolbox */\n\t editor.nodes.toolbox.classList.add('opened');\n\t\n\t /** Animate plus button */\n\t editor.nodes.plusButton.classList.add('clicked');\n\t\n\t /** toolbox state */\n\t editor.toolbar.toolbox.opened = true;\n\t };\n\t\n\t /** Closes toolbox */\n\t toolbox.close = function () {\n\t\n\t /** Remove 'toolbar-opened' class from current block **/\n\t if (toolbox.openedOnBlock) toolbox.openedOnBlock.classList.remove('toolbar-opened');\n\t toolbox.openedOnBlock = null;\n\t\n\t /** Makes toolbox disappear */\n\t editor.nodes.toolbox.classList.remove('opened');\n\t\n\t /** Rotate plus button */\n\t editor.nodes.plusButton.classList.remove('clicked');\n\t\n\t /** toolbox state */\n\t editor.toolbar.toolbox.opened = false;\n\t\n\t editor.toolbar.current = null;\n\t };\n\t\n\t toolbox.leaf = function () {\n\t\n\t var currentTool = editor.toolbar.current,\n\t tools = Object.keys(editor.tools),\n\t barButtons = editor.nodes.toolbarButtons,\n\t nextToolIndex = 0,\n\t toolToSelect = void 0,\n\t visibleTool = void 0,\n\t tool = void 0;\n\t\n\t if (!currentTool) {\n\t\n\t /** Get first tool from object*/\n\t for (tool in editor.tools) {\n\t\n\t if (editor.tools[tool].displayInToolbox) {\n\t\n\t break;\n\t }\n\t\n\t nextToolIndex++;\n\t }\n\t } else {\n\t\n\t nextToolIndex = (tools.indexOf(currentTool) + 1) % tools.length;\n\t visibleTool = tools[nextToolIndex];\n\t\n\t while (!editor.tools[visibleTool].displayInToolbox) {\n\t\n\t nextToolIndex = (nextToolIndex + 1) % tools.length;\n\t visibleTool = tools[nextToolIndex];\n\t }\n\t }\n\t\n\t toolToSelect = tools[nextToolIndex];\n\t\n\t for (var button in barButtons) {\n\t\n\t barButtons[button].classList.remove('selected');\n\t }\n\t\n\t barButtons[toolToSelect].classList.add('selected');\n\t editor.toolbar.current = toolToSelect;\n\t };\n\t\n\t /**\n\t * Transforming selected node type into selected toolbar element type\n\t * @param {event} event\n\t */\n\t toolbox.toolClicked = function (event) {\n\t\n\t /**\n\t * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty\n\t */\n\t var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'],\n\t tool = editor.tools[editor.toolbar.current],\n\t workingNode = editor.content.currentNode,\n\t currentInputIndex = editor.caret.inputIndex,\n\t newBlockContent,\n\t appendCallback,\n\t blockData;\n\t\n\t /** Make block from plugin */\n\t newBlockContent = tool.render();\n\t\n\t /** information about block */\n\t blockData = {\n\t block: newBlockContent,\n\t type: tool.type,\n\t stretched: false\n\t };\n\t\n\t if (workingNode && UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 && workingNode.textContent.trim() === '') {\n\t\n\t /** Replace current block */\n\t editor.content.switchBlock(workingNode, newBlockContent, tool.type);\n\t } else {\n\t\n\t /** Insert new Block from plugin */\n\t editor.content.insertBlock(blockData);\n\t\n\t /** increase input index */\n\t currentInputIndex++;\n\t }\n\t\n\t /** Fire tool append callback */\n\t appendCallback = tool.appendCallback;\n\t\n\t if (appendCallback && typeof appendCallback == 'function') {\n\t\n\t appendCallback.call(event);\n\t }\n\t\n\t window.setTimeout(function () {\n\t\n\t /** Set caret to current block */\n\t editor.caret.setToBlock(currentInputIndex);\n\t }, 10);\n\t\n\t /**\n\t * Changing current Node\n\t */\n\t editor.content.workingNodeChanged();\n\t\n\t /**\n\t * Move toolbar when node is changed\n\t */\n\t editor.toolbar.move();\n\t };\n\t\n\t return toolbox;\n\t}({});\n\n/***/ }),\n/* 12 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * @module Codex Editor Callbacks module\n\t * @description Module works with editor added Elements\n\t *\n\t * @author Codex Team\n\t * @version 1.4.0\n\t */\n\t\n\tmodule.exports = function (callbacks) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * used by UI module\n\t * @description Routes all keydowns on document\n\t * @param {Object} event\n\t */\n\t callbacks.globalKeydown = function (event) {\n\t\n\t switch (event.keyCode) {\n\t case editor.core.keys.ENTER:\n\t enterKeyPressed_(event);break;\n\t }\n\t };\n\t\n\t /**\n\t * used by UI module\n\t * @description Routes all keydowns on redactors area\n\t * @param {Object} event\n\t */\n\t callbacks.redactorKeyDown = function (event) {\n\t\n\t switch (event.keyCode) {\n\t case editor.core.keys.TAB:\n\t tabKeyPressedOnRedactorsZone_(event);break;\n\t case editor.core.keys.ENTER:\n\t enterKeyPressedOnRedactorsZone_(event);break;\n\t case editor.core.keys.ESC:\n\t escapeKeyPressedOnRedactorsZone_(event);break;\n\t default:\n\t defaultKeyPressedOnRedactorsZone_(event);break;\n\t }\n\t };\n\t\n\t /**\n\t * used by UI module\n\t * @description Routes all keyup events\n\t * @param {Object} event\n\t */\n\t callbacks.globalKeyup = function (event) {\n\t\n\t switch (event.keyCode) {\n\t case editor.core.keys.UP:\n\t case editor.core.keys.LEFT:\n\t case editor.core.keys.RIGHT:\n\t case editor.core.keys.DOWN:\n\t arrowKeyPressed_(event);break;\n\t }\n\t };\n\t\n\t /**\n\t * @param {Object} event\n\t * @private\n\t *\n\t * Handles behaviour when tab pressed\n\t * @description if Content is empty show toolbox (if it is closed) or leaf tools\n\t * uses Toolbars toolbox module to handle the situation\n\t */\n\t var tabKeyPressedOnRedactorsZone_ = function tabKeyPressedOnRedactorsZone_(event) {\n\t\n\t /**\n\t * Wait for solution. Would like to know the behaviour\n\t * @todo Add spaces\n\t */\n\t event.preventDefault();\n\t\n\t if (!editor.core.isBlockEmpty(editor.content.currentNode)) {\n\t\n\t return;\n\t }\n\t\n\t if (!editor.toolbar.opened) {\n\t\n\t editor.toolbar.open();\n\t }\n\t\n\t if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) {\n\t\n\t editor.toolbar.toolbox.open();\n\t } else {\n\t\n\t editor.toolbar.toolbox.leaf();\n\t }\n\t };\n\t\n\t /**\n\t * Handles global EnterKey Press\n\t * @see enterPressedOnBlock_\n\t * @param {Object} event\n\t */\n\t var enterKeyPressed_ = function enterKeyPressed_() {\n\t\n\t if (editor.content.editorAreaHightlighted) {\n\t\n\t /**\n\t * it means that we lose input index, saved index before is not correct\n\t * therefore we need to set caret when we insert new block\n\t */\n\t editor.caret.inputIndex = -1;\n\t\n\t enterPressedOnBlock_();\n\t }\n\t };\n\t\n\t /**\n\t * Callback for enter key pressing in first-level block area\n\t *\n\t * @param {Event} event\n\t * @private\n\t *\n\t * @description Inserts new block with initial type from settings\n\t */\n\t var enterPressedOnBlock_ = function enterPressedOnBlock_() {\n\t\n\t var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\t\n\t editor.content.insertBlock({\n\t type: NEW_BLOCK_TYPE,\n\t block: editor.tools[NEW_BLOCK_TYPE].render()\n\t }, true);\n\t\n\t editor.toolbar.move();\n\t editor.toolbar.open();\n\t };\n\t\n\t /**\n\t * ENTER key handler\n\t *\n\t * @param {Object} event\n\t * @private\n\t *\n\t * @description Makes new block with initial type from settings\n\t */\n\t var enterKeyPressedOnRedactorsZone_ = function enterKeyPressedOnRedactorsZone_(event) {\n\t\n\t if (event.target.contentEditable == 'true') {\n\t\n\t /** Update input index */\n\t editor.caret.saveCurrentInputIndex();\n\t }\n\t\n\t var currentInputIndex = editor.caret.getCurrentInputIndex() || 0,\n\t workingNode = editor.content.currentNode,\n\t tool = workingNode.dataset.tool,\n\t isEnterPressedOnToolbar = editor.toolbar.opened && editor.toolbar.current && event.target == editor.state.inputs[currentInputIndex];\n\t\n\t /** The list of tools which needs the default browser behaviour */\n\t var enableLineBreaks = editor.tools[tool].enableLineBreaks;\n\t\n\t /** This type of block creates when enter is pressed */\n\t var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\t\n\t /**\n\t * When toolbar is opened, select tool instead of making new paragraph\n\t */\n\t if (isEnterPressedOnToolbar) {\n\t\n\t event.preventDefault();\n\t\n\t editor.toolbar.toolbox.toolClicked(event);\n\t\n\t editor.toolbar.close();\n\t\n\t /**\n\t * Stop other listeners callback executions\n\t */\n\t event.stopPropagation();\n\t event.stopImmediatePropagation();\n\t\n\t return;\n\t }\n\t\n\t /**\n\t * Allow paragraph lineBreaks with shift enter\n\t * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation\n\t */\n\t if (event.shiftKey || enableLineBreaks) {\n\t\n\t event.stopPropagation();\n\t event.stopImmediatePropagation();\n\t return;\n\t }\n\t\n\t var currentSelection = window.getSelection(),\n\t currentSelectedNode = currentSelection.anchorNode,\n\t caretAtTheEndOfText = editor.caret.position.atTheEnd(),\n\t isTextNodeHasParentBetweenContenteditable = false;\n\t\n\t /**\n\t * Allow making new

in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\n\t */\n\t if (event.shiftKey && !enableLineBreaks) {\n\t\n\t editor.callback.enterPressedOnBlock(editor.content.currentBlock, event);\n\t event.preventDefault();\n\t return;\n\t }\n\t\n\t /**\n\t * Workaround situation when caret at the Text node that has some wrapper Elements\n\t * Split block cant handle this.\n\t * We need to save default behavior\n\t */\n\t isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true';\n\t\n\t /**\n\t * Split blocks when input has several nodes and caret placed in textNode\n\t */\n\t if (currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT && !isTextNodeHasParentBetweenContenteditable && !caretAtTheEndOfText) {\n\t\n\t event.preventDefault();\n\t\n\t editor.core.log('Splitting Text node...');\n\t\n\t editor.content.splitBlock(currentInputIndex);\n\t\n\t /** Show plus button when next input after split is empty*/\n\t if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) {\n\t\n\t editor.toolbar.showPlusButton();\n\t }\n\t } else {\n\t\n\t var islastNode = editor.content.isLastNode(currentSelectedNode);\n\t\n\t if (islastNode && caretAtTheEndOfText) {\n\t\n\t event.preventDefault();\n\t event.stopPropagation();\n\t event.stopImmediatePropagation();\n\t\n\t editor.core.log('ENTER clicked in last textNode. Create new BLOCK');\n\t\n\t editor.content.insertBlock({\n\t type: NEW_BLOCK_TYPE,\n\t block: editor.tools[NEW_BLOCK_TYPE].render()\n\t }, true);\n\t\n\t editor.toolbar.move();\n\t editor.toolbar.open();\n\t\n\t /** Show plus button with empty block */\n\t editor.toolbar.showPlusButton();\n\t }\n\t }\n\t\n\t /** get all inputs after new appending block */\n\t editor.ui.saveInputs();\n\t };\n\t\n\t /**\n\t * Escape behaviour\n\t * @param event\n\t * @private\n\t *\n\t * @description Closes toolbox and toolbar. Prevents default behaviour\n\t */\n\t var escapeKeyPressedOnRedactorsZone_ = function escapeKeyPressedOnRedactorsZone_(event) {\n\t\n\t /** Close all toolbar */\n\t editor.toolbar.close();\n\t\n\t /** Close toolbox */\n\t editor.toolbar.toolbox.close();\n\t\n\t event.preventDefault();\n\t };\n\t\n\t /**\n\t * @param {Event} event\n\t * @private\n\t *\n\t * closes and moves toolbar\n\t */\n\t var arrowKeyPressed_ = function arrowKeyPressed_(event) {\n\t\n\t editor.content.workingNodeChanged();\n\t\n\t /* Closing toolbar */\n\t editor.toolbar.close();\n\t editor.toolbar.move();\n\t };\n\t\n\t /**\n\t * @private\n\t * @param {Event} event\n\t *\n\t * @description Closes all opened bars from toolbar.\n\t * If block is mark, clears highlightning\n\t */\n\t var defaultKeyPressedOnRedactorsZone_ = function defaultKeyPressedOnRedactorsZone_() {\n\t\n\t editor.toolbar.close();\n\t\n\t if (!editor.toolbar.inline.actionsOpened) {\n\t\n\t editor.toolbar.inline.close();\n\t editor.content.clearMark();\n\t }\n\t };\n\t\n\t /**\n\t * Handler when clicked on redactors area\n\t *\n\t * @protected\n\t * @param event\n\t *\n\t * @description Detects clicked area. If it is first-level block area, marks as detected and\n\t * on next enter press will be inserted new block\n\t * Otherwise, save carets position (input index) and put caret to the editable zone.\n\t *\n\t * @see detectWhenClickedOnFirstLevelBlockArea_\n\t *\n\t */\n\t callbacks.redactorClicked = function (event) {\n\t\n\t detectWhenClickedOnFirstLevelBlockArea_();\n\t\n\t editor.content.workingNodeChanged(event.target);\n\t editor.ui.saveInputs();\n\t\n\t var selectedText = editor.toolbar.inline.getSelectionText(),\n\t firstLevelBlock;\n\t\n\t /** If selection range took off, then we hide inline toolbar */\n\t if (selectedText.length === 0) {\n\t\n\t editor.toolbar.inline.close();\n\t }\n\t\n\t /** Update current input index in memory when caret focused into existed input */\n\t if (event.target.contentEditable == 'true') {\n\t\n\t editor.caret.saveCurrentInputIndex();\n\t }\n\t\n\t if (editor.content.currentNode === null) {\n\t\n\t /**\n\t * If inputs in redactor does not exits, then we put input index 0 not -1\n\t */\n\t var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0;\n\t\n\t /** If we have any inputs */\n\t if (editor.state.inputs.length) {\n\t\n\t /** getting firstlevel parent of input */\n\t firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);\n\t }\n\t\n\t /** If input is empty, then we set caret to the last input */\n\t if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) {\n\t\n\t editor.caret.setToBlock(indexOfLastInput);\n\t } else {\n\t\n\t /** Create new input when caret clicked in redactors area */\n\t var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\t\n\t editor.content.insertBlock({\n\t type: NEW_BLOCK_TYPE,\n\t block: editor.tools[NEW_BLOCK_TYPE].render()\n\t });\n\t\n\t /** If there is no inputs except inserted */\n\t if (editor.state.inputs.length === 1) {\n\t\n\t editor.caret.setToBlock(indexOfLastInput);\n\t } else {\n\t\n\t /** Set caret to this appended input */\n\t editor.caret.setToNextBlock(indexOfLastInput);\n\t }\n\t }\n\t } else {\n\t\n\t /** Close all panels */\n\t editor.toolbar.settings.close();\n\t editor.toolbar.toolbox.close();\n\t }\n\t\n\t /**\n\t * Move toolbar and open\n\t */\n\t editor.toolbar.move();\n\t editor.toolbar.open();\n\t\n\t var inputIsEmpty = !editor.content.currentNode.textContent.trim(),\n\t currentNodeType = editor.content.currentNode.dataset.tool,\n\t isInitialType = currentNodeType == editor.settings.initialBlockPlugin;\n\t\n\t /** Hide plus buttons */\n\t editor.toolbar.hidePlusButton();\n\t\n\t if (!inputIsEmpty) {\n\t\n\t /** Mark current block */\n\t editor.content.markBlock();\n\t }\n\t\n\t if (isInitialType && inputIsEmpty) {\n\t\n\t /** Show plus button */\n\t editor.toolbar.showPlusButton();\n\t }\n\t };\n\t\n\t /**\n\t * This method allows to define, is caret in contenteditable element or not.\n\t *\n\t * @private\n\t *\n\t * @description Otherwise, if we get TEXT node from range container, that will means we have input index.\n\t * In this case we use default browsers behaviour (if plugin allows that) or overwritten action.\n\t * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always\n\t * specifies to the first-level block. Other cases we just ignore.\n\t */\n\t var detectWhenClickedOnFirstLevelBlockArea_ = function detectWhenClickedOnFirstLevelBlockArea_() {\n\t\n\t var selection = window.getSelection(),\n\t anchorNode = selection.anchorNode,\n\t flag = false;\n\t\n\t if (selection.rangeCount === 0) {\n\t\n\t editor.content.editorAreaHightlighted = true;\n\t } else {\n\t\n\t if (!editor.core.isDomNode(anchorNode)) {\n\t\n\t anchorNode = anchorNode.parentNode;\n\t }\n\t\n\t /** Already founded, without loop */\n\t if (anchorNode.contentEditable == 'true') {\n\t\n\t flag = true;\n\t }\n\t\n\t while (anchorNode.contentEditable != 'true') {\n\t\n\t anchorNode = anchorNode.parentNode;\n\t\n\t if (anchorNode.contentEditable == 'true') {\n\t\n\t flag = true;\n\t }\n\t\n\t if (anchorNode == document.body) {\n\t\n\t break;\n\t }\n\t }\n\t\n\t /** If editable element founded, flag is \"TRUE\", Therefore we return \"FALSE\" */\n\t editor.content.editorAreaHightlighted = !flag;\n\t }\n\t };\n\t\n\t /**\n\t * Toolbar button click handler\n\t *\n\t * @param {Object} event - cursor to the button\n\t * @protected\n\t *\n\t * @description gets current tool and calls render method\n\t */\n\t callbacks.toolbarButtonClicked = function (event) {\n\t\n\t var button = this;\n\t\n\t editor.toolbar.current = button.dataset.type;\n\t\n\t editor.toolbar.toolbox.toolClicked(event);\n\t editor.toolbar.close();\n\t };\n\t\n\t /**\n\t * Show or Hide toolbox when plus button is clicked\n\t */\n\t callbacks.plusButtonClicked = function () {\n\t\n\t if (!editor.nodes.toolbox.classList.contains('opened')) {\n\t\n\t editor.toolbar.toolbox.open();\n\t } else {\n\t\n\t editor.toolbar.toolbox.close();\n\t }\n\t };\n\t\n\t /**\n\t * Block handlers for KeyDown events\n\t *\n\t * @protected\n\t * @param {Object} event\n\t *\n\t * Handles keydowns on block\n\t * @see blockRightOrDownArrowPressed_\n\t * @see backspacePressed_\n\t * @see blockLeftOrUpArrowPressed_\n\t */\n\t callbacks.blockKeydown = function (event) {\n\t\n\t var block = event.target; // event.target is input\n\t\n\t switch (event.keyCode) {\n\t\n\t case editor.core.keys.DOWN:\n\t case editor.core.keys.RIGHT:\n\t blockRightOrDownArrowPressed_(event);\n\t break;\n\t\n\t case editor.core.keys.BACKSPACE:\n\t backspacePressed_(block, event);\n\t break;\n\t\n\t case editor.core.keys.UP:\n\t case editor.core.keys.LEFT:\n\t blockLeftOrUpArrowPressed_(event);\n\t break;\n\t\n\t }\n\t };\n\t\n\t /**\n\t * RIGHT or DOWN keydowns on block\n\t *\n\t * @param {Object} event\n\t * @private\n\t *\n\t * @description watches the selection and gets closest editable element.\n\t * Uses method getDeepestTextNodeFromPosition to get the last node of next block\n\t * Sets caret if it is contenteditable\n\t */\n\t var blockRightOrDownArrowPressed_ = function blockRightOrDownArrowPressed_(event) {\n\t\n\t var selection = window.getSelection(),\n\t inputs = editor.state.inputs,\n\t focusedNode = selection.anchorNode,\n\t focusedNodeHolder;\n\t\n\t /** Check for caret existance */\n\t if (!focusedNode) {\n\t\n\t return false;\n\t }\n\t\n\t /** Looking for closest (parent) contentEditable element of focused node */\n\t while (focusedNode.contentEditable != 'true') {\n\t\n\t focusedNodeHolder = focusedNode.parentNode;\n\t focusedNode = focusedNodeHolder;\n\t }\n\t\n\t /** Input index in DOM level */\n\t var editableElementIndex = 0;\n\t\n\t while (focusedNode != inputs[editableElementIndex]) {\n\t\n\t editableElementIndex++;\n\t }\n\t\n\t /**\n\t * Founded contentEditable element doesn't have childs\n\t * Or maybe New created block\n\t */\n\t if (!focusedNode.textContent) {\n\t\n\t editor.caret.setToNextBlock(editableElementIndex);\n\t return;\n\t }\n\t\n\t /**\n\t * Do nothing when caret doesn not reaches the end of last child\n\t */\n\t var caretInLastChild = false,\n\t caretAtTheEndOfText = false;\n\t\n\t var lastChild, deepestTextnode;\n\t\n\t lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1];\n\t\n\t if (editor.core.isDomNode(lastChild)) {\n\t\n\t deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);\n\t } else {\n\t\n\t deepestTextnode = lastChild;\n\t }\n\t\n\t caretInLastChild = selection.anchorNode == deepestTextnode;\n\t caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;\n\t\n\t if (!caretInLastChild || !caretAtTheEndOfText) {\n\t\n\t editor.core.log('arrow [down|right] : caret does not reached the end');\n\t return false;\n\t }\n\t\n\t editor.caret.setToNextBlock(editableElementIndex);\n\t };\n\t\n\t /**\n\t * LEFT or UP keydowns on block\n\t *\n\t * @param {Object} event\n\t * @private\n\t *\n\t * watches the selection and gets closest editable element.\n\t * Uses method getDeepestTextNodeFromPosition to get the last node of previous block\n\t * Sets caret if it is contenteditable\n\t *\n\t */\n\t var blockLeftOrUpArrowPressed_ = function blockLeftOrUpArrowPressed_(event) {\n\t\n\t var selection = window.getSelection(),\n\t inputs = editor.state.inputs,\n\t focusedNode = selection.anchorNode,\n\t focusedNodeHolder;\n\t\n\t /** Check for caret existance */\n\t if (!focusedNode) {\n\t\n\t return false;\n\t }\n\t\n\t /**\n\t * LEFT or UP not at the beginning\n\t */\n\t if (selection.anchorOffset !== 0) {\n\t\n\t return false;\n\t }\n\t\n\t /** Looking for parent contentEditable block */\n\t while (focusedNode.contentEditable != 'true') {\n\t\n\t focusedNodeHolder = focusedNode.parentNode;\n\t focusedNode = focusedNodeHolder;\n\t }\n\t\n\t /** Input index in DOM level */\n\t var editableElementIndex = 0;\n\t\n\t while (focusedNode != inputs[editableElementIndex]) {\n\t\n\t editableElementIndex++;\n\t }\n\t\n\t /**\n\t * Do nothing if caret is not at the beginning of first child\n\t */\n\t var caretInFirstChild = false,\n\t caretAtTheBeginning = false;\n\t\n\t var firstChild, deepestTextnode;\n\t\n\t /**\n\t * Founded contentEditable element doesn't have childs\n\t * Or maybe New created block\n\t */\n\t if (!focusedNode.textContent) {\n\t\n\t editor.caret.setToPreviousBlock(editableElementIndex);\n\t return;\n\t }\n\t\n\t firstChild = focusedNode.childNodes[0];\n\t\n\t if (editor.core.isDomNode(firstChild)) {\n\t\n\t deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);\n\t } else {\n\t\n\t deepestTextnode = firstChild;\n\t }\n\t\n\t caretInFirstChild = selection.anchorNode == deepestTextnode;\n\t caretAtTheBeginning = selection.anchorOffset === 0;\n\t\n\t if (caretInFirstChild && caretAtTheBeginning) {\n\t\n\t editor.caret.setToPreviousBlock(editableElementIndex);\n\t }\n\t };\n\t\n\t /**\n\t * Handles backspace keydown\n\t *\n\t * @param {Element} block\n\t * @param {Object} event\n\t * @private\n\t *\n\t * @description if block is empty, delete the block and set caret to the previous block\n\t * If block is not empty, try to merge two blocks - current and previous\n\t * But it we try'n to remove first block, then we should set caret to the next block, not previous.\n\t * If we removed the last block, create new one\n\t */\n\t var backspacePressed_ = function backspacePressed_(block, event) {\n\t\n\t var currentInputIndex = editor.caret.getCurrentInputIndex(),\n\t range,\n\t selectionLength,\n\t firstLevelBlocksCount;\n\t\n\t if (editor.core.isNativeInput(event.target)) {\n\t\n\t /** If input value is empty - remove block */\n\t if (event.target.value.trim() == '') {\n\t\n\t block.remove();\n\t } else {\n\t\n\t return;\n\t }\n\t }\n\t\n\t if (block.textContent.trim()) {\n\t\n\t range = editor.content.getRange();\n\t selectionLength = range.endOffset - range.startOffset;\n\t\n\t if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) {\n\t\n\t editor.content.mergeBlocks(currentInputIndex);\n\t } else {\n\t\n\t return;\n\t }\n\t }\n\t\n\t if (!selectionLength) {\n\t\n\t block.remove();\n\t }\n\t\n\t firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\n\t\n\t /**\n\t * If all blocks are removed\n\t */\n\t if (firstLevelBlocksCount === 0) {\n\t\n\t /** update currentNode variable */\n\t editor.content.currentNode = null;\n\t\n\t /** Inserting new empty initial block */\n\t editor.ui.addInitialBlock();\n\t\n\t /** Updating inputs state after deleting last block */\n\t editor.ui.saveInputs();\n\t\n\t /** Set to current appended block */\n\t window.setTimeout(function () {\n\t\n\t editor.caret.setToPreviousBlock(1);\n\t }, 10);\n\t } else {\n\t\n\t if (editor.caret.inputIndex !== 0) {\n\t\n\t /** Target block is not first */\n\t editor.caret.setToPreviousBlock(editor.caret.inputIndex);\n\t } else {\n\t\n\t /** If we try to delete first block */\n\t editor.caret.setToNextBlock(editor.caret.inputIndex);\n\t }\n\t }\n\t\n\t editor.toolbar.move();\n\t\n\t if (!editor.toolbar.opened) {\n\t\n\t editor.toolbar.open();\n\t }\n\t\n\t /** Updating inputs state */\n\t editor.ui.saveInputs();\n\t\n\t /** Prevent default browser behaviour */\n\t event.preventDefault();\n\t };\n\t\n\t /**\n\t * used by UI module\n\t * Clicks on block settings button\n\t *\n\t * @param {Object} event\n\t * @protected\n\t * @description Opens toolbar settings\n\t */\n\t callbacks.showSettingsButtonClicked = function (event) {\n\t\n\t /**\n\t * Get type of current block\n\t * It uses to append settings from tool.settings property.\n\t * ...\n\t * Type is stored in data-type attribute on block\n\t */\n\t var currentToolType = editor.content.currentNode.dataset.tool;\n\t\n\t editor.toolbar.settings.toggle(currentToolType);\n\t\n\t /** Close toolbox when settings button is active */\n\t editor.toolbar.toolbox.close();\n\t editor.toolbar.settings.hideRemoveActions();\n\t };\n\t\n\t return callbacks;\n\t}({});\n\n/***/ }),\n/* 13 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Draw module\n\t *\n\t * @author Codex Team\n\t * @version 1.0.\n\t */\n\t\n\tmodule.exports = function (draw) {\n\t\n\t /**\n\t * Base editor wrapper\n\t */\n\t draw.wrapper = function () {\n\t\n\t var wrapper = document.createElement('div');\n\t\n\t wrapper.className += 'codex-editor';\n\t\n\t return wrapper;\n\t };\n\t\n\t /**\n\t * Content-editable holder\n\t */\n\t draw.redactor = function () {\n\t\n\t var redactor = document.createElement('div');\n\t\n\t redactor.className += 'ce-redactor';\n\t\n\t return redactor;\n\t };\n\t\n\t draw.ceBlock = function () {\n\t\n\t var block = document.createElement('DIV');\n\t\n\t block.className += 'ce_block';\n\t\n\t return block;\n\t };\n\t\n\t /**\n\t * Empty toolbar with toggler\n\t */\n\t draw.toolbar = function () {\n\t\n\t var bar = document.createElement('div');\n\t\n\t bar.className += 'ce-toolbar';\n\t\n\t return bar;\n\t };\n\t\n\t draw.toolbarContent = function () {\n\t\n\t var wrapper = document.createElement('DIV');\n\t\n\t wrapper.classList.add('ce-toolbar__content');\n\t\n\t return wrapper;\n\t };\n\t\n\t /**\n\t * Inline toolbar\n\t */\n\t draw.inlineToolbar = function () {\n\t\n\t var bar = document.createElement('DIV');\n\t\n\t bar.className += 'ce-toolbar-inline';\n\t\n\t return bar;\n\t };\n\t\n\t /**\n\t * Wrapper for inline toobar buttons\n\t */\n\t draw.inlineToolbarButtons = function () {\n\t\n\t var wrapper = document.createElement('DIV');\n\t\n\t wrapper.className += 'ce-toolbar-inline__buttons';\n\t\n\t return wrapper;\n\t };\n\t\n\t /**\n\t * For some actions\n\t */\n\t draw.inlineToolbarActions = function () {\n\t\n\t var wrapper = document.createElement('DIV');\n\t\n\t wrapper.className += 'ce-toolbar-inline__actions';\n\t\n\t return wrapper;\n\t };\n\t\n\t draw.inputForLink = function () {\n\t\n\t var input = document.createElement('INPUT');\n\t\n\t input.type = 'input';\n\t input.className += 'inputForLink';\n\t input.placeholder = 'Вставьте ссылку ...';\n\t input.setAttribute('form', 'defaultForm');\n\t\n\t input.setAttribute('autofocus', 'autofocus');\n\t\n\t return input;\n\t };\n\t\n\t /**\n\t * @todo Desc\n\t */\n\t draw.blockButtons = function () {\n\t\n\t var block = document.createElement('div');\n\t\n\t block.className += 'ce-toolbar__actions';\n\t\n\t return block;\n\t };\n\t\n\t /**\n\t * Block settings panel\n\t */\n\t draw.blockSettings = function () {\n\t\n\t var settings = document.createElement('div');\n\t\n\t settings.className += 'ce-settings';\n\t\n\t return settings;\n\t };\n\t\n\t draw.defaultSettings = function () {\n\t\n\t var div = document.createElement('div');\n\t\n\t div.classList.add('ce-settings_default');\n\t\n\t return div;\n\t };\n\t\n\t draw.pluginsSettings = function () {\n\t\n\t var div = document.createElement('div');\n\t\n\t div.classList.add('ce-settings_plugin');\n\t\n\t return div;\n\t };\n\t\n\t draw.plusButton = function () {\n\t\n\t var button = document.createElement('span');\n\t\n\t button.className = 'ce-toolbar__plus';\n\t // button.innerHTML = '';\n\t\n\t return button;\n\t };\n\t\n\t /**\n\t * Settings button in toolbar\n\t */\n\t draw.settingsButton = function () {\n\t\n\t var toggler = document.createElement('span');\n\t\n\t toggler.className = 'ce-toolbar__settings-btn';\n\t\n\t /** Toggler button*/\n\t toggler.innerHTML = '';\n\t\n\t return toggler;\n\t };\n\t\n\t /**\n\t * Redactor tools wrapper\n\t */\n\t\n\t draw.toolbox = function () {\n\t\n\t var wrapper = document.createElement('div');\n\t\n\t wrapper.className = 'ce-toolbar__tools';\n\t\n\t return wrapper;\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Draws tool buttons for toolbox\n\t *\n\t * @param {String} type\n\t * @param {String} classname\n\t * @returns {Element}\n\t */\n\t draw.toolbarButton = function (type, classname) {\n\t\n\t var button = document.createElement('li'),\n\t toolIcon = document.createElement('i'),\n\t toolTitle = document.createElement('span');\n\t\n\t button.dataset.type = type;\n\t button.setAttribute('title', type);\n\t\n\t toolIcon.classList.add(classname);\n\t toolTitle.classList.add('ce_toolbar_tools--title');\n\t\n\t button.appendChild(toolIcon);\n\t button.appendChild(toolTitle);\n\t\n\t return button;\n\t };\n\t\n\t /**\n\t * @protected\n\t *\n\t * Draws tools for inline toolbar\n\t *\n\t * @param {String} type\n\t * @param {String} classname\n\t */\n\t draw.toolbarButtonInline = function (type, classname) {\n\t\n\t var button = document.createElement('BUTTON'),\n\t toolIcon = document.createElement('I');\n\t\n\t button.type = 'button';\n\t button.dataset.type = type;\n\t toolIcon.classList.add(classname);\n\t\n\t button.appendChild(toolIcon);\n\t\n\t return button;\n\t };\n\t\n\t /**\n\t * Redactor block\n\t */\n\t draw.block = function (tagName, content) {\n\t\n\t var node = document.createElement(tagName);\n\t\n\t node.innerHTML = content || '';\n\t\n\t return node;\n\t };\n\t\n\t /**\n\t * Creates Node with passed tagName and className\n\t * @param {string} tagName\n\t * @param {string} className\n\t * @param {object} properties - allow to assign properties\n\t */\n\t draw.node = function (tagName, className, properties) {\n\t\n\t var el = document.createElement(tagName);\n\t\n\t if (className) el.className = className;\n\t\n\t if (properties) {\n\t\n\t for (var name in properties) {\n\t\n\t el[name] = properties[name];\n\t }\n\t }\n\t\n\t return el;\n\t };\n\t\n\t /**\n\t * Unavailable plugin block\n\t */\n\t draw.unavailableBlock = function () {\n\t\n\t var wrapper = document.createElement('DIV');\n\t\n\t wrapper.classList.add('cdx-unavailable-block');\n\t\n\t return wrapper;\n\t };\n\t\n\t return draw;\n\t}({});\n\n/***/ }),\n/* 14 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Caret Module\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (caret) {\n\t\n\t var editor = codex.editor;\n\t\n\t /**\n\t * @var {int} InputIndex - editable element in DOM\n\t */\n\t caret.inputIndex = null;\n\t\n\t /**\n\t * @var {int} offset - caret position in a text node.\n\t */\n\t caret.offset = null;\n\t\n\t /**\n\t * @var {int} focusedNodeIndex - we get index of child node from first-level block\n\t */\n\t caret.focusedNodeIndex = null;\n\t\n\t /**\n\t * Creates Document Range and sets caret to the element.\n\t * @protected\n\t * @uses caret.save — if you need to save caret position\n\t * @param {Element} el - Changed Node.\n\t */\n\t caret.set = function (el, index, offset) {\n\t\n\t offset = offset || caret.offset || 0;\n\t index = index || caret.focusedNodeIndex || 0;\n\t\n\t var childs = el.childNodes,\n\t nodeToSet;\n\t\n\t if (childs.length === 0) {\n\t\n\t nodeToSet = el;\n\t } else {\n\t\n\t nodeToSet = childs[index];\n\t }\n\t\n\t /** If Element is INPUT */\n\t if (el.contentEditable != 'true') {\n\t\n\t el.focus();\n\t return;\n\t }\n\t\n\t if (editor.core.isDomNode(nodeToSet)) {\n\t\n\t nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);\n\t }\n\t\n\t var range = document.createRange(),\n\t selection = window.getSelection();\n\t\n\t window.setTimeout(function () {\n\t\n\t range.setStart(nodeToSet, offset);\n\t range.setEnd(nodeToSet, offset);\n\t\n\t selection.removeAllRanges();\n\t selection.addRange(range);\n\t\n\t editor.caret.saveCurrentInputIndex();\n\t }, 20);\n\t };\n\t\n\t /**\n\t * @protected\n\t * Updates index of input and saves it in caret object\n\t */\n\t caret.saveCurrentInputIndex = function () {\n\t\n\t /** Index of Input that we paste sanitized content */\n\t var selection = window.getSelection(),\n\t inputs = editor.state.inputs,\n\t focusedNode = selection.anchorNode,\n\t focusedNodeHolder;\n\t\n\t if (!focusedNode) {\n\t\n\t return;\n\t }\n\t\n\t /** Looking for parent contentEditable block */\n\t while (focusedNode && focusedNode.contentEditable != 'true') {\n\t\n\t focusedNodeHolder = focusedNode.parentNode;\n\t focusedNode = focusedNodeHolder;\n\t }\n\t\n\t /** Input index in DOM level */\n\t var editableElementIndex = 0;\n\t\n\t while (focusedNode != inputs[editableElementIndex]) {\n\t\n\t editableElementIndex++;\n\t }\n\t\n\t caret.inputIndex = editableElementIndex;\n\t };\n\t\n\t /**\n\t * Returns current input index (caret object)\n\t */\n\t caret.getCurrentInputIndex = function () {\n\t\n\t return caret.inputIndex;\n\t };\n\t\n\t /**\n\t * @param {int} index - index of first-level block after that we set caret into next input\n\t */\n\t caret.setToNextBlock = function (index) {\n\t\n\t var inputs = editor.state.inputs,\n\t nextInput = inputs[index + 1];\n\t\n\t if (!nextInput) {\n\t\n\t editor.core.log('We are reached the end');\n\t return;\n\t }\n\t\n\t /**\n\t * When new Block created or deleted content of input\n\t * We should add some text node to set caret\n\t */\n\t if (!nextInput.childNodes.length) {\n\t\n\t var emptyTextElement = document.createTextNode('');\n\t\n\t nextInput.appendChild(emptyTextElement);\n\t }\n\t\n\t editor.caret.inputIndex = index + 1;\n\t editor.caret.set(nextInput, 0, 0);\n\t editor.content.workingNodeChanged(nextInput);\n\t };\n\t\n\t /**\n\t * @param {int} index - index of target input.\n\t * Sets caret to input with this index\n\t */\n\t caret.setToBlock = function (index) {\n\t\n\t var inputs = editor.state.inputs,\n\t targetInput = inputs[index];\n\t\n\t if (!targetInput) {\n\t\n\t return;\n\t }\n\t\n\t /**\n\t * When new Block created or deleted content of input\n\t * We should add some text node to set caret\n\t */\n\t if (!targetInput.childNodes.length) {\n\t\n\t var emptyTextElement = document.createTextNode('');\n\t\n\t targetInput.appendChild(emptyTextElement);\n\t }\n\t\n\t editor.caret.inputIndex = index;\n\t editor.caret.set(targetInput, 0, 0);\n\t editor.content.workingNodeChanged(targetInput);\n\t };\n\t\n\t /**\n\t * @param {int} index - index of input\n\t */\n\t caret.setToPreviousBlock = function (index) {\n\t\n\t index = index || 0;\n\t\n\t var inputs = editor.state.inputs,\n\t previousInput = inputs[index - 1],\n\t lastChildNode,\n\t lengthOfLastChildNode,\n\t emptyTextElement;\n\t\n\t if (!previousInput) {\n\t\n\t editor.core.log('We are reached first node');\n\t return;\n\t }\n\t\n\t lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length);\n\t lengthOfLastChildNode = lastChildNode.length;\n\t\n\t /**\n\t * When new Block created or deleted content of input\n\t * We should add some text node to set caret\n\t */\n\t if (!previousInput.childNodes.length) {\n\t\n\t emptyTextElement = document.createTextNode('');\n\t previousInput.appendChild(emptyTextElement);\n\t }\n\t editor.caret.inputIndex = index - 1;\n\t editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode);\n\t editor.content.workingNodeChanged(inputs[index - 1]);\n\t };\n\t\n\t caret.position = {\n\t\n\t atStart: function atStart() {\n\t\n\t var selection = window.getSelection(),\n\t anchorOffset = selection.anchorOffset,\n\t anchorNode = selection.anchorNode,\n\t firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode),\n\t pluginsRender = firstLevelBlock.childNodes[0];\n\t\n\t if (!editor.core.isDomNode(anchorNode)) {\n\t\n\t anchorNode = anchorNode.parentNode;\n\t }\n\t\n\t var isFirstNode = anchorNode === pluginsRender.childNodes[0],\n\t isOffsetZero = anchorOffset === 0;\n\t\n\t return isFirstNode && isOffsetZero;\n\t },\n\t\n\t atTheEnd: function atTheEnd() {\n\t\n\t var selection = window.getSelection(),\n\t anchorOffset = selection.anchorOffset,\n\t anchorNode = selection.anchorNode;\n\t\n\t /** Caret is at the end of input */\n\t return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;\n\t }\n\t };\n\t\n\t /**\n\t * Inserts node at the caret location\n\t * @param {HTMLElement|DocumentFragment} node\n\t */\n\t caret.insertNode = function (node) {\n\t\n\t var selection,\n\t range,\n\t lastNode = node;\n\t\n\t if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) {\n\t\n\t lastNode = node.lastChild;\n\t }\n\t\n\t selection = window.getSelection();\n\t\n\t range = selection.getRangeAt(0);\n\t range.deleteContents();\n\t\n\t range.insertNode(node);\n\t\n\t range.setStartAfter(lastNode);\n\t range.collapse(true);\n\t\n\t selection.removeAllRanges();\n\t selection.addRange(range);\n\t };\n\t\n\t return caret;\n\t}({});\n\n/***/ }),\n/* 15 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Notification Module\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (notifications) {\n\t\n\t var editor = codex.editor;\n\t\n\t var queue = [];\n\t\n\t var addToQueue = function addToQueue(settings) {\n\t\n\t queue.push(settings);\n\t\n\t var index = 0;\n\t\n\t while (index < queue.length && queue.length > 5) {\n\t\n\t if (queue[index].type == 'confirm' || queue[index].type == 'prompt') {\n\t\n\t index++;\n\t continue;\n\t }\n\t\n\t queue[index].close();\n\t queue.splice(index, 1);\n\t }\n\t };\n\t\n\t notifications.createHolder = function () {\n\t\n\t var holder = editor.draw.node('DIV', 'cdx-notifications-block');\n\t\n\t editor.nodes.notifications = document.body.appendChild(holder);\n\t\n\t return holder;\n\t };\n\t\n\t /**\n\t * Error notificator. Shows block with message\n\t * @protected\n\t */\n\t notifications.errorThrown = function (errorMsg, event) {\n\t\n\t editor.notifications.notification({ message: 'This action is not available currently', type: event.type });\n\t };\n\t\n\t /**\n\t *\n\t * Appends notification\n\t *\n\t * settings = {\n\t * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type\n\t * message - notification message\n\t * okMsg - confirm button text (default - 'Ok')\n\t * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types\n\t * confirm - function-handler for ok button click\n\t * cancel - function-handler for cancel button click. Only for confirm and prompt types\n\t * time - time (in seconds) after which notification will close (default - 10s)\n\t * }\n\t *\n\t * @param settings\n\t */\n\t notifications.notification = function (constructorSettings) {\n\t\n\t /** Private vars and methods */\n\t var notification = null,\n\t cancel = null,\n\t type = null,\n\t confirm = null,\n\t inputField = null;\n\t\n\t var confirmHandler = function confirmHandler() {\n\t\n\t close();\n\t\n\t if (typeof confirm !== 'function') {\n\t\n\t return;\n\t }\n\t\n\t if (type == 'prompt') {\n\t\n\t confirm(inputField.value);\n\t return;\n\t }\n\t\n\t confirm();\n\t };\n\t\n\t var cancelHandler = function cancelHandler() {\n\t\n\t close();\n\t\n\t if (typeof cancel !== 'function') {\n\t\n\t return;\n\t }\n\t\n\t cancel();\n\t };\n\t\n\t /** Public methods */\n\t function create(settings) {\n\t\n\t if (!(settings && settings.message)) {\n\t\n\t editor.core.log('Can\\'t create notification. Message is missed');\n\t return;\n\t }\n\t\n\t settings.type = settings.type || 'alert';\n\t settings.time = settings.time * 1000 || 10000;\n\t\n\t var wrapper = editor.draw.node('DIV', 'cdx-notification'),\n\t message = editor.draw.node('DIV', 'cdx-notification__message'),\n\t input = editor.draw.node('INPUT', 'cdx-notification__input'),\n\t okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'),\n\t cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn');\n\t\n\t message.textContent = settings.message;\n\t okBtn.textContent = settings.okMsg || 'ОК';\n\t cancelBtn.textContent = settings.cancelMsg || 'Отмена';\n\t\n\t editor.listeners.add(okBtn, 'click', confirmHandler);\n\t editor.listeners.add(cancelBtn, 'click', cancelHandler);\n\t\n\t wrapper.appendChild(message);\n\t\n\t if (settings.type == 'prompt') {\n\t\n\t wrapper.appendChild(input);\n\t }\n\t\n\t wrapper.appendChild(okBtn);\n\t\n\t if (settings.type == 'prompt' || settings.type == 'confirm') {\n\t\n\t wrapper.appendChild(cancelBtn);\n\t }\n\t\n\t wrapper.classList.add('cdx-notification-' + settings.type);\n\t wrapper.dataset.type = settings.type;\n\t\n\t notification = wrapper;\n\t type = settings.type;\n\t confirm = settings.confirm;\n\t cancel = settings.cancel;\n\t inputField = input;\n\t\n\t if (settings.type != 'prompt' && settings.type != 'confirm') {\n\t\n\t window.setTimeout(close, settings.time);\n\t }\n\t };\n\t\n\t /**\n\t * Show notification block\n\t */\n\t function send() {\n\t\n\t editor.nodes.notifications.appendChild(notification);\n\t inputField.focus();\n\t\n\t editor.nodes.notifications.classList.add('cdx-notification__notification-appending');\n\t\n\t window.setTimeout(function () {\n\t\n\t editor.nodes.notifications.classList.remove('cdx-notification__notification-appending');\n\t }, 100);\n\t\n\t addToQueue({ type: type, close: close });\n\t };\n\t\n\t /**\n\t * Remove notification block\n\t */\n\t function close() {\n\t\n\t notification.remove();\n\t };\n\t\n\t if (constructorSettings) {\n\t\n\t create(constructorSettings);\n\t send();\n\t }\n\t\n\t return {\n\t create: create,\n\t send: send,\n\t close: close\n\t };\n\t };\n\t\n\t notifications.clear = function () {\n\t\n\t editor.nodes.notifications.innerHTML = '';\n\t queue = [];\n\t };\n\t\n\t return notifications;\n\t}({});\n\n/***/ }),\n/* 16 */\n/***/ (function(module, exports) {\n\n\t\"use strict\";\n\t\n\t/**\n\t * Codex Editor Parser Module\n\t *\n\t * @author Codex Team\n\t * @version 1.1\n\t */\n\t\n\tmodule.exports = function (parser) {\n\t\n\t var editor = codex.editor;\n\t\n\t /** inserting text */\n\t parser.insertPastedContent = function (blockType, tag) {\n\t\n\t editor.content.insertBlock({\n\t type: blockType.type,\n\t block: blockType.render({\n\t text: tag.innerHTML\n\t })\n\t });\n\t };\n\t\n\t /**\n\t * Check DOM node for display style: separated block or child-view\n\t */\n\t parser.isFirstLevelBlock = function (node) {\n\t\n\t return node.nodeType == editor.core.nodeTypes.TAG && node.classList.contains(editor.ui.className.BLOCK_CLASSNAME);\n\t };\n\t\n\t return parser;\n\t}({});\n\n/***/ }),\n/* 17 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Sanitizer\n\t */\n\t\n\tmodule.exports = function (sanitizer) {\n\t\n\t /** HTML Janitor library */\n\t var janitor = __webpack_require__(18);\n\t\n\t /** Codex Editor */\n\t var editor = codex.editor;\n\t\n\t sanitizer.prepare = function () {\n\t\n\t if (editor.settings.sanitizer && !editor.core.isEmpty(editor.settings.sanitizer)) {\n\t\n\t Config.CUSTOM = editor.settings.sanitizer;\n\t }\n\t };\n\t\n\t /**\n\t * Basic config\n\t */\n\t var Config = {\n\t\n\t /** User configuration */\n\t CUSTOM: null,\n\t\n\t BASIC: {\n\t\n\t tags: {\n\t p: {},\n\t a: {\n\t href: true,\n\t target: '_blank',\n\t rel: 'nofollow'\n\t }\n\t }\n\t }\n\t };\n\t\n\t sanitizer.Config = Config;\n\t\n\t /**\n\t *\n\t * @param userCustomConfig\n\t * @returns {*}\n\t * @private\n\t *\n\t * @description If developer uses editor's API, then he can customize sane restrictions.\n\t * Or, sane config can be defined globally in editors initialization. That config will be used everywhere\n\t * At least, if there is no config overrides, that API uses BASIC Default configation\n\t */\n\t var init_ = function init_(userCustomConfig) {\n\t\n\t var configuration = userCustomConfig || Config.CUSTOM || Config.BASIC;\n\t\n\t return new janitor(configuration);\n\t };\n\t\n\t /**\n\t * Cleans string from unwanted tags\n\t * @protected\n\t * @param {String} dirtyString - taint string\n\t * @param {Object} customConfig - allowed tags\n\t */\n\t sanitizer.clean = function (dirtyString, customConfig) {\n\t\n\t var janitorInstance = init_(customConfig);\n\t\n\t return janitorInstance.clean(dirtyString);\n\t };\n\t\n\t return sanitizer;\n\t}({});\n\n/***/ }),\n/* 18 */\n/***/ (function(module, exports, __webpack_require__) {\n\n\tvar __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (root, factory) {\n\t if (true) {\n\t !(__WEBPACK_AMD_DEFINE_FACTORY__ = (factory), __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? (__WEBPACK_AMD_DEFINE_FACTORY__.call(exports, __webpack_require__, exports, module)) : __WEBPACK_AMD_DEFINE_FACTORY__), __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));\n\t } else if (typeof exports === 'object') {\n\t module.exports = factory();\n\t } else {\n\t root.HTMLJanitor = factory();\n\t }\n\t}(this, function () {\n\t\n\t /**\n\t * @param {Object} config.tags Dictionary of allowed tags.\n\t * @param {boolean} config.keepNestedBlockElements Default false.\n\t */\n\t function HTMLJanitor(config) {\n\t\n\t var tagDefinitions = config['tags'];\n\t var tags = Object.keys(tagDefinitions);\n\t\n\t var validConfigValues = tags\n\t .map(function(k) { return typeof tagDefinitions[k]; })\n\t .every(function(type) { return type === 'object' || type === 'boolean' || type === 'function'; });\n\t\n\t if(!validConfigValues) {\n\t throw new Error(\"The configuration was invalid\");\n\t }\n\t\n\t this.config = config;\n\t }\n\t\n\t // TODO: not exhaustive?\n\t var blockElementNames = ['P', 'LI', 'TD', 'TH', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'PRE'];\n\t function isBlockElement(node) {\n\t return blockElementNames.indexOf(node.nodeName) !== -1;\n\t }\n\t\n\t var inlineElementNames = ['A', 'B', 'STRONG', 'I', 'EM', 'SUB', 'SUP', 'U', 'STRIKE'];\n\t function isInlineElement(node) {\n\t return inlineElementNames.indexOf(node.nodeName) !== -1;\n\t }\n\t\n\t HTMLJanitor.prototype.clean = function (html) {\n\t var sandbox = document.createElement('div');\n\t sandbox.innerHTML = html;\n\t\n\t this._sanitize(sandbox);\n\t\n\t return sandbox.innerHTML;\n\t };\n\t\n\t HTMLJanitor.prototype._sanitize = function (parentNode) {\n\t var treeWalker = createTreeWalker(parentNode);\n\t var node = treeWalker.firstChild();\n\t if (!node) { return; }\n\t\n\t do {\n\t // Ignore nodes that have already been sanitized\n\t if (node._sanitized) {\n\t continue;\n\t }\n\t\n\t if (node.nodeType === Node.TEXT_NODE) {\n\t // If this text node is just whitespace and the previous or next element\n\t // sibling is a block element, remove it\n\t // N.B.: This heuristic could change. Very specific to a bug with\n\t // `contenteditable` in Firefox: http://jsbin.com/EyuKase/1/edit?js,output\n\t // FIXME: make this an option?\n\t if (node.data.trim() === ''\n\t && ((node.previousElementSibling && isBlockElement(node.previousElementSibling))\n\t || (node.nextElementSibling && isBlockElement(node.nextElementSibling)))) {\n\t parentNode.removeChild(node);\n\t this._sanitize(parentNode);\n\t break;\n\t } else {\n\t continue;\n\t }\n\t }\n\t\n\t // Remove all comments\n\t if (node.nodeType === Node.COMMENT_NODE) {\n\t parentNode.removeChild(node);\n\t this._sanitize(parentNode);\n\t break;\n\t }\n\t\n\t var isInline = isInlineElement(node);\n\t var containsBlockElement;\n\t if (isInline) {\n\t containsBlockElement = Array.prototype.some.call(node.childNodes, isBlockElement);\n\t }\n\t\n\t // Block elements should not be nested (e.g.

  • ...); if\n\t // they are, we want to unwrap the inner block element.\n\t var isNotTopContainer = !! parentNode.parentNode;\n\t var isNestedBlockElement =\n\t isBlockElement(parentNode) &&\n\t isBlockElement(node) &&\n\t isNotTopContainer;\n\t\n\t var nodeName = node.nodeName.toLowerCase();\n\t\n\t var allowedAttrs = getAllowedAttrs(this.config, nodeName, node);\n\t\n\t var isInvalid = isInline && containsBlockElement;\n\t\n\t // Drop tag entirely according to the whitelist *and* if the markup\n\t // is invalid.\n\t if (isInvalid || shouldRejectNode(node, allowedAttrs)\n\t || (!this.config.keepNestedBlockElements && isNestedBlockElement)) {\n\t // Do not keep the inner text of SCRIPT/STYLE elements.\n\t if (! (node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE')) {\n\t while (node.childNodes.length > 0) {\n\t parentNode.insertBefore(node.childNodes[0], node);\n\t }\n\t }\n\t parentNode.removeChild(node);\n\t\n\t this._sanitize(parentNode);\n\t break;\n\t }\n\t\n\t // Sanitize attributes\n\t for (var a = 0; a < node.attributes.length; a += 1) {\n\t var attr = node.attributes[a];\n\t\n\t if (shouldRejectAttr(attr, allowedAttrs, node)) {\n\t node.removeAttribute(attr.name);\n\t // Shift the array to continue looping.\n\t a = a - 1;\n\t }\n\t }\n\t\n\t // Sanitize children\n\t this._sanitize(node);\n\t\n\t // Mark node as sanitized so it's ignored in future runs\n\t node._sanitized = true;\n\t } while ((node = treeWalker.nextSibling()));\n\t };\n\t\n\t function createTreeWalker(node) {\n\t return document.createTreeWalker(node,\n\t NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT,\n\t null, false);\n\t }\n\t\n\t function getAllowedAttrs(config, nodeName, node){\n\t if (typeof config.tags[nodeName] === 'function') {\n\t return config.tags[nodeName](node);\n\t } else {\n\t return config.tags[nodeName];\n\t }\n\t }\n\t\n\t function shouldRejectNode(node, allowedAttrs){\n\t if (typeof allowedAttrs === 'undefined') {\n\t return true;\n\t } else if (typeof allowedAttrs === 'boolean') {\n\t return !allowedAttrs;\n\t }\n\t\n\t return false;\n\t }\n\t\n\t function shouldRejectAttr(attr, allowedAttrs, node){\n\t var attrName = attr.name.toLowerCase();\n\t\n\t if (allowedAttrs === true){\n\t return false;\n\t } else if (typeof allowedAttrs[attrName] === 'function'){\n\t return !allowedAttrs[attrName](attr.value, node);\n\t } else if (typeof allowedAttrs[attrName] === 'undefined'){\n\t return true;\n\t } else if (allowedAttrs[attrName] === false) {\n\t return true;\n\t } else if (typeof allowedAttrs[attrName] === 'string') {\n\t return (allowedAttrs[attrName] !== attr.value);\n\t }\n\t\n\t return false;\n\t }\n\t\n\t return HTMLJanitor;\n\t\n\t}));\n\n\n/***/ }),\n/* 19 */\n/***/ (function(module, exports) {\n\n\t\"use strict\";\n\t\n\t/**\n\t * Codex Editor Listeners module\n\t *\n\t * @author Codex Team\n\t * @version 1.0\n\t */\n\t\n\t/**\n\t * Module-decorator for event listeners assignment\n\t */\n\tmodule.exports = function (listeners) {\n\t\n\t var allListeners = [];\n\t\n\t /**\n\t * Search methods\n\t *\n\t * byElement, byType and byHandler returns array of suitable listeners\n\t * one and all takes element, eventType, and handler and returns first (all) suitable listener\n\t *\n\t */\n\t listeners.search = function () {\n\t\n\t var byElement = function byElement(element, context) {\n\t\n\t var listenersOnElement = [];\n\t\n\t context = context || allListeners;\n\t\n\t for (var i = 0; i < context.length; i++) {\n\t\n\t var listener = context[i];\n\t\n\t if (listener.element === element) {\n\t\n\t listenersOnElement.push(listener);\n\t }\n\t }\n\t\n\t return listenersOnElement;\n\t };\n\t\n\t var byType = function byType(eventType, context) {\n\t\n\t var listenersWithType = [];\n\t\n\t context = context || allListeners;\n\t\n\t for (var i = 0; i < context.length; i++) {\n\t\n\t var listener = context[i];\n\t\n\t if (listener.type === eventType) {\n\t\n\t listenersWithType.push(listener);\n\t }\n\t }\n\t\n\t return listenersWithType;\n\t };\n\t\n\t var byHandler = function byHandler(handler, context) {\n\t\n\t var listenersWithHandler = [];\n\t\n\t context = context || allListeners;\n\t\n\t for (var i = 0; i < context.length; i++) {\n\t\n\t var listener = context[i];\n\t\n\t if (listener.handler === handler) {\n\t\n\t listenersWithHandler.push(listener);\n\t }\n\t }\n\t\n\t return listenersWithHandler;\n\t };\n\t\n\t var one = function one(element, eventType, handler) {\n\t\n\t var result = allListeners;\n\t\n\t if (element) result = byElement(element, result);\n\t\n\t if (eventType) result = byType(eventType, result);\n\t\n\t if (handler) result = byHandler(handler, result);\n\t\n\t return result[0];\n\t };\n\t\n\t var all = function all(element, eventType, handler) {\n\t\n\t var result = allListeners;\n\t\n\t if (element) result = byElement(element, result);\n\t\n\t if (eventType) result = byType(eventType, result);\n\t\n\t if (handler) result = byHandler(handler, result);\n\t\n\t return result;\n\t };\n\t\n\t return {\n\t byElement: byElement,\n\t byType: byType,\n\t byHandler: byHandler,\n\t one: one,\n\t all: all\n\t };\n\t }();\n\t\n\t listeners.add = function (element, eventType, handler, isCapture) {\n\t\n\t element.addEventListener(eventType, handler, isCapture);\n\t\n\t var data = {\n\t element: element,\n\t type: eventType,\n\t handler: handler\n\t };\n\t\n\t var alreadyAddedListener = listeners.search.one(element, eventType, handler);\n\t\n\t if (!alreadyAddedListener) {\n\t\n\t allListeners.push(data);\n\t }\n\t };\n\t\n\t listeners.remove = function (element, eventType, handler) {\n\t\n\t element.removeEventListener(eventType, handler);\n\t\n\t var existingListeners = listeners.search.all(element, eventType, handler);\n\t\n\t for (var i = 0; i < existingListeners.length; i++) {\n\t\n\t var index = allListeners.indexOf(existingListeners[i]);\n\t\n\t if (index > 0) {\n\t\n\t allListeners.splice(index, 1);\n\t }\n\t }\n\t };\n\t\n\t listeners.removeAll = function () {\n\t\n\t allListeners.map(function (current) {\n\t\n\t listeners.remove(current.element, current.type, current.handler);\n\t });\n\t };\n\t\n\t listeners.get = function (element, eventType, handler) {\n\t\n\t return listeners.search.all(element, eventType, handler);\n\t };\n\t\n\t return listeners;\n\t}({});\n\n/***/ }),\n/* 20 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\tvar _typeof = typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; };\n\t\n\t/**\n\t * Codex Editor Destroyer module\n\t *\n\t * @auhor Codex Team\n\t * @version 1.0\n\t */\n\t\n\tmodule.exports = function (destroyer) {\n\t\n\t var editor = codex.editor;\n\t\n\t destroyer.removeNodes = function () {\n\t\n\t editor.nodes.wrapper.remove();\n\t editor.nodes.notifications.remove();\n\t };\n\t\n\t destroyer.destroyPlugins = function () {\n\t\n\t for (var tool in editor.tools) {\n\t\n\t if (typeof editor.tools[tool].destroy === 'function') {\n\t\n\t editor.tools[tool].destroy();\n\t }\n\t }\n\t };\n\t\n\t destroyer.destroyScripts = function () {\n\t\n\t var scripts = document.getElementsByTagName('SCRIPT');\n\t\n\t for (var i = 0; i < scripts.length; i++) {\n\t\n\t if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) {\n\t\n\t scripts[i].remove();\n\t i--;\n\t }\n\t }\n\t };\n\t\n\t /**\n\t * Delete editor data from webpage.\n\t * You should send settings argument with boolean flags:\n\t * @param settings.ui- remove redactor event listeners and DOM nodes\n\t * @param settings.scripts - remove redactor scripts from DOM\n\t * @param settings.plugins - remove plugin's objects\n\t * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true\n\t * }\n\t *\n\t */\n\t destroyer.destroy = function (settings) {\n\t\n\t if (!settings || (typeof settings === 'undefined' ? 'undefined' : _typeof(settings)) !== 'object') {\n\t\n\t return;\n\t }\n\t\n\t if (settings.ui) {\n\t\n\t destroyer.removeNodes();\n\t editor.listeners.removeAll();\n\t }\n\t\n\t if (settings.scripts) {\n\t\n\t destroyer.destroyScripts();\n\t }\n\t\n\t if (settings.plugins) {\n\t\n\t destroyer.destroyPlugins();\n\t }\n\t\n\t if (settings.ui && settings.scripts && settings.core) {\n\t\n\t delete codex.editor;\n\t }\n\t };\n\t\n\t return destroyer;\n\t}({});\n\n/***/ }),\n/* 21 */\n/***/ (function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\n\t * Codex Editor Paste module\n\t *\n\t * @author Codex Team\n\t * @version 1.1.1\n\t */\n\t\n\tmodule.exports = function (paste) {\n\t\n\t var editor = codex.editor;\n\t\n\t var patterns = [];\n\t\n\t paste.prepare = function () {\n\t\n\t var tools = editor.tools;\n\t\n\t for (var tool in tools) {\n\t\n\t if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) {\n\t\n\t continue;\n\t }\n\t\n\t tools[tool].renderOnPastePatterns.map(function (pattern) {\n\t\n\t patterns.push(pattern);\n\t });\n\t }\n\t\n\t return Promise.resolve();\n\t };\n\t\n\t /**\n\t * Saves data\n\t * @param event\n\t */\n\t paste.pasted = function (event) {\n\t\n\t var clipBoardData = event.clipboardData || window.clipboardData,\n\t content = clipBoardData.getData('Text');\n\t\n\t var result = analize(content);\n\t\n\t if (result) {\n\t\n\t event.preventDefault();\n\t event.stopImmediatePropagation();\n\t }\n\t\n\t return result;\n\t };\n\t\n\t /**\n\t * Analizes pated string and calls necessary method\n\t */\n\t\n\t var analize = function analize(string) {\n\t\n\t var result = false,\n\t content = editor.content.currentNode,\n\t plugin = content.dataset.tool;\n\t\n\t patterns.map(function (pattern) {\n\t\n\t var execArray = pattern.regex.exec(string),\n\t match = execArray && execArray[0];\n\t\n\t if (match && match === string.trim()) {\n\t\n\t /** current block is not empty */\n\t if (content.textContent.trim() && plugin == editor.settings.initialBlockPlugin) {\n\t\n\t pasteToNewBlock_();\n\t }\n\t\n\t pattern.callback(string, pattern);\n\t result = true;\n\t }\n\t });\n\t\n\t return result;\n\t };\n\t\n\t var pasteToNewBlock_ = function pasteToNewBlock_() {\n\t\n\t /** Create new initial block */\n\t editor.content.insertBlock({\n\t\n\t type: editor.settings.initialBlockPlugin,\n\t block: editor.tools[editor.settings.initialBlockPlugin].render({\n\t text: ''\n\t })\n\t\n\t }, false);\n\t };\n\t\n\t /**\n\t * This method prevents default behaviour.\n\t *\n\t * @param {Object} event\n\t * @protected\n\t *\n\t * @description We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.\n\t * Firstly, we need to memorize the caret position. We can do that by getting the range of selection.\n\t * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node\n\t */\n\t paste.blockPasteCallback = function (event) {\n\t\n\t if (!needsToHandlePasteEvent(event.target)) {\n\t\n\t return;\n\t }\n\t\n\t /** Prevent default behaviour */\n\t event.preventDefault();\n\t\n\t /** get html pasted data - dirty data */\n\t var htmlData = event.clipboardData.getData('text/html'),\n\t plainData = event.clipboardData.getData('text/plain');\n\t\n\t /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/\n\t var paragraphs = editor.draw.node('DIV', '', {}),\n\t cleanData,\n\t wrappedData;\n\t\n\t /** Create fragment, that we paste to range after proccesing */\n\t cleanData = editor.sanitizer.clean(htmlData);\n\t\n\t /**\n\t * We wrap pasted text with

    tags to split it logically\n\t * @type {string}\n\t */\n\t wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData);\n\t paragraphs.innerHTML = wrappedData;\n\t\n\t /**\n\t * If there only one paragraph, just insert in at the caret location\n\t */\n\t if (paragraphs.childNodes.length == 1) {\n\t\n\t emulateUserAgentBehaviour(paragraphs.firstChild);\n\t return;\n\t }\n\t\n\t insertPastedParagraphs(paragraphs.childNodes);\n\t };\n\t\n\t /**\n\t * Checks if we should handle paste event on block\n\t * @param block\n\t *\n\t * @return {boolean}\n\t */\n\t var needsToHandlePasteEvent = function needsToHandlePasteEvent(block) {\n\t\n\t /** If area is input or textarea then allow default behaviour */\n\t if (editor.core.isNativeInput(block)) {\n\t\n\t return false;\n\t }\n\t\n\t var editableParent = editor.content.getEditableParent(block);\n\t\n\t /** Allow paste when event target placed in Editable element */\n\t if (!editableParent) {\n\t\n\t return false;\n\t }\n\t\n\t return true;\n\t };\n\t\n\t /**\n\t * Inserts new initial plugin blocks with data in paragraphs\n\t *\n\t * @param {Array} paragraphs - array of paragraphs (

    ) whit content, that should be inserted\n\t */\n\t var insertPastedParagraphs = function insertPastedParagraphs(paragraphs) {\n\t\n\t var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin,\n\t currentNode = editor.content.currentNode;\n\t\n\t paragraphs.forEach(function (paragraph) {\n\t\n\t /** Don't allow empty paragraphs */\n\t if (editor.core.isBlockEmpty(paragraph)) {\n\t\n\t return;\n\t }\n\t\n\t editor.content.insertBlock({\n\t type: NEW_BLOCK_TYPE,\n\t block: editor.tools[NEW_BLOCK_TYPE].render({\n\t text: paragraph.innerHTML\n\t })\n\t });\n\t\n\t editor.caret.inputIndex++;\n\t });\n\t\n\t editor.caret.setToPreviousBlock(editor.caret.getCurrentInputIndex() + 1);\n\t\n\t /**\n\t * If there was no data in working node, remove it\n\t */\n\t if (editor.core.isBlockEmpty(currentNode)) {\n\t\n\t currentNode.remove();\n\t editor.ui.saveInputs();\n\t }\n\t };\n\t\n\t /**\n\t * Inserts node content at the caret position\n\t *\n\t * @param {Node} node - DOM node (could be DocumentFragment), that should be inserted at the caret location\n\t */\n\t var emulateUserAgentBehaviour = function emulateUserAgentBehaviour(node) {\n\t\n\t var newNode;\n\t\n\t if (node.childElementCount) {\n\t\n\t newNode = document.createDocumentFragment();\n\t\n\t node.childNodes.forEach(function (current) {\n\t\n\t if (!editor.core.isDomNode(current) && current.data.trim() === '') {\n\t\n\t return;\n\t }\n\t\n\t newNode.appendChild(current.cloneNode(true));\n\t });\n\t } else {\n\t\n\t newNode = document.createTextNode(node.textContent);\n\t }\n\t\n\t editor.caret.insertNode(newNode);\n\t };\n\t\n\t return paste;\n\t}({});\n\n/***/ })\n/******/ ]);\n\n\n// WEBPACK FOOTER //\n// codex-editor.js"," \t// The module cache\n \tvar installedModules = {};\n\n \t// The require function\n \tfunction __webpack_require__(moduleId) {\n\n \t\t// Check if module is in cache\n \t\tif(installedModules[moduleId])\n \t\t\treturn installedModules[moduleId].exports;\n\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\texports: {},\n \t\t\tid: moduleId,\n \t\t\tloaded: false\n \t\t};\n\n \t\t// Execute the module function\n \t\tmodules[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n \t\t// Flag the module as loaded\n \t\tmodule.loaded = true;\n\n \t\t// Return the exports of the module\n \t\treturn module.exports;\n \t}\n\n\n \t// expose the modules object (__webpack_modules__)\n \t__webpack_require__.m = modules;\n\n \t// expose the module cache\n \t__webpack_require__.c = installedModules;\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(0);\n\n\n\n// WEBPACK FOOTER //\n// webpack/bootstrap 804711f99558afed9516","/**\n *\n * Codex Editor\n *\n * @author Codex Team\n */\n\nmodule.exports = (function (editor) {\n\n 'use strict';\n\n editor.version = VERSION;\n editor.scriptPrefix = 'cdx-script-';\n\n var init = function () {\n\n editor.core = require('./modules/core');\n editor.tools = require('./modules/tools');\n editor.ui = require('./modules/ui');\n editor.transport = require('./modules/transport');\n editor.renderer = require('./modules/renderer');\n editor.saver = require('./modules/saver');\n editor.content = require('./modules/content');\n editor.toolbar = require('./modules/toolbar/toolbar');\n editor.callback = require('./modules/callbacks');\n editor.draw = require('./modules/draw');\n editor.caret = require('./modules/caret');\n editor.notifications = require('./modules/notifications');\n editor.parser = require('./modules/parser');\n editor.sanitizer = require('./modules/sanitizer');\n editor.listeners = require('./modules/listeners');\n editor.destroyer = require('./modules/destroyer');\n editor.paste = require('./modules/paste');\n\n };\n\n /**\n * @public\n * holds initial settings\n */\n editor.settings = {\n tools : ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'],\n holderId : 'codex-editor',\n\n // Type of block showing on empty editor\n initialBlockPlugin: 'paragraph'\n };\n\n /**\n * public\n *\n * Static nodes\n */\n editor.nodes = {\n holder : null,\n wrapper : null,\n toolbar : null,\n inlineToolbar : {\n wrapper : null,\n buttons : null,\n actions : null\n },\n toolbox : null,\n notifications : null,\n plusButton : null,\n showSettingsButton: null,\n showTrashButton : null,\n blockSettings : null,\n pluginSettings : null,\n defaultSettings : null,\n toolbarButtons : {}, // { type : DomEl, ... }\n redactor : null\n };\n\n /**\n * @public\n *\n * Output state\n */\n editor.state = {\n jsonOutput : [],\n blocks : [],\n inputs : []\n };\n\n /**\n * @public\n * Editor plugins\n */\n editor.tools = {};\n\n /**\n * Initialization\n * @uses Promise cEditor.core.prepare\n * @param {Object} userSettings\n * @param {Array} userSettings.tools list of plugins\n * @param {String} userSettings.holderId Element's id to append editor\n *\n * Load user defined tools\n * Tools must contain this important objects :\n * @param {String} type - this is a type of plugin. It can be used as plugin name\n * @param {String} iconClassname - this a icon in toolbar\n * @param {Object} make - what should plugin do, when it is clicked\n * @param {Object} appendCallback - callback after clicking\n * @param {Element} settings - what settings does it have\n * @param {Object} render - plugin get JSON, and should return HTML\n * @param {Object} save - plugin gets HTML content, returns JSON\n * @param {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE\n * @param {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE\n *\n * @example\n * - type : 'header',\n * - iconClassname : 'ce-icon-header',\n * - make : headerTool.make,\n * - appendCallback : headerTool.appendCallback,\n * - settings : headerTool.makeSettings(),\n * - render : headerTool.render,\n * - save : headerTool.save,\n * - displayInToolbox : true,\n * - enableLineBreaks : false\n */\n editor.start = function (userSettings) {\n\n init();\n\n editor.core.prepare(userSettings)\n\n // If all ok, make UI, bind events and parse initial-content\n .then(editor.ui.prepare)\n .then(editor.tools.prepare)\n .then(editor.sanitizer.prepare)\n .then(editor.paste.prepare)\n .then(editor.transport.prepare)\n .then(editor.renderer.makeBlocksFromData)\n .then(editor.ui.saveInputs)\n .catch(function (error) {\n\n editor.core.log('Initialization failed with error: %o', 'warn', error);\n\n });\n\n };\n\n return editor;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./codex.js","/**\n * Codex Editor Core\n *\n * @author Codex Team\n * @version 1.1.3\n */\n\nmodule.exports = (function (core) {\n\n let editor = codex.editor;\n\n /**\n * @public\n *\n * Editor preparing method\n * @return Promise\n */\n core.prepare = function (userSettings) {\n\n return new Promise(function (resolve, reject) {\n\n if ( userSettings ) {\n\n editor.settings.tools = userSettings.tools || editor.settings.tools;\n\n }\n\n if (userSettings.data) {\n\n editor.state.blocks = userSettings.data;\n\n }\n\n if (userSettings.initialBlockPlugin) {\n\n editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin;\n\n }\n\n if (userSettings.sanitizer) {\n\n editor.settings.sanitizer = userSettings.sanitizer;\n\n }\n\n editor.hideToolbar = userSettings.hideToolbar;\n\n editor.settings.placeholder = userSettings.placeholder || '';\n\n editor.nodes.holder = document.getElementById(userSettings.holderId || editor.settings.holderId);\n\n if (typeof editor.nodes.holder === undefined || editor.nodes.holder === null) {\n\n reject(Error(\"Holder wasn't found by ID: #\" + userSettings.holderId));\n\n } else {\n\n resolve();\n\n }\n\n });\n\n };\n\n /**\n * Logging method\n * @param type = ['log', 'info', 'warn']\n */\n core.log = function (msg, type, arg) {\n\n type = type || 'log';\n\n if (!arg) {\n\n arg = msg || 'undefined';\n msg = '[codex-editor]: %o';\n\n } else {\n\n msg = '[codex-editor]: ' + msg;\n\n }\n\n try{\n\n if ( 'console' in window && window.console[ type ] ) {\n\n if ( arg ) window.console[ type ]( msg, arg );\n else window.console[ type ]( msg );\n\n }\n\n }catch(e) {}\n\n };\n\n /**\n * @protected\n *\n * Helper for insert one element after another\n */\n core.insertAfter = function (target, element) {\n\n target.parentNode.insertBefore(element, target.nextSibling);\n\n };\n\n /**\n * @const\n *\n * Readable DOM-node types map\n */\n core.nodeTypes = {\n TAG : 1,\n TEXT : 3,\n COMMENT : 8,\n DOCUMENT_FRAGMENT: 11\n };\n\n /**\n * @const\n * Readable keys map\n */\n core.keys = { BACKSPACE: 8, TAB: 9, ENTER: 13, SHIFT: 16, CTRL: 17, ALT: 18, ESC: 27, SPACE: 32, LEFT: 37, UP: 38, DOWN: 40, RIGHT: 39, DELETE: 46, META: 91 };\n\n /**\n * @protected\n *\n * Check object for DOM node\n */\n core.isDomNode = function (el) {\n\n return el && typeof el === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG;\n\n };\n\n /**\n * Checks passed object for emptiness\n * @require ES5 - Object.keys\n * @param {object}\n */\n core.isEmpty = function ( obj ) {\n\n return Object.keys(obj).length === 0;\n\n };\n\n /**\n * Native Ajax\n * @param {String} settings.url - request URL\n * @param {function} settings.beforeSend - returned value will be passed as context to the Success, Error and Progress callbacks\n * @param {function} settings.success\n * @param {function} settings.progress\n */\n core.ajax = function (settings) {\n\n if (!settings || !settings.url) {\n\n return;\n\n }\n\n var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),\n encodedString,\n isFormData,\n prop;\n\n\n settings.async = true;\n settings.type = settings.type || 'GET';\n settings.data = settings.data || '';\n settings['content-type'] = settings['content-type'] || 'application/json; charset=utf-8';\n\n if (settings.type == 'GET' && settings.data) {\n\n settings.url = /\\?/.test(settings.url) ? settings.url + '&' + settings.data : settings.url + '?' + settings.data;\n\n } else {\n\n encodedString = '';\n for(prop in settings.data) {\n\n encodedString += (prop + '=' + encodeURIComponent(settings.data[prop]) + '&');\n\n }\n\n }\n\n if (settings.withCredentials) {\n\n XMLHTTP.withCredentials = true;\n\n }\n\n /**\n * Value returned in beforeSend funtion will be passed as context to the other response callbacks\n * If beforeSend returns false, AJAX will be blocked\n */\n let responseContext,\n beforeSendResult;\n\n if (typeof settings.beforeSend === 'function') {\n\n beforeSendResult = settings.beforeSend.call();\n\n if (beforeSendResult === false) {\n\n return;\n\n }\n\n }\n\n XMLHTTP.open( settings.type, settings.url, settings.async );\n\n /**\n * If we send FormData, we need no content-type header\n */\n isFormData = isFormData_(settings.data);\n\n if (!isFormData) {\n\n if (settings.type !== 'POST') {\n\n XMLHTTP.setRequestHeader('Content-type', settings['content-type']);\n\n } else {\n\n XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n\n }\n\n }\n\n XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\n responseContext = beforeSendResult || XMLHTTP;\n\n if (typeof settings.progress === 'function') {\n\n XMLHTTP.upload.onprogress = settings.progress.bind(responseContext);\n\n }\n\n XMLHTTP.onreadystatechange = function () {\n\n if (XMLHTTP.readyState === 4) {\n\n if (XMLHTTP.status === 200) {\n\n if (typeof settings.success === 'function') {\n\n settings.success.call(responseContext, XMLHTTP.responseText);\n\n }\n\n } else {\n\n if (typeof settings.error === 'function') {\n\n settings.error.call(responseContext, XMLHTTP.responseText, XMLHTTP.status);\n\n }\n\n }\n\n }\n\n };\n\n if (isFormData) {\n\n // Sending FormData\n XMLHTTP.send(settings.data);\n\n } else {\n\n // POST requests\n XMLHTTP.send(encodedString);\n\n }\n\n return XMLHTTP;\n\n };\n\n /**\n * Appends script to head of document\n * @return Promise\n */\n core.importScript = function (scriptPath, instanceName) {\n\n return new Promise(function (resolve, reject) {\n\n let script;\n\n /** Script is already loaded */\n if ( !instanceName ) {\n\n reject('Instance name is missed');\n\n } else if ( document.getElementById(editor.scriptPrefix + instanceName) ) {\n\n resolve(scriptPath);\n\n }\n\n script = document.createElement('SCRIPT');\n script.async = true;\n script.defer = true;\n script.id = editor.scriptPrefix + instanceName;\n\n script.onload = function () {\n\n resolve(scriptPath);\n\n };\n\n script.onerror = function () {\n\n reject(scriptPath);\n\n };\n\n script.src = scriptPath;\n document.head.appendChild(script);\n\n });\n\n };\n\n /**\n * Function for checking is it FormData object to send.\n * @param {Object} object to check\n * @return boolean\n */\n var isFormData_ = function (object) {\n\n return object instanceof FormData;\n\n };\n\n /**\n * Check block\n * @param target\n * @description Checks target is it native input\n */\n core.isNativeInput = function (target) {\n\n var nativeInputAreas = ['INPUT', 'TEXTAREA'];\n\n return nativeInputAreas.indexOf(target.tagName) != -1;\n\n };\n\n /**\n * Check if block is empty\n * We should check block textContent, child native inputs and some exceptions like IMG and IFRAME\n *\n * @param block\n * @returns {boolean}\n */\n core.isBlockEmpty = function (block) {\n\n const EXCEPTION_TAGS = ['IMG', 'IFRAME'];\n\n var nativeInputs = block.querySelectorAll('textarea, input'),\n nativeInputsAreEmpty = true,\n textContentIsEmpty = !block.textContent.trim();\n\n Array.prototype.forEach.call(nativeInputs, function (input) {\n\n if (input.type == 'textarea' || input.type == 'text') {\n\n nativeInputsAreEmpty = nativeInputsAreEmpty && !input.value.trim();\n\n }\n\n });\n\n return textContentIsEmpty && nativeInputsAreEmpty && !EXCEPTION_TAGS.includes(block.tagName);\n\n };\n\n\n return core;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/core.js","/**\n* Module working with plugins\n*/\nmodule.exports = (function () {\n\n let editor = codex.editor;\n\n /**\n * Initialize plugins before using\n * Ex. Load scripts or call some internal methods\n * @return Promise\n */\n function prepare() {\n\n return new Promise(function (resolve_, reject_) {\n\n Promise.resolve()\n\n /**\n * Compose a sequence of plugins that requires preparation\n */\n .then(function () {\n\n let pluginsRequiresPreparation = [],\n allPlugins = editor.tools;\n\n for ( let pluginName in allPlugins ) {\n\n let plugin = allPlugins[pluginName];\n\n if (plugin.prepare && typeof plugin.prepare != 'function' || !plugin.prepare) {\n\n continue;\n\n }\n\n pluginsRequiresPreparation.push(plugin);\n\n }\n\n /**\n * If no one passed plugins requires preparation, finish prepare() and go ahead\n */\n if (!pluginsRequiresPreparation.length) {\n\n resolve_();\n\n }\n\n return pluginsRequiresPreparation;\n\n })\n\n /** Wait plugins while they prepares */\n .then(waitAllPluginsPreparation_)\n\n .then(function () {\n\n editor.core.log('Plugins loaded', 'info');\n resolve_();\n\n }).catch(function (error) {\n\n reject_(error);\n\n });\n\n });\n\n }\n\n /**\n * @param {array} plugins - list of tools that requires preparation\n * @return {Promise} resolved while all plugins will be ready or failed\n */\n function waitAllPluginsPreparation_(plugins) {\n\n /**\n * @calls allPluginsProcessed__ when all plugins prepared or failed\n */\n return new Promise (function (allPluginsProcessed__) {\n\n /**\n * pluck each element from queue\n * First, send resolved Promise as previous value\n * Each plugins \"prepare\" method returns a Promise, that's why\n * reduce current element will not be able to continue while can't get\n * a resolved Promise\n *\n * If last plugin is \"prepared\" then go to the next stage of initialization\n */\n plugins.reduce(function (previousValue, plugin, iteration) {\n\n return previousValue.then(function () {\n\n /**\n * Wait till plugins prepared\n * @calls pluginIsReady__ when plugin is ready or failed\n */\n return new Promise ( function (pluginIsReady__) {\n\n callPluginsPrepareMethod_( plugin )\n\n .then( pluginIsReady__ )\n .then( function () {\n\n plugin.available = true;\n\n })\n\n .catch(function (error) {\n\n editor.core.log(`Plugin «${plugin.type}» was not loaded. Preparation failed because %o`, 'warn', error);\n plugin.available = false;\n plugin.loadingMessage = error;\n\n /** Go ahead even some plugin has problems */\n pluginIsReady__();\n\n })\n\n .then(function () {\n\n /** If last plugin has problems then just ignore and continue */\n if (iteration == plugins.length - 1) {\n\n allPluginsProcessed__();\n\n }\n\n });\n\n });\n\n });\n\n }, Promise.resolve() );\n\n });\n\n }\n\n var callPluginsPrepareMethod_ = function (plugin) {\n\n return plugin.prepare( plugin.config || {} );\n\n };\n\n return {\n prepare: prepare\n };\n\n}());\n\n\n// WEBPACK FOOTER //\n// ./modules/tools.js","/**\n * Codex Editor UI module\n *\n * @author Codex Team\n * @version 1.2.0\n */\n\nmodule.exports = (function (ui) {\n\n let editor = codex.editor;\n\n /**\n * Basic editor classnames\n */\n ui.className = {\n\n /**\n * @const {string} BLOCK_CLASSNAME - redactor blocks name\n */\n BLOCK_CLASSNAME : 'ce-block',\n\n /**\n * @const {String} wrapper for plugins content\n */\n BLOCK_CONTENT : 'ce-block__content',\n\n /**\n * @const {String} BLOCK_STRETCHED - makes block stretched\n */\n BLOCK_STRETCHED : 'ce-block--stretched',\n\n /**\n * @const {String} BLOCK_HIGHLIGHTED - adds background\n */\n BLOCK_HIGHLIGHTED : 'ce-block--focused',\n\n /**\n * @const {String} - for all default settings\n */\n SETTINGS_ITEM : 'ce-settings__item'\n\n };\n\n /**\n * @protected\n *\n * Making main interface\n */\n ui.prepare = function () {\n\n return new Promise(function (resolve) {\n\n let wrapper = editor.draw.wrapper(),\n redactor = editor.draw.redactor(),\n toolbar = makeToolBar_();\n\n wrapper.appendChild(toolbar);\n wrapper.appendChild(redactor);\n\n /** Save created ui-elements to static nodes state */\n editor.nodes.wrapper = wrapper;\n editor.nodes.redactor = redactor;\n\n /** Append editor wrapper with redactor zone into holder */\n editor.nodes.holder.appendChild(wrapper);\n\n resolve();\n\n })\n\n /** Add toolbox tools */\n .then(addTools_)\n\n /** Make container for inline toolbar */\n .then(makeInlineToolbar_)\n\n /** Add inline toolbar tools */\n .then(addInlineToolbarTools_)\n\n /** Draw wrapper for notifications */\n .then(makeNotificationHolder_)\n\n /** Add eventlisteners to redactor elements */\n .then(bindEvents_)\n\n .catch( function () {\n\n editor.core.log(\"Can't draw editor interface\");\n\n });\n\n };\n\n /**\n * @private\n * Draws inline toolbar zone\n */\n var makeInlineToolbar_ = function () {\n\n var container = editor.draw.inlineToolbar();\n\n /** Append to redactor new inline block */\n editor.nodes.inlineToolbar.wrapper = container;\n\n /** Draw toolbar buttons */\n editor.nodes.inlineToolbar.buttons = editor.draw.inlineToolbarButtons();\n\n /** Buttons action or settings */\n editor.nodes.inlineToolbar.actions = editor.draw.inlineToolbarActions();\n\n /** Append to inline toolbar buttons as part of it */\n editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.buttons);\n editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.actions);\n\n editor.nodes.wrapper.appendChild(editor.nodes.inlineToolbar.wrapper);\n\n };\n\n var makeToolBar_ = function () {\n\n let toolbar = editor.draw.toolbar(),\n blockButtons = makeToolbarSettings_(),\n toolbarContent = makeToolbarContent_();\n\n /** Appending first-level block buttons */\n toolbar.appendChild(blockButtons);\n\n /** Append toolbarContent to toolbar */\n toolbar.appendChild(toolbarContent);\n\n /** Make toolbar global */\n editor.nodes.toolbar = toolbar;\n\n return toolbar;\n\n };\n\n var makeToolbarContent_ = function () {\n\n let toolbarContent = editor.draw.toolbarContent(),\n toolbox = editor.draw.toolbox(),\n plusButton = editor.draw.plusButton();\n\n /** Append plus button */\n toolbarContent.appendChild(plusButton);\n\n /** Appending toolbar tools */\n toolbarContent.appendChild(toolbox);\n\n /** Make Toolbox and plusButton global */\n editor.nodes.toolbox = toolbox;\n editor.nodes.plusButton = plusButton;\n\n return toolbarContent;\n\n };\n\n var makeToolbarSettings_ = function () {\n\n let blockSettings = editor.draw.blockSettings(),\n blockButtons = editor.draw.blockButtons(),\n defaultSettings = editor.draw.defaultSettings(),\n showSettingsButton = editor.draw.settingsButton(),\n showTrashButton = editor.toolbar.settings.makeRemoveBlockButton(),\n pluginSettings = editor.draw.pluginsSettings();\n\n /** Add default and plugins settings */\n blockSettings.appendChild(pluginSettings);\n blockSettings.appendChild(defaultSettings);\n\n /**\n * Make blocks buttons\n * This block contains settings button and remove block button\n */\n blockButtons.appendChild(showSettingsButton);\n blockButtons.appendChild(showTrashButton);\n blockButtons.appendChild(blockSettings);\n\n /** Make BlockSettings, PluginSettings, DefaultSettings global */\n editor.nodes.blockSettings = blockSettings;\n editor.nodes.pluginSettings = pluginSettings;\n editor.nodes.defaultSettings = defaultSettings;\n editor.nodes.showSettingsButton = showSettingsButton;\n editor.nodes.showTrashButton = showTrashButton;\n\n return blockButtons;\n\n };\n\n /** Draw notifications holder */\n var makeNotificationHolder_ = function () {\n\n /** Append block with notifications to the document */\n editor.nodes.notifications = editor.notifications.createHolder();\n\n };\n\n /**\n * @private\n * Append tools passed in editor.tools\n */\n var addTools_ = function () {\n\n var tool,\n toolName,\n toolButton;\n\n for ( toolName in editor.settings.tools ) {\n\n tool = editor.settings.tools[toolName];\n\n editor.tools[toolName] = tool;\n\n if (!tool.iconClassname && tool.displayInToolbox) {\n\n editor.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', toolName);\n continue;\n\n }\n\n if (typeof tool.render != 'function') {\n\n editor.core.log('render method missed. Tool %o skipped', 'warn', toolName);\n continue;\n\n }\n\n if (!tool.displayInToolbox) {\n\n continue;\n\n } else {\n\n /** if tools is for toolbox */\n toolButton = editor.draw.toolbarButton(toolName, tool.iconClassname);\n\n editor.nodes.toolbox.appendChild(toolButton);\n\n editor.nodes.toolbarButtons[toolName] = toolButton;\n\n }\n\n }\n\n };\n\n var addInlineToolbarTools_ = function () {\n\n var tools = {\n\n bold: {\n icon : 'ce-icon-bold',\n command : 'bold'\n },\n\n italic: {\n icon : 'ce-icon-italic',\n command : 'italic'\n },\n\n link: {\n icon : 'ce-icon-link',\n command : 'createLink'\n }\n };\n\n var toolButton,\n tool;\n\n for(var name in tools) {\n\n tool = tools[name];\n\n toolButton = editor.draw.toolbarButtonInline(name, tool.icon);\n\n editor.nodes.inlineToolbar.buttons.appendChild(toolButton);\n /**\n * Add callbacks to this buttons\n */\n editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command);\n\n }\n\n };\n\n /**\n * @private\n * Bind editor UI events\n */\n var bindEvents_ = function () {\n\n editor.core.log('ui.bindEvents fired', 'info');\n\n // window.addEventListener('error', function (errorMsg, url, lineNumber) {\n // editor.notifications.errorThrown(errorMsg, event);\n // }, false );\n\n /** All keydowns on Document */\n editor.listeners.add(document, 'keydown', editor.callback.globalKeydown, false);\n\n /** All keydowns on Redactor zone */\n editor.listeners.add(editor.nodes.redactor, 'keydown', editor.callback.redactorKeyDown, false);\n\n /** All keydowns on Document */\n editor.listeners.add(document, 'keyup', editor.callback.globalKeyup, false );\n\n /**\n * Mouse click to radactor\n */\n editor.listeners.add(editor.nodes.redactor, 'click', editor.callback.redactorClicked, false );\n\n /**\n * Clicks to the Plus button\n */\n editor.listeners.add(editor.nodes.plusButton, 'click', editor.callback.plusButtonClicked, false);\n\n /**\n * Clicks to SETTINGS button in toolbar\n */\n editor.listeners.add(editor.nodes.showSettingsButton, 'click', editor.callback.showSettingsButtonClicked, false );\n\n /** Bind click listeners on toolbar buttons */\n for (var button in editor.nodes.toolbarButtons) {\n\n editor.listeners.add(editor.nodes.toolbarButtons[button], 'click', editor.callback.toolbarButtonClicked, false);\n\n }\n\n };\n\n ui.addBlockHandlers = function (block) {\n\n if (!block) return;\n\n /**\n * Block keydowns\n */\n editor.listeners.add(block, 'keydown', editor.callback.blockKeydown, false);\n\n /**\n * Pasting content from another source\n * We have two type of sanitization\n * First - uses deep-first search algorithm to get sub nodes,\n * sanitizes whole Block_content and replaces cleared nodes\n * This method is deprecated\n * Method is used in editor.callback.blockPaste(event)\n *\n * Secont - uses Mutation observer.\n * Observer \"observe\" DOM changes and send changings to callback.\n * Callback gets changed node, not whole Block_content.\n * Inserted or changed node, which we've gotten have been cleared and replaced with diry node\n *\n * Method is used in editor.callback.blockPasteViaSanitize(event)\n *\n * @uses html-janitor\n * @example editor.callback.blockPasteViaSanitize(event), the second method.\n *\n */\n editor.listeners.add(block, 'paste', editor.paste.blockPasteCallback, false);\n\n /**\n * Show inline toolbar for selected text\n */\n editor.listeners.add(block, 'mouseup', editor.toolbar.inline.show, false);\n editor.listeners.add(block, 'keyup', editor.toolbar.inline.show, false);\n\n };\n\n /** getting all contenteditable elements */\n ui.saveInputs = function () {\n\n var redactor = editor.nodes.redactor;\n\n editor.state.inputs = [];\n\n /** Save all inputs in global variable state */\n var inputs = redactor.querySelectorAll('[contenteditable], input, textarea');\n\n Array.prototype.map.call(inputs, function (current) {\n\n if (!current.type || current.type == 'text' || current.type == 'textarea') {\n\n editor.state.inputs.push(current);\n\n }\n\n });\n\n };\n\n /**\n * Adds first initial block on empty redactor\n */\n ui.addInitialBlock = function () {\n\n var initialBlockType = editor.settings.initialBlockPlugin,\n initialBlock;\n\n if ( !editor.tools[initialBlockType] ) {\n\n editor.core.log('Plugin %o was not implemented and can\\'t be used as initial block', 'warn', initialBlockType);\n return;\n\n }\n\n initialBlock = editor.tools[initialBlockType].render();\n\n initialBlock.setAttribute('data-placeholder', editor.settings.placeholder);\n\n editor.content.insertBlock({\n type : initialBlockType,\n block : initialBlock\n });\n\n editor.content.workingNodeChanged(initialBlock);\n\n };\n\n ui.setInlineToolbarButtonBehaviour = function (button, type) {\n\n editor.listeners.add(button, 'mousedown', function (event) {\n\n editor.toolbar.inline.toolClicked(event, type);\n\n }, false);\n\n };\n\n return ui;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/ui.js","/**\n *\n * Codex.Editor Transport Module\n *\n * @copyright 2017 Codex-Team\n * @version 1.2.0\n */\n\nmodule.exports = (function (transport) {\n\n let editor = codex.editor;\n\n\n /**\n * @private {Object} current XmlHttpRequest instance\n */\n var currentRequest = null;\n\n\n /**\n * @type {null} | {DOMElement} input - keeps input element in memory\n */\n transport.input = null;\n\n /**\n * @property {Object} arguments - keep plugin settings and defined callbacks\n */\n transport.arguments = null;\n\n /**\n * Prepares input element where will be files\n */\n transport.prepare = function () {\n\n let input = editor.draw.node( 'INPUT', '', { type : 'file' } );\n\n editor.listeners.add(input, 'change', editor.transport.fileSelected);\n editor.transport.input = input;\n\n };\n\n /** Clear input when files is uploaded */\n transport.clearInput = function () {\n\n /** Remove old input */\n transport.input = null;\n\n /** Prepare new one */\n transport.prepare();\n\n };\n\n /**\n * Callback for file selection\n * @param {Event} event\n */\n transport.fileSelected = function () {\n\n var input = this,\n i,\n files = input.files,\n formData = new FormData();\n\n if (editor.transport.arguments.multiple === true) {\n\n for ( i = 0; i < files.length; i++) {\n\n formData.append('files[]', files[i], files[i].name);\n\n }\n\n } else {\n\n formData.append('files', files[0], files[0].name);\n\n }\n\n currentRequest = editor.core.ajax({\n type : 'POST',\n data : formData,\n url : editor.transport.arguments.url,\n beforeSend : editor.transport.arguments.beforeSend,\n success : editor.transport.arguments.success,\n error : editor.transport.arguments.error,\n progress : editor.transport.arguments.progress\n });\n\n /** Clear input */\n transport.clearInput();\n\n };\n\n /**\n * Use plugin callbacks\n * @protected\n *\n * @param {Object} args - can have :\n * @param {String} args.url - fetch URL\n * @param {Function} args.beforeSend - function calls before sending ajax\n * @param {Function} args.success - success callback\n * @param {Function} args.error - on error handler\n * @param {Function} args.progress - xhr onprogress handler\n * @param {Boolean} args.multiple - allow select several files\n * @param {String} args.accept - adds accept attribute\n */\n transport.selectAndUpload = function (args) {\n\n transport.arguments = args;\n\n if ( args.multiple === true) {\n\n transport.input.setAttribute('multiple', 'multiple');\n\n }\n\n if ( args.accept ) {\n\n transport.input.setAttribute('accept', args.accept);\n\n }\n\n transport.input.click();\n\n };\n\n transport.abort = function () {\n\n currentRequest.abort();\n\n currentRequest = null;\n\n };\n\n return transport;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/transport.js","/**\n * Codex Editor Renderer Module\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (renderer) {\n\n let editor = codex.editor;\n\n /**\n * Asyncronously parses input JSON to redactor blocks\n */\n renderer.makeBlocksFromData = function () {\n\n /**\n * If redactor is empty, add first paragraph to start writing\n */\n if (editor.core.isEmpty(editor.state.blocks) || !editor.state.blocks.items.length) {\n\n editor.ui.addInitialBlock();\n return;\n\n }\n\n Promise.resolve()\n\n /** First, get JSON from state */\n .then(function () {\n\n return editor.state.blocks;\n\n })\n\n /** Then, start to iterate they */\n .then(editor.renderer.appendBlocks)\n\n /** Write log if something goes wrong */\n .catch(function (error) {\n\n editor.core.log('Error while parsing JSON: %o', 'error', error);\n\n });\n\n };\n\n /**\n * Parses JSON to blocks\n * @param {object} data\n * @return Primise -> nodeList\n */\n renderer.appendBlocks = function (data) {\n\n var blocks = data.items;\n\n /**\n * Sequence of one-by-one blocks appending\n * Uses to save blocks order after async-handler\n */\n var nodeSequence = Promise.resolve();\n\n for (var index = 0; index < blocks.length ; index++ ) {\n\n /** Add node to sequence at specified index */\n editor.renderer.appendNodeAtIndex(nodeSequence, blocks, index);\n\n }\n\n };\n\n /**\n * Append node at specified index\n */\n renderer.appendNodeAtIndex = function (nodeSequence, blocks, index) {\n\n /** We need to append node to sequence */\n nodeSequence\n\n /** first, get node async-aware */\n .then(function () {\n\n return editor.renderer.getNodeAsync(blocks, index);\n\n })\n\n /**\n * second, compose editor-block from JSON object\n */\n .then(editor.renderer.createBlockFromData)\n\n /**\n * now insert block to redactor\n */\n .then(function (blockData) {\n\n /**\n * blockData has 'block', 'type' and 'stretched' information\n */\n editor.content.insertBlock(blockData);\n\n /** Pass created block to next step */\n return blockData.block;\n\n })\n\n /** Log if something wrong with node */\n .catch(function (error) {\n\n editor.core.log('Node skipped while parsing because %o', 'error', error);\n\n });\n\n };\n\n /**\n * Asynchronously returns block data from blocksList by index\n * @return Promise to node\n */\n renderer.getNodeAsync = function (blocksList, index) {\n\n return Promise.resolve().then(function () {\n\n return {\n tool : blocksList[index],\n position : index\n };\n\n });\n\n };\n\n /**\n * Creates editor block by JSON-data\n *\n * @uses render method of each plugin\n *\n * @param {Object} toolData.tool\n * { header : {\n * text: '',\n * type: 'H3', ...\n * }\n * }\n * @param {Number} toolData.position - index in input-blocks array\n * @return {Object} with type and Element\n */\n renderer.createBlockFromData = function ( toolData ) {\n\n /** New parser */\n var block,\n tool = toolData.tool,\n pluginName = tool.type;\n\n /** Get first key of object that stores plugin name */\n // for (var pluginName in blockData) break;\n\n /** Check for plugin existance */\n if (!editor.tools[pluginName]) {\n\n throw Error(`Plugin «${pluginName}» not found`);\n\n }\n\n /** Check for plugin having render method */\n if (typeof editor.tools[pluginName].render != 'function') {\n\n throw Error(`Plugin «${pluginName}» must have «render» method`);\n\n }\n\n if ( editor.tools[pluginName].available === false ) {\n\n block = editor.draw.unavailableBlock();\n\n block.innerHTML = editor.tools[pluginName].loadingMessage;\n\n /**\n * Saver will extract data from initial block data by position in array\n */\n block.dataset.inputPosition = toolData.position;\n\n } else {\n\n /** New Parser */\n block = editor.tools[pluginName].render(tool.data);\n\n }\n\n /** is first-level block stretched */\n var stretched = editor.tools[pluginName].isStretched || false;\n\n /** Retrun type and block */\n return {\n type : pluginName,\n block : block,\n stretched : stretched\n };\n\n };\n\n return renderer;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/renderer.js","/**\n * Codex Editor Saver\n *\n * @author Codex Team\n * @version 1.1.0\n */\n\nmodule.exports = (function (saver) {\n\n let editor = codex.editor;\n\n /**\n * @public\n * Save blocks\n */\n saver.save = function () {\n\n /** Save html content of redactor to memory */\n editor.state.html = editor.nodes.redactor.innerHTML;\n\n /** Clean jsonOutput state */\n editor.state.jsonOutput = [];\n\n return saveBlocks(editor.nodes.redactor.childNodes);\n\n };\n\n /**\n * @private\n * Save each block data\n *\n * @param blocks\n * @returns {Promise.}\n */\n let saveBlocks = function (blocks) {\n\n let data = [];\n\n for(let index = 0; index < blocks.length; index++) {\n\n data.push(getBlockData(blocks[index]));\n\n }\n\n return Promise.all(data)\n .then(makeOutput)\n .catch(editor.core.log);\n\n };\n\n /** Save and validate block data */\n let getBlockData = function (block) {\n\n return saveBlockData(block)\n .then(validateBlockData)\n .catch(editor.core.log);\n\n };\n\n /**\n * @private\n * Call block`s plugin save method and return saved data\n *\n * @param block\n * @returns {Object}\n */\n let saveBlockData = function (block) {\n\n let pluginName = block.dataset.tool;\n\n /** Check for plugin existence */\n if (!editor.tools[pluginName]) {\n\n editor.core.log(`Plugin «${pluginName}» not found`, 'error');\n return {data: null, pluginName: null};\n\n }\n\n /** Check for plugin having save method */\n if (typeof editor.tools[pluginName].save !== 'function') {\n\n editor.core.log(`Plugin «${pluginName}» must have save method`, 'error');\n return {data: null, pluginName: null};\n\n }\n\n /** Result saver */\n let blockContent = block.childNodes[0],\n pluginsContent = blockContent.childNodes[0],\n position = pluginsContent.dataset.inputPosition;\n\n /** If plugin wasn't available then return data from cache */\n if ( editor.tools[pluginName].available === false ) {\n\n return Promise.resolve({data: codex.editor.state.blocks.items[position].data, pluginName});\n\n }\n\n return Promise.resolve(pluginsContent)\n .then(editor.tools[pluginName].save)\n .then(data => Object({data, pluginName}));\n\n };\n\n /**\n * Call plugin`s validate method. Return false if validation failed\n *\n * @param data\n * @param pluginName\n * @returns {Object|Boolean}\n */\n let validateBlockData = function ({data, pluginName}) {\n\n if (!data || !pluginName) {\n\n return false;\n\n }\n\n if (editor.tools[pluginName].validate) {\n\n let result = editor.tools[pluginName].validate(data);\n\n /**\n * Do not allow invalid data\n */\n if (!result) {\n\n return false;\n\n }\n\n }\n\n return {data, pluginName};\n\n\n };\n\n /**\n * Compile article output\n *\n * @param savedData\n * @returns {{time: number, version, items: (*|Array)}}\n */\n let makeOutput = function (savedData) {\n\n savedData = savedData.filter(blockData => blockData);\n\n let items = savedData.map(blockData => Object({type: blockData.pluginName, data: blockData.data}));\n\n editor.state.jsonOutput = items;\n\n return {\n id: editor.state.blocks.id || null,\n time: +new Date(),\n version: editor.version,\n items\n };\n\n };\n\n return saver;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/saver.js","/**\n * Codex Editor Content Module\n * Works with DOM\n *\n * @module Codex Editor content module\n *\n * @author Codex Team\n * @version 1.3.13\n *\n * @description Module works with Elements that have been appended to the main DOM\n */\n\nmodule.exports = (function (content) {\n\n let editor = codex.editor;\n\n /**\n * Links to current active block\n * @type {null | Element}\n */\n content.currentNode = null;\n\n /**\n * clicked in redactor area\n * @type {null | Boolean}\n */\n content.editorAreaHightlighted = null;\n\n /**\n * @deprecated\n * Synchronizes redactor with original textarea\n */\n content.sync = function () {\n\n editor.core.log('syncing...');\n\n /**\n * Save redactor content to editor.state\n */\n editor.state.html = editor.nodes.redactor.innerHTML;\n\n };\n\n /**\n * Appends background to the block\n *\n * @description add CSS class to highlight visually first-level block area\n */\n content.markBlock = function () {\n\n editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED);\n\n };\n\n /**\n * Clear background\n *\n * @description clears styles that highlights block\n */\n content.clearMark = function () {\n\n if (editor.content.currentNode) {\n\n editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED);\n\n }\n\n };\n\n /**\n * Finds first-level block\n *\n * @param {Element} node - selected or clicked in redactors area node\n * @protected\n *\n * @description looks for first-level block.\n * gets parent while node is not first-level\n */\n content.getFirstLevelBlock = function (node) {\n\n if (!editor.core.isDomNode(node)) {\n\n node = node.parentNode;\n\n }\n\n if (node === editor.nodes.redactor || node === document.body) {\n\n return null;\n\n } else {\n\n while(!node.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\n\n node = node.parentNode;\n\n }\n\n return node;\n\n }\n\n };\n\n /**\n * Trigger this event when working node changed\n * @param {Element} targetNode - first-level of this node will be current\n * @protected\n *\n * @description If targetNode is first-level then we set it as current else we look for parents to find first-level\n */\n content.workingNodeChanged = function (targetNode) {\n\n /** Clear background from previous marked block before we change */\n editor.content.clearMark();\n\n if (!targetNode) {\n\n return;\n\n }\n\n content.currentNode = content.getFirstLevelBlock(targetNode);\n\n };\n\n /**\n * Replaces one redactor block with another\n * @protected\n * @param {Element} targetBlock - block to replace. Mostly currentNode.\n * @param {Element} newBlock\n * @param {string} newBlockType - type of new block; we need to store it to data-attribute\n *\n * [!] Function does not saves old block content.\n * You can get it manually and pass with newBlock.innerHTML\n */\n content.replaceBlock = function (targetBlock, newBlock) {\n\n if (!targetBlock || !newBlock) {\n\n editor.core.log('replaceBlock: missed params');\n return;\n\n }\n\n /** If target-block is not a frist-level block, then we iterate parents to find it */\n while(!targetBlock.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\n\n targetBlock = targetBlock.parentNode;\n\n }\n\n /** Replacing */\n editor.nodes.redactor.replaceChild(newBlock, targetBlock);\n\n /**\n * Set new node as current\n */\n editor.content.workingNodeChanged(newBlock);\n\n /**\n * Add block handlers\n */\n editor.ui.addBlockHandlers(newBlock);\n\n /**\n * Save changes\n */\n editor.ui.saveInputs();\n\n };\n\n /**\n * @protected\n *\n * Inserts new block to redactor\n * Wrapps block into a DIV with BLOCK_CLASSNAME class\n *\n * @param blockData {object}\n * @param blockData.block {Element} element with block content\n * @param blockData.type {string} block plugin\n * @param needPlaceCaret {bool} pass true to set caret in new block\n *\n */\n content.insertBlock = function ( blockData, needPlaceCaret ) {\n\n var workingBlock = editor.content.currentNode,\n newBlockContent = blockData.block,\n blockType = blockData.type,\n isStretched = blockData.stretched;\n\n var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched);\n\n if (workingBlock) {\n\n editor.core.insertAfter(workingBlock, newBlock);\n\n } else {\n\n /**\n * If redactor is empty, append as first child\n */\n editor.nodes.redactor.appendChild(newBlock);\n\n }\n\n /**\n * Block handler\n */\n editor.ui.addBlockHandlers(newBlock);\n\n /**\n * Set new node as current\n */\n editor.content.workingNodeChanged(newBlock);\n\n /**\n * Save changes\n */\n editor.ui.saveInputs();\n\n\n if ( needPlaceCaret ) {\n\n /**\n * If we don't know input index then we set default value -1\n */\n var currentInputIndex = editor.caret.getCurrentInputIndex() || -1;\n\n\n if (currentInputIndex == -1) {\n\n\n var editableElement = newBlock.querySelector('[contenteditable]'),\n emptyText = document.createTextNode('');\n\n editableElement.appendChild(emptyText);\n editor.caret.set(editableElement, 0, 0);\n\n editor.toolbar.move();\n editor.toolbar.showPlusButton();\n\n\n } else {\n\n if (currentInputIndex === editor.state.inputs.length - 1)\n return;\n\n /** Timeout for browsers execution */\n window.setTimeout(function () {\n\n /** Setting to the new input */\n editor.caret.setToNextBlock(currentInputIndex);\n editor.toolbar.move();\n editor.toolbar.open();\n\n }, 10);\n\n }\n\n }\n\n /**\n * Block is inserted, wait for new click that defined focusing on editors area\n * @type {boolean}\n */\n content.editorAreaHightlighted = false;\n\n };\n\n /**\n * Replaces blocks with saving content\n * @protected\n * @param {Element} noteToReplace\n * @param {Element} newNode\n * @param {Element} blockType\n */\n content.switchBlock = function (blockToReplace, newBlock, tool) {\n\n tool = tool || editor.content.currentNode.dataset.tool;\n var newBlockComposed = composeNewBlock_(newBlock, tool);\n\n /** Replacing */\n editor.content.replaceBlock(blockToReplace, newBlockComposed);\n\n /** Save new Inputs when block is changed */\n editor.ui.saveInputs();\n\n };\n\n /**\n * Iterates between child noted and looking for #text node on deepest level\n * @protected\n *\n * @param {Element} block - node where find\n * @param {int} postiton - starting postion\n * Example: childNodex.length to find from the end\n * or 0 to find from the start\n * @return {Text} block\n * @uses DFS\n */\n content.getDeepestTextNodeFromPosition = function (block, position) {\n\n /**\n * Clear Block from empty and useless spaces with trim.\n * Such nodes we should remove\n */\n var blockChilds = block.childNodes,\n index,\n node,\n text;\n\n for(index = 0; index < blockChilds.length; index++) {\n\n node = blockChilds[index];\n\n if (node.nodeType == editor.core.nodeTypes.TEXT) {\n\n text = node.textContent.trim();\n\n /** Text is empty. We should remove this child from node before we start DFS\n * decrease the quantity of childs.\n */\n if (text === '') {\n\n block.removeChild(node);\n position--;\n\n }\n\n }\n\n }\n\n if (block.childNodes.length === 0) {\n\n return document.createTextNode('');\n\n }\n\n /** Setting default position when we deleted all empty nodes */\n if ( position < 0 )\n position = 1;\n\n var lookingFromStart = false;\n\n /** For looking from START */\n if (position === 0) {\n\n lookingFromStart = true;\n position = 1;\n\n }\n\n while ( position ) {\n\n /** initial verticle of node. */\n if ( lookingFromStart ) {\n\n block = block.childNodes[0];\n\n } else {\n\n block = block.childNodes[position - 1];\n\n }\n\n if ( block.nodeType == editor.core.nodeTypes.TAG ) {\n\n position = block.childNodes.length;\n\n } else if (block.nodeType == editor.core.nodeTypes.TEXT ) {\n\n position = 0;\n\n }\n\n }\n\n return block;\n\n };\n\n /**\n * @private\n * @param {Element} block - current plugins render\n * @param {String} tool - plugins name\n * @param {Boolean} isStretched - make stretched block or not\n *\n * @description adds necessary information to wrap new created block by first-level holder\n */\n var composeNewBlock_ = function (block, tool, isStretched) {\n\n var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}),\n blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {});\n\n blockContent.appendChild(block);\n newBlock.appendChild(blockContent);\n\n if (isStretched) {\n\n blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED);\n\n }\n\n newBlock.dataset.tool = tool;\n return newBlock;\n\n };\n\n /**\n * Returns Range object of current selection\n * @protected\n */\n content.getRange = function () {\n\n var selection = window.getSelection().getRangeAt(0);\n\n return selection;\n\n };\n\n /**\n * Divides block in two blocks (after and before caret)\n *\n * @protected\n * @param {int} inputIndex - target input index\n *\n * @description splits current input content to the separate blocks\n * When enter is pressed among the words, that text will be splited.\n */\n content.splitBlock = function (inputIndex) {\n\n var selection = window.getSelection(),\n anchorNode = selection.anchorNode,\n anchorNodeText = anchorNode.textContent,\n caretOffset = selection.anchorOffset,\n textBeforeCaret,\n textNodeBeforeCaret,\n textAfterCaret,\n textNodeAfterCaret;\n\n var currentBlock = editor.content.currentNode.querySelector('[contentEditable]');\n\n\n textBeforeCaret = anchorNodeText.substring(0, caretOffset);\n textAfterCaret = anchorNodeText.substring(caretOffset);\n\n textNodeBeforeCaret = document.createTextNode(textBeforeCaret);\n\n if (textAfterCaret) {\n\n textNodeAfterCaret = document.createTextNode(textAfterCaret);\n\n }\n\n var previousChilds = [],\n nextChilds = [],\n reachedCurrent = false;\n\n if (textNodeAfterCaret) {\n\n nextChilds.push(textNodeAfterCaret);\n\n }\n\n for ( var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) {\n\n if ( child != anchorNode ) {\n\n if ( !reachedCurrent ) {\n\n previousChilds.push(child);\n\n } else {\n\n nextChilds.push(child);\n\n }\n\n } else {\n\n reachedCurrent = true;\n\n }\n\n }\n\n /** Clear current input */\n editor.state.inputs[inputIndex].innerHTML = '';\n\n /**\n * Append all childs founded before anchorNode\n */\n var previousChildsLength = previousChilds.length;\n\n for(i = 0; i < previousChildsLength; i++) {\n\n editor.state.inputs[inputIndex].appendChild(previousChilds[i]);\n\n }\n\n editor.state.inputs[inputIndex].appendChild(textNodeBeforeCaret);\n\n /**\n * Append text node which is after caret\n */\n var nextChildsLength = nextChilds.length,\n newNode = document.createElement('div');\n\n for(i = 0; i < nextChildsLength; i++) {\n\n newNode.appendChild(nextChilds[i]);\n\n }\n\n newNode = newNode.innerHTML;\n\n /** This type of block creates when enter is pressed */\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\n /**\n * Make new paragraph with text after caret\n */\n editor.content.insertBlock({\n type : NEW_BLOCK_TYPE,\n block : editor.tools[NEW_BLOCK_TYPE].render({\n text : newNode\n })\n }, true );\n\n };\n\n /**\n * Merges two blocks — current and target\n * If target index is not exist, then previous will be as target\n *\n * @protected\n * @param {int} currentInputIndex\n * @param {int} targetInputIndex\n *\n * @description gets two inputs indexes and merges into one\n */\n content.mergeBlocks = function (currentInputIndex, targetInputIndex) {\n\n /** If current input index is zero, then prevent method execution */\n if (currentInputIndex === 0) {\n\n return;\n\n }\n\n var targetInput,\n currentInputContent = editor.state.inputs[currentInputIndex].innerHTML;\n\n if (!targetInputIndex) {\n\n targetInput = editor.state.inputs[currentInputIndex - 1];\n\n } else {\n\n targetInput = editor.state.inputs[targetInputIndex];\n\n }\n\n targetInput.innerHTML += currentInputContent;\n\n };\n\n /**\n * Iterates all right siblings and parents, which has right siblings\n * while it does not reached the first-level block\n *\n * @param {Element} node\n * @return {boolean}\n */\n content.isLastNode = function (node) {\n\n // console.log('погнали перебор родителей');\n\n var allChecked = false;\n\n while ( !allChecked ) {\n\n // console.log('Смотрим на %o', node);\n // console.log('Проверим, пустые ли соседи справа');\n\n if ( !allSiblingsEmpty_(node) ) {\n\n // console.log('Есть непустые соседи. Узел не последний. Выходим.');\n return false;\n\n }\n\n node = node.parentNode;\n\n /**\n * Проверяем родителей до тех пор, пока не найдем блок первого уровня\n */\n if ( node.classList.contains(editor.ui.className.BLOCK_CONTENT) ) {\n\n allChecked = true;\n\n }\n\n }\n\n return true;\n\n };\n\n /**\n * Checks if all element right siblings is empty\n * @param node\n */\n var allSiblingsEmpty_ = function (node) {\n\n /**\n * Нужно убедиться, что после пустого соседа ничего нет\n */\n var sibling = node.nextSibling;\n\n while ( sibling ) {\n\n if (sibling.textContent.length) {\n\n return false;\n\n }\n\n sibling = sibling.nextSibling;\n\n }\n\n return true;\n\n };\n\n /**\n * @public\n *\n * @param {string} htmlData - html content as string\n * @param {string} plainData - plain text\n * @return {string} - html content as string\n */\n content.wrapTextWithParagraphs = function (htmlData, plainData) {\n\n if (!htmlData.trim()) {\n\n return wrapPlainTextWithParagraphs(plainData);\n\n }\n\n var wrapper = document.createElement('DIV'),\n newWrapper = document.createElement('DIV'),\n i,\n paragraph,\n firstLevelBlocks = ['DIV', 'P'],\n blockTyped,\n node;\n\n /**\n * Make HTML Element to Wrap Text\n * It allows us to work with input data as HTML content\n */\n wrapper.innerHTML = htmlData;\n paragraph = document.createElement('P');\n\n for (i = 0; i < wrapper.childNodes.length; i++) {\n\n node = wrapper.childNodes[i];\n\n blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1;\n\n /**\n * If node is first-levet\n * we add this node to our new wrapper\n */\n if ( blockTyped ) {\n\n /**\n * If we had splitted inline nodes to paragraph before\n */\n if ( paragraph.childNodes.length ) {\n\n newWrapper.appendChild(paragraph.cloneNode(true));\n\n /** empty paragraph */\n paragraph = null;\n paragraph = document.createElement('P');\n\n }\n\n newWrapper.appendChild(node.cloneNode(true));\n\n } else {\n\n /** Collect all inline nodes to one as paragraph */\n paragraph.appendChild(node.cloneNode(true));\n\n /** if node is last we should append this node to paragraph and paragraph to new wrapper */\n if ( i == wrapper.childNodes.length - 1 ) {\n\n newWrapper.appendChild(paragraph.cloneNode(true));\n\n }\n\n }\n\n }\n\n return newWrapper.innerHTML;\n\n };\n\n /**\n * Splits strings on new line and wraps paragraphs with

    tag\n * @param plainText\n * @returns {string}\n */\n var wrapPlainTextWithParagraphs = function (plainText) {\n\n if (!plainText) return '';\n\n return '

    ' + plainText.split('\\n\\n').join('

    ') + '

    ';\n\n };\n\n /**\n * Finds closest Contenteditable parent from Element\n * @param {Element} node element looking from\n * @return {Element} node contenteditable\n */\n content.getEditableParent = function (node) {\n\n while (node && node.contentEditable != 'true') {\n\n node = node.parentNode;\n\n }\n\n return node;\n\n };\n\n /**\n * Clear editors content\n *\n * @param {Boolean} all — if true, delete all article data (content, id, etc.)\n */\n content.clear = function (all) {\n\n editor.nodes.redactor.innerHTML = '';\n editor.content.sync();\n editor.ui.saveInputs();\n if (all) {\n\n editor.state.blocks = {};\n\n } else if (editor.state.blocks) {\n\n editor.state.blocks.items = [];\n\n }\n\n editor.content.currentNode = null;\n\n };\n\n /**\n *\n * Load new data to editor\n * If editor is not empty, just append articleData.items\n *\n * @param articleData.items\n */\n content.load = function (articleData) {\n\n var currentContent = Object.assign({}, editor.state.blocks);\n\n editor.content.clear();\n\n if (!Object.keys(currentContent).length) {\n\n editor.state.blocks = articleData;\n\n } else if (!currentContent.items) {\n\n currentContent.items = articleData.items;\n editor.state.blocks = currentContent;\n\n } else {\n\n currentContent.items = currentContent.items.concat(articleData.items);\n editor.state.blocks = currentContent;\n\n }\n\n editor.renderer.makeBlocksFromData();\n\n };\n\n return content;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/content.js","/**\n * Codex Editor toolbar module\n *\n * Contains:\n * - Inline toolbox\n * - Toolbox within plus button\n * - Settings section\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (toolbar) {\n\n let editor = codex.editor;\n\n toolbar.settings = require('./settings');\n toolbar.inline = require('./inline');\n toolbar.toolbox = require('./toolbox');\n\n /**\n * Margin between focused node and toolbar\n */\n toolbar.defaultToolbarHeight = 49;\n\n toolbar.defaultOffset = 34;\n\n toolbar.opened = false;\n\n toolbar.current = null;\n\n /**\n * @protected\n */\n toolbar.open = function () {\n\n if (editor.hideToolbar) {\n\n return;\n\n }\n\n let toolType = editor.content.currentNode.dataset.tool;\n\n if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings ) {\n\n editor.nodes.showSettingsButton.classList.add('hide');\n\n } else {\n\n editor.nodes.showSettingsButton.classList.remove('hide');\n\n }\n\n editor.nodes.toolbar.classList.add('opened');\n this.opened = true;\n\n };\n\n /**\n * @protected\n */\n toolbar.close = function () {\n\n editor.nodes.toolbar.classList.remove('opened');\n\n toolbar.opened = false;\n toolbar.current = null;\n\n for (var button in editor.nodes.toolbarButtons) {\n\n editor.nodes.toolbarButtons[button].classList.remove('selected');\n\n }\n\n /** Close toolbox when toolbar is not displayed */\n editor.toolbar.toolbox.close();\n editor.toolbar.settings.close();\n\n };\n\n toolbar.toggle = function () {\n\n if ( !this.opened ) {\n\n this.open();\n\n } else {\n\n this.close();\n\n }\n\n };\n\n toolbar.hidePlusButton = function () {\n\n editor.nodes.plusButton.classList.add('hide');\n\n };\n\n toolbar.showPlusButton = function () {\n\n editor.nodes.plusButton.classList.remove('hide');\n\n };\n\n /**\n * Moving toolbar to the specified node\n */\n toolbar.move = function () {\n\n /** Close Toolbox when we move toolbar */\n editor.toolbar.toolbox.close();\n\n if (!editor.content.currentNode) {\n\n return;\n\n }\n\n var newYCoordinate = editor.content.currentNode.offsetTop - (editor.toolbar.defaultToolbarHeight / 2) + editor.toolbar.defaultOffset;\n\n editor.nodes.toolbar.style.transform = `translate3D(0, ${Math.floor(newYCoordinate)}px, 0)`;\n\n /** Close trash actions */\n editor.toolbar.settings.hideRemoveActions();\n\n };\n\n return toolbar;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/toolbar.js","/**\n * Toolbar settings\n *\n * @version 1.0.5\n */\n\nmodule.exports = (function (settings) {\n\n let editor = codex.editor;\n\n settings.opened = false;\n\n settings.setting = null;\n settings.actions = null;\n\n /**\n * Append and open settings\n */\n settings.open = function (toolType) {\n\n /**\n * Append settings content\n * It's stored in tool.settings\n */\n if ( !editor.tools[toolType] || !editor.tools[toolType].makeSettings ) {\n\n return;\n\n }\n\n /**\n * Draw settings block\n */\n var settingsBlock = editor.tools[toolType].makeSettings();\n\n editor.nodes.pluginSettings.appendChild(settingsBlock);\n\n\n /** Open settings block */\n editor.nodes.blockSettings.classList.add('opened');\n this.opened = true;\n\n };\n\n /**\n * Close and clear settings\n */\n settings.close = function () {\n\n editor.nodes.blockSettings.classList.remove('opened');\n editor.nodes.pluginSettings.innerHTML = '';\n\n this.opened = false;\n\n };\n\n /**\n * @param {string} toolType - plugin type\n */\n settings.toggle = function ( toolType ) {\n\n if ( !this.opened ) {\n\n this.open(toolType);\n\n } else {\n\n this.close();\n\n }\n\n };\n\n /**\n * Here we will draw buttons and add listeners to components\n */\n settings.makeRemoveBlockButton = function () {\n\n var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}),\n settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML : '' }),\n actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}),\n confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent : 'Удалить блок' }),\n cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent : 'Отмена' });\n\n editor.listeners.add(settingButton, 'click', editor.toolbar.settings.removeButtonClicked, false);\n\n editor.listeners.add(confirmAction, 'click', editor.toolbar.settings.confirmRemovingRequest, false);\n\n editor.listeners.add(cancelAction, 'click', editor.toolbar.settings.cancelRemovingRequest, false);\n\n actionWrapper.appendChild(confirmAction);\n actionWrapper.appendChild(cancelAction);\n\n removeBlockWrapper.appendChild(settingButton);\n removeBlockWrapper.appendChild(actionWrapper);\n\n /** Save setting */\n editor.toolbar.settings.setting = settingButton;\n editor.toolbar.settings.actions = actionWrapper;\n\n return removeBlockWrapper;\n\n };\n\n settings.removeButtonClicked = function () {\n\n var action = editor.toolbar.settings.actions;\n\n if (action.classList.contains('opened')) {\n\n editor.toolbar.settings.hideRemoveActions();\n\n } else {\n\n editor.toolbar.settings.showRemoveActions();\n\n }\n\n editor.toolbar.toolbox.close();\n editor.toolbar.settings.close();\n\n };\n\n settings.cancelRemovingRequest = function () {\n\n editor.toolbar.settings.actions.classList.remove('opened');\n\n };\n\n settings.confirmRemovingRequest = function () {\n\n var currentBlock = editor.content.currentNode,\n firstLevelBlocksCount;\n\n currentBlock.remove();\n\n firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\n\n /**\n * If all blocks are removed\n */\n if (firstLevelBlocksCount === 0) {\n\n /** update currentNode variable */\n editor.content.currentNode = null;\n\n /** Inserting new empty initial block */\n editor.ui.addInitialBlock();\n\n }\n\n editor.ui.saveInputs();\n\n editor.toolbar.close();\n\n };\n\n settings.showRemoveActions = function () {\n\n editor.toolbar.settings.actions.classList.add('opened');\n\n };\n\n settings.hideRemoveActions = function () {\n\n editor.toolbar.settings.actions.classList.remove('opened');\n\n };\n\n return settings;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/settings.js","/**\n * Inline toolbar\n *\n * Contains from tools:\n * Bold, Italic, Underline and Anchor\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (inline) {\n\n let editor = codex.editor;\n\n inline.buttonsOpened = null;\n inline.actionsOpened = null;\n inline.wrappersOffset = null;\n\n /**\n * saving selection that need for execCommand for styling\n *\n */\n inline.storedSelection = null;\n\n /**\n * @protected\n *\n * Open inline toobar\n */\n inline.show = function () {\n\n var currentNode = editor.content.currentNode,\n tool = currentNode.dataset.tool,\n plugin;\n\n /**\n * tool allowed to open inline toolbar\n */\n plugin = editor.tools[tool];\n\n if (!plugin.showInlineToolbar)\n return;\n\n var selectedText = inline.getSelectionText(),\n toolbar = editor.nodes.inlineToolbar.wrapper;\n\n if (selectedText.length > 0) {\n\n /** Move toolbar and open */\n editor.toolbar.inline.move();\n\n /** Open inline toolbar */\n toolbar.classList.add('opened');\n\n /** show buttons of inline toolbar */\n editor.toolbar.inline.showButtons();\n\n }\n\n };\n\n /**\n * @protected\n *\n * Closes inline toolbar\n */\n inline.close = function () {\n\n var toolbar = editor.nodes.inlineToolbar.wrapper;\n\n toolbar.classList.remove('opened');\n\n };\n\n /**\n * @private\n *\n * Moving toolbar\n */\n inline.move = function () {\n\n if (!this.wrappersOffset) {\n\n this.wrappersOffset = this.getWrappersOffset();\n\n }\n\n var coords = this.getSelectionCoords(),\n defaultOffset = 0,\n toolbar = editor.nodes.inlineToolbar.wrapper,\n newCoordinateX,\n newCoordinateY;\n\n if (!coords) {\n\n return;\n\n }\n\n if (toolbar.offsetHeight === 0) {\n\n defaultOffset = 40;\n\n }\n\n newCoordinateX = coords.x - this.wrappersOffset.left;\n newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight;\n\n toolbar.style.transform = `translate3D(${Math.floor(newCoordinateX)}px, ${Math.floor(newCoordinateY)}px, 0)`;\n\n /** Close everything */\n editor.toolbar.inline.closeButtons();\n editor.toolbar.inline.closeAction();\n\n };\n\n /**\n * @private\n *\n * Tool Clicked\n */\n\n inline.toolClicked = function (event, type) {\n\n /**\n * For simple tools we use default browser function\n * For more complicated tools, we should write our own behavior\n */\n switch (type) {\n case 'createLink' : editor.toolbar.inline.createLinkAction(event, type); break;\n default : editor.toolbar.inline.defaultToolAction(type); break;\n }\n\n /**\n * highlight buttons\n * after making some action\n */\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\n\n };\n\n /**\n * @private\n *\n * Saving wrappers offset in DOM\n */\n inline.getWrappersOffset = function () {\n\n var wrapper = editor.nodes.wrapper,\n offset = this.getOffset(wrapper);\n\n this.wrappersOffset = offset;\n return offset;\n\n };\n\n /**\n * @private\n *\n * Calculates offset of DOM element\n *\n * @param el\n * @returns {{top: number, left: number}}\n */\n inline.getOffset = function ( el ) {\n\n var _x = 0;\n var _y = 0;\n\n while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {\n\n _x += (el.offsetLeft + el.clientLeft);\n _y += (el.offsetTop + el.clientTop);\n el = el.offsetParent;\n\n }\n return { top: _y, left: _x };\n\n };\n\n /**\n * @private\n *\n * Calculates position of selected text\n * @returns {{x: number, y: number}}\n */\n inline.getSelectionCoords = function () {\n\n var sel = document.selection, range;\n var x = 0, y = 0;\n\n if (sel) {\n\n if (sel.type != 'Control') {\n\n range = sel.createRange();\n range.collapse(true);\n x = range.boundingLeft;\n y = range.boundingTop;\n\n }\n\n } else if (window.getSelection) {\n\n sel = window.getSelection();\n\n if (sel.rangeCount) {\n\n range = sel.getRangeAt(0).cloneRange();\n if (range.getClientRects) {\n\n range.collapse(true);\n var rect = range.getClientRects()[0];\n\n if (!rect) {\n\n return;\n\n }\n\n x = rect.left;\n y = rect.top;\n\n }\n\n }\n\n }\n return { x: x, y: y };\n\n };\n\n /**\n * @private\n *\n * Returns selected text as String\n * @returns {string}\n */\n inline.getSelectionText = function () {\n\n var selectedText = '';\n\n // all modern browsers and IE9+\n if (window.getSelection) {\n\n selectedText = window.getSelection().toString();\n\n }\n\n return selectedText;\n\n };\n\n /** Opens buttons block */\n inline.showButtons = function () {\n\n var buttons = editor.nodes.inlineToolbar.buttons;\n\n buttons.classList.add('opened');\n\n editor.toolbar.inline.buttonsOpened = true;\n\n /** highlight buttons */\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\n\n };\n\n /** Makes buttons disappear */\n inline.closeButtons = function () {\n\n var buttons = editor.nodes.inlineToolbar.buttons;\n\n buttons.classList.remove('opened');\n\n editor.toolbar.inline.buttonsOpened = false;\n\n };\n\n /** Open buttons defined action if exist */\n inline.showActions = function () {\n\n var action = editor.nodes.inlineToolbar.actions;\n\n action.classList.add('opened');\n\n editor.toolbar.inline.actionsOpened = true;\n\n };\n\n /** Close actions block */\n inline.closeAction = function () {\n\n var action = editor.nodes.inlineToolbar.actions;\n\n action.innerHTML = '';\n action.classList.remove('opened');\n editor.toolbar.inline.actionsOpened = false;\n\n };\n\n\n /**\n * Callback for keydowns in inline toolbar \"Insert link...\" input\n */\n let inlineToolbarAnchorInputKeydown_ = function (event) {\n\n if (event.keyCode != editor.core.keys.ENTER) {\n\n return;\n\n }\n\n let editable = editor.content.currentNode,\n storedSelection = editor.toolbar.inline.storedSelection;\n\n editor.toolbar.inline.restoreSelection(editable, storedSelection);\n editor.toolbar.inline.setAnchor(this.value);\n\n /**\n * Preventing events that will be able to happen\n */\n event.preventDefault();\n event.stopImmediatePropagation();\n\n editor.toolbar.inline.clearRange();\n\n };\n\n /** Action for link creation or for setting anchor */\n inline.createLinkAction = function (event) {\n\n var isActive = this.isLinkActive();\n\n var editable = editor.content.currentNode,\n storedSelection = editor.toolbar.inline.saveSelection(editable);\n\n /** Save globally selection */\n editor.toolbar.inline.storedSelection = storedSelection;\n\n if (isActive) {\n\n\n /**\n * Changing stored selection. if we want to remove anchor from word\n * we should remove anchor from whole word, not only selected part.\n * The solution is than we get the length of current link\n * Change start position to - end of selection minus length of anchor\n */\n editor.toolbar.inline.restoreSelection(editable, storedSelection);\n\n editor.toolbar.inline.defaultToolAction('unlink');\n\n } else {\n\n /** Create input and close buttons */\n var action = editor.draw.inputForLink();\n\n editor.nodes.inlineToolbar.actions.appendChild(action);\n\n editor.toolbar.inline.closeButtons();\n editor.toolbar.inline.showActions();\n\n /**\n * focus to input\n * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus\n * Prevents event after showing input and when we need to focus an input which is in unexisted form\n */\n action.focus();\n event.preventDefault();\n\n /** Callback to link action */\n editor.listeners.add(action, 'keydown', inlineToolbarAnchorInputKeydown_, false);\n\n }\n\n };\n\n inline.isLinkActive = function () {\n\n var isActive = false;\n\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) {\n\n var dataType = tool.dataset.type;\n\n if (dataType == 'link' && tool.classList.contains('hightlighted')) {\n\n isActive = true;\n\n }\n\n });\n\n return isActive;\n\n };\n\n /** default action behavior of tool */\n inline.defaultToolAction = function (type) {\n\n document.execCommand(type, false, null);\n\n };\n\n /**\n * @private\n *\n * Sets URL\n *\n * @param {String} url - URL\n */\n inline.setAnchor = function (url) {\n\n document.execCommand('createLink', false, url);\n\n /** Close after URL inserting */\n editor.toolbar.inline.closeAction();\n\n };\n\n /**\n * @private\n *\n * Saves selection\n */\n inline.saveSelection = function (containerEl) {\n\n var range = window.getSelection().getRangeAt(0),\n preSelectionRange = range.cloneRange(),\n start;\n\n preSelectionRange.selectNodeContents(containerEl);\n preSelectionRange.setEnd(range.startContainer, range.startOffset);\n\n start = preSelectionRange.toString().length;\n\n return {\n start: start,\n end: start + range.toString().length\n };\n\n };\n\n /**\n * @private\n *\n * Sets to previous selection (Range)\n *\n * @param {Element} containerEl - editable element where we restore range\n * @param {Object} savedSel - range basic information to restore\n */\n inline.restoreSelection = function (containerEl, savedSel) {\n\n var range = document.createRange(),\n charIndex = 0;\n\n range.setStart(containerEl, 0);\n range.collapse(true);\n\n var nodeStack = [ containerEl ],\n node,\n foundStart = false,\n stop = false,\n nextCharIndex;\n\n while (!stop && (node = nodeStack.pop())) {\n\n if (node.nodeType == 3) {\n\n nextCharIndex = charIndex + node.length;\n\n if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {\n\n range.setStart(node, savedSel.start - charIndex);\n foundStart = true;\n\n }\n if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {\n\n range.setEnd(node, savedSel.end - charIndex);\n stop = true;\n\n }\n charIndex = nextCharIndex;\n\n } else {\n\n var i = node.childNodes.length;\n\n while (i--) {\n\n nodeStack.push(node.childNodes[i]);\n\n }\n\n }\n\n }\n\n var sel = window.getSelection();\n\n sel.removeAllRanges();\n sel.addRange(range);\n\n };\n\n /**\n * @private\n *\n * Removes all ranges from window selection\n */\n inline.clearRange = function () {\n\n var selection = window.getSelection();\n\n selection.removeAllRanges();\n\n };\n\n /**\n * @private\n *\n * sets or removes hightlight\n */\n inline.hightlight = function (tool) {\n\n var dataType = tool.dataset.type;\n\n if (document.queryCommandState(dataType)) {\n\n editor.toolbar.inline.setButtonHighlighted(tool);\n\n } else {\n\n editor.toolbar.inline.removeButtonsHighLight(tool);\n\n }\n\n /**\n *\n * hightlight for anchors\n */\n var selection = window.getSelection(),\n tag = selection.anchorNode.parentNode;\n\n if (tag.tagName == 'A' && dataType == 'link') {\n\n editor.toolbar.inline.setButtonHighlighted(tool);\n\n }\n\n };\n\n /**\n * @private\n *\n * Mark button if text is already executed\n */\n inline.setButtonHighlighted = function (button) {\n\n button.classList.add('hightlighted');\n\n /** At link tool we also change icon */\n if (button.dataset.type == 'link') {\n\n var icon = button.childNodes[0];\n\n icon.classList.remove('ce-icon-link');\n icon.classList.add('ce-icon-unlink');\n\n }\n\n };\n\n /**\n * @private\n *\n * Removes hightlight\n */\n inline.removeButtonsHighLight = function (button) {\n\n button.classList.remove('hightlighted');\n\n /** At link tool we also change icon */\n if (button.dataset.type == 'link') {\n\n var icon = button.childNodes[0];\n\n icon.classList.remove('ce-icon-unlink');\n icon.classList.add('ce-icon-link');\n\n }\n\n };\n\n\n return inline;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/inline.js","/**\n * Codex Editor toolbox\n *\n * All tools be able to appended here\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (toolbox) {\n\n let editor = codex.editor;\n\n toolbox.opened = false;\n toolbox.openedOnBlock = null;\n\n /** Shows toolbox */\n toolbox.open = function () {\n\n /** Close setting if toolbox is opened */\n if (editor.toolbar.settings.opened) {\n\n editor.toolbar.settings.close();\n\n }\n\n /** Add 'toolbar-opened' class for current block **/\n toolbox.openedOnBlock = editor.content.currentNode;\n toolbox.openedOnBlock.classList.add('toolbar-opened');\n\n /** display toolbox */\n editor.nodes.toolbox.classList.add('opened');\n\n /** Animate plus button */\n editor.nodes.plusButton.classList.add('clicked');\n\n /** toolbox state */\n editor.toolbar.toolbox.opened = true;\n\n };\n\n /** Closes toolbox */\n toolbox.close = function () {\n\n /** Remove 'toolbar-opened' class from current block **/\n if (toolbox.openedOnBlock) toolbox.openedOnBlock.classList.remove('toolbar-opened');\n toolbox.openedOnBlock = null;\n\n /** Makes toolbox disappear */\n editor.nodes.toolbox.classList.remove('opened');\n\n /** Rotate plus button */\n editor.nodes.plusButton.classList.remove('clicked');\n\n /** toolbox state */\n editor.toolbar.toolbox.opened = false;\n\n editor.toolbar.current = null;\n\n };\n\n toolbox.leaf = function () {\n\n let currentTool = editor.toolbar.current,\n tools = Object.keys(editor.tools),\n barButtons = editor.nodes.toolbarButtons,\n nextToolIndex = 0,\n toolToSelect,\n visibleTool,\n tool;\n\n if ( !currentTool ) {\n\n /** Get first tool from object*/\n for(tool in editor.tools) {\n\n if (editor.tools[tool].displayInToolbox) {\n\n break;\n\n }\n\n nextToolIndex ++;\n\n }\n\n } else {\n\n nextToolIndex = (tools.indexOf(currentTool) + 1) % tools.length;\n visibleTool = tools[nextToolIndex];\n\n while (!editor.tools[visibleTool].displayInToolbox) {\n\n nextToolIndex = (nextToolIndex + 1) % tools.length;\n visibleTool = tools[nextToolIndex];\n\n }\n\n }\n\n toolToSelect = tools[nextToolIndex];\n\n for ( var button in barButtons ) {\n\n barButtons[button].classList.remove('selected');\n\n }\n\n barButtons[toolToSelect].classList.add('selected');\n editor.toolbar.current = toolToSelect;\n\n };\n\n /**\n * Transforming selected node type into selected toolbar element type\n * @param {event} event\n */\n toolbox.toolClicked = function (event) {\n\n /**\n * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty\n */\n var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'],\n tool = editor.tools[editor.toolbar.current],\n workingNode = editor.content.currentNode,\n currentInputIndex = editor.caret.inputIndex,\n newBlockContent,\n appendCallback,\n blockData;\n\n /** Make block from plugin */\n newBlockContent = tool.render();\n\n /** information about block */\n blockData = {\n block : newBlockContent,\n type : tool.type,\n stretched : false\n };\n\n if (\n workingNode &&\n UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 &&\n workingNode.textContent.trim() === ''\n ) {\n\n /** Replace current block */\n editor.content.switchBlock(workingNode, newBlockContent, tool.type);\n\n } else {\n\n /** Insert new Block from plugin */\n editor.content.insertBlock(blockData);\n\n /** increase input index */\n currentInputIndex++;\n\n }\n\n /** Fire tool append callback */\n appendCallback = tool.appendCallback;\n\n if (appendCallback && typeof appendCallback == 'function') {\n\n appendCallback.call(event);\n\n }\n\n window.setTimeout(function () {\n\n /** Set caret to current block */\n editor.caret.setToBlock(currentInputIndex);\n\n }, 10);\n\n\n /**\n * Changing current Node\n */\n editor.content.workingNodeChanged();\n\n /**\n * Move toolbar when node is changed\n */\n editor.toolbar.move();\n\n };\n\n return toolbox;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/toolbox.js","/**\n * @module Codex Editor Callbacks module\n * @description Module works with editor added Elements\n *\n * @author Codex Team\n * @version 1.4.0\n */\n\nmodule.exports = (function (callbacks) {\n\n let editor = codex.editor;\n\n /**\n * used by UI module\n * @description Routes all keydowns on document\n * @param {Object} event\n */\n callbacks.globalKeydown = function (event) {\n\n switch (event.keyCode) {\n case editor.core.keys.ENTER : enterKeyPressed_(event); break;\n }\n\n };\n\n /**\n * used by UI module\n * @description Routes all keydowns on redactors area\n * @param {Object} event\n */\n callbacks.redactorKeyDown = function (event) {\n\n switch (event.keyCode) {\n case editor.core.keys.TAB : tabKeyPressedOnRedactorsZone_(event); break;\n case editor.core.keys.ENTER : enterKeyPressedOnRedactorsZone_(event); break;\n case editor.core.keys.ESC : escapeKeyPressedOnRedactorsZone_(event); break;\n default : defaultKeyPressedOnRedactorsZone_(event); break;\n }\n\n };\n\n /**\n * used by UI module\n * @description Routes all keyup events\n * @param {Object} event\n */\n callbacks.globalKeyup = function (event) {\n\n switch (event.keyCode) {\n case editor.core.keys.UP :\n case editor.core.keys.LEFT :\n case editor.core.keys.RIGHT :\n case editor.core.keys.DOWN : arrowKeyPressed_(event); break;\n }\n\n };\n\n /**\n * @param {Object} event\n * @private\n *\n * Handles behaviour when tab pressed\n * @description if Content is empty show toolbox (if it is closed) or leaf tools\n * uses Toolbars toolbox module to handle the situation\n */\n var tabKeyPressedOnRedactorsZone_ = function (event) {\n\n /**\n * Wait for solution. Would like to know the behaviour\n * @todo Add spaces\n */\n event.preventDefault();\n\n\n if (!editor.core.isBlockEmpty(editor.content.currentNode)) {\n\n return;\n\n }\n\n if ( !editor.toolbar.opened ) {\n\n editor.toolbar.open();\n\n }\n\n if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) {\n\n editor.toolbar.toolbox.open();\n\n } else {\n\n editor.toolbar.toolbox.leaf();\n\n }\n\n };\n\n /**\n * Handles global EnterKey Press\n * @see enterPressedOnBlock_\n * @param {Object} event\n */\n var enterKeyPressed_ = function () {\n\n if (editor.content.editorAreaHightlighted) {\n\n /**\n * it means that we lose input index, saved index before is not correct\n * therefore we need to set caret when we insert new block\n */\n editor.caret.inputIndex = -1;\n\n enterPressedOnBlock_();\n\n }\n\n };\n\n /**\n * Callback for enter key pressing in first-level block area\n *\n * @param {Event} event\n * @private\n *\n * @description Inserts new block with initial type from settings\n */\n var enterPressedOnBlock_ = function () {\n\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\n editor.content.insertBlock({\n type : NEW_BLOCK_TYPE,\n block : editor.tools[NEW_BLOCK_TYPE].render()\n }, true );\n\n editor.toolbar.move();\n editor.toolbar.open();\n\n };\n\n\n /**\n * ENTER key handler\n *\n * @param {Object} event\n * @private\n *\n * @description Makes new block with initial type from settings\n */\n var enterKeyPressedOnRedactorsZone_ = function (event) {\n\n if (event.target.contentEditable == 'true') {\n\n /** Update input index */\n editor.caret.saveCurrentInputIndex();\n\n }\n\n var currentInputIndex = editor.caret.getCurrentInputIndex() || 0,\n workingNode = editor.content.currentNode,\n tool = workingNode.dataset.tool,\n isEnterPressedOnToolbar = editor.toolbar.opened &&\n editor.toolbar.current &&\n event.target == editor.state.inputs[currentInputIndex];\n\n /** The list of tools which needs the default browser behaviour */\n var enableLineBreaks = editor.tools[tool].enableLineBreaks;\n\n /** This type of block creates when enter is pressed */\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\n /**\n * When toolbar is opened, select tool instead of making new paragraph\n */\n if ( isEnterPressedOnToolbar ) {\n\n event.preventDefault();\n\n editor.toolbar.toolbox.toolClicked(event);\n\n editor.toolbar.close();\n\n /**\n * Stop other listeners callback executions\n */\n event.stopPropagation();\n event.stopImmediatePropagation();\n\n return;\n\n }\n\n /**\n * Allow paragraph lineBreaks with shift enter\n * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation\n */\n if ( event.shiftKey || enableLineBreaks ) {\n\n event.stopPropagation();\n event.stopImmediatePropagation();\n return;\n\n }\n\n var currentSelection = window.getSelection(),\n currentSelectedNode = currentSelection.anchorNode,\n caretAtTheEndOfText = editor.caret.position.atTheEnd(),\n isTextNodeHasParentBetweenContenteditable = false;\n\n /**\n * Allow making new

    in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\n */\n if ( event.shiftKey && !enableLineBreaks ) {\n\n editor.callback.enterPressedOnBlock(editor.content.currentBlock, event);\n event.preventDefault();\n return;\n\n }\n\n /**\n * Workaround situation when caret at the Text node that has some wrapper Elements\n * Split block cant handle this.\n * We need to save default behavior\n */\n isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true';\n\n /**\n * Split blocks when input has several nodes and caret placed in textNode\n */\n if (\n currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT &&\n !isTextNodeHasParentBetweenContenteditable &&\n !caretAtTheEndOfText\n ) {\n\n event.preventDefault();\n\n editor.core.log('Splitting Text node...');\n\n editor.content.splitBlock(currentInputIndex);\n\n /** Show plus button when next input after split is empty*/\n if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) {\n\n editor.toolbar.showPlusButton();\n\n }\n\n } else {\n\n var islastNode = editor.content.isLastNode(currentSelectedNode);\n\n if ( islastNode && caretAtTheEndOfText ) {\n\n event.preventDefault();\n event.stopPropagation();\n event.stopImmediatePropagation();\n\n editor.core.log('ENTER clicked in last textNode. Create new BLOCK');\n\n editor.content.insertBlock({\n type: NEW_BLOCK_TYPE,\n block: editor.tools[NEW_BLOCK_TYPE].render()\n }, true);\n\n editor.toolbar.move();\n editor.toolbar.open();\n\n /** Show plus button with empty block */\n editor.toolbar.showPlusButton();\n\n }\n\n }\n\n /** get all inputs after new appending block */\n editor.ui.saveInputs();\n\n };\n\n /**\n * Escape behaviour\n * @param event\n * @private\n *\n * @description Closes toolbox and toolbar. Prevents default behaviour\n */\n var escapeKeyPressedOnRedactorsZone_ = function (event) {\n\n /** Close all toolbar */\n editor.toolbar.close();\n\n /** Close toolbox */\n editor.toolbar.toolbox.close();\n\n event.preventDefault();\n\n };\n\n /**\n * @param {Event} event\n * @private\n *\n * closes and moves toolbar\n */\n var arrowKeyPressed_ = function (event) {\n\n editor.content.workingNodeChanged();\n\n /* Closing toolbar */\n editor.toolbar.close();\n editor.toolbar.move();\n\n };\n\n /**\n * @private\n * @param {Event} event\n *\n * @description Closes all opened bars from toolbar.\n * If block is mark, clears highlightning\n */\n var defaultKeyPressedOnRedactorsZone_ = function () {\n\n editor.toolbar.close();\n\n if (!editor.toolbar.inline.actionsOpened) {\n\n editor.toolbar.inline.close();\n editor.content.clearMark();\n\n }\n\n };\n\n /**\n * Handler when clicked on redactors area\n *\n * @protected\n * @param event\n *\n * @description Detects clicked area. If it is first-level block area, marks as detected and\n * on next enter press will be inserted new block\n * Otherwise, save carets position (input index) and put caret to the editable zone.\n *\n * @see detectWhenClickedOnFirstLevelBlockArea_\n *\n */\n callbacks.redactorClicked = function (event) {\n\n detectWhenClickedOnFirstLevelBlockArea_();\n\n editor.content.workingNodeChanged(event.target);\n editor.ui.saveInputs();\n\n var selectedText = editor.toolbar.inline.getSelectionText(),\n firstLevelBlock;\n\n /** If selection range took off, then we hide inline toolbar */\n if (selectedText.length === 0) {\n\n editor.toolbar.inline.close();\n\n }\n\n /** Update current input index in memory when caret focused into existed input */\n if (event.target.contentEditable == 'true') {\n\n editor.caret.saveCurrentInputIndex();\n\n }\n\n if (editor.content.currentNode === null) {\n\n /**\n * If inputs in redactor does not exits, then we put input index 0 not -1\n */\n var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0;\n\n /** If we have any inputs */\n if (editor.state.inputs.length) {\n\n /** getting firstlevel parent of input */\n firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);\n\n }\n\n /** If input is empty, then we set caret to the last input */\n if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) {\n\n editor.caret.setToBlock(indexOfLastInput);\n\n } else {\n\n /** Create new input when caret clicked in redactors area */\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\n\n editor.content.insertBlock({\n type : NEW_BLOCK_TYPE,\n block : editor.tools[NEW_BLOCK_TYPE].render()\n });\n\n /** If there is no inputs except inserted */\n if (editor.state.inputs.length === 1) {\n\n editor.caret.setToBlock(indexOfLastInput);\n\n } else {\n\n /** Set caret to this appended input */\n editor.caret.setToNextBlock(indexOfLastInput);\n\n }\n\n }\n\n } else {\n\n /** Close all panels */\n editor.toolbar.settings.close();\n editor.toolbar.toolbox.close();\n\n }\n\n /**\n * Move toolbar and open\n */\n editor.toolbar.move();\n editor.toolbar.open();\n\n var inputIsEmpty = !editor.content.currentNode.textContent.trim(),\n currentNodeType = editor.content.currentNode.dataset.tool,\n isInitialType = currentNodeType == editor.settings.initialBlockPlugin;\n\n\n /** Hide plus buttons */\n editor.toolbar.hidePlusButton();\n\n if (!inputIsEmpty) {\n\n /** Mark current block */\n editor.content.markBlock();\n\n }\n\n if ( isInitialType && inputIsEmpty ) {\n\n /** Show plus button */\n editor.toolbar.showPlusButton();\n\n }\n\n\n };\n\n /**\n * This method allows to define, is caret in contenteditable element or not.\n *\n * @private\n *\n * @description Otherwise, if we get TEXT node from range container, that will means we have input index.\n * In this case we use default browsers behaviour (if plugin allows that) or overwritten action.\n * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always\n * specifies to the first-level block. Other cases we just ignore.\n */\n var detectWhenClickedOnFirstLevelBlockArea_ = function () {\n\n var selection = window.getSelection(),\n anchorNode = selection.anchorNode,\n flag = false;\n\n if (selection.rangeCount === 0) {\n\n editor.content.editorAreaHightlighted = true;\n\n } else {\n\n if (!editor.core.isDomNode(anchorNode)) {\n\n anchorNode = anchorNode.parentNode;\n\n }\n\n /** Already founded, without loop */\n if (anchorNode.contentEditable == 'true') {\n\n flag = true;\n\n }\n\n while (anchorNode.contentEditable != 'true') {\n\n anchorNode = anchorNode.parentNode;\n\n if (anchorNode.contentEditable == 'true') {\n\n flag = true;\n\n }\n\n if (anchorNode == document.body) {\n\n break;\n\n }\n\n }\n\n /** If editable element founded, flag is \"TRUE\", Therefore we return \"FALSE\" */\n editor.content.editorAreaHightlighted = !flag;\n\n }\n\n };\n\n /**\n * Toolbar button click handler\n *\n * @param {Object} event - cursor to the button\n * @protected\n *\n * @description gets current tool and calls render method\n */\n callbacks.toolbarButtonClicked = function (event) {\n\n var button = this;\n\n editor.toolbar.current = button.dataset.type;\n\n editor.toolbar.toolbox.toolClicked(event);\n editor.toolbar.close();\n\n };\n\n /**\n * Show or Hide toolbox when plus button is clicked\n */\n callbacks.plusButtonClicked = function () {\n\n if (!editor.nodes.toolbox.classList.contains('opened')) {\n\n editor.toolbar.toolbox.open();\n\n } else {\n\n editor.toolbar.toolbox.close();\n\n }\n\n };\n\n /**\n * Block handlers for KeyDown events\n *\n * @protected\n * @param {Object} event\n *\n * Handles keydowns on block\n * @see blockRightOrDownArrowPressed_\n * @see backspacePressed_\n * @see blockLeftOrUpArrowPressed_\n */\n callbacks.blockKeydown = function (event) {\n\n let block = event.target; // event.target is input\n\n switch (event.keyCode) {\n\n case editor.core.keys.DOWN:\n case editor.core.keys.RIGHT:\n blockRightOrDownArrowPressed_(event);\n break;\n\n case editor.core.keys.BACKSPACE:\n backspacePressed_(block, event);\n break;\n\n case editor.core.keys.UP:\n case editor.core.keys.LEFT:\n blockLeftOrUpArrowPressed_(event);\n break;\n\n }\n\n };\n\n /**\n * RIGHT or DOWN keydowns on block\n *\n * @param {Object} event\n * @private\n *\n * @description watches the selection and gets closest editable element.\n * Uses method getDeepestTextNodeFromPosition to get the last node of next block\n * Sets caret if it is contenteditable\n */\n var blockRightOrDownArrowPressed_ = function (event) {\n\n var selection = window.getSelection(),\n inputs = editor.state.inputs,\n focusedNode = selection.anchorNode,\n focusedNodeHolder;\n\n /** Check for caret existance */\n if (!focusedNode) {\n\n return false;\n\n }\n\n /** Looking for closest (parent) contentEditable element of focused node */\n while (focusedNode.contentEditable != 'true') {\n\n focusedNodeHolder = focusedNode.parentNode;\n focusedNode = focusedNodeHolder;\n\n }\n\n /** Input index in DOM level */\n var editableElementIndex = 0;\n\n while (focusedNode != inputs[editableElementIndex]) {\n\n editableElementIndex ++;\n\n }\n\n /**\n * Founded contentEditable element doesn't have childs\n * Or maybe New created block\n */\n if (!focusedNode.textContent) {\n\n editor.caret.setToNextBlock(editableElementIndex);\n return;\n\n }\n\n /**\n * Do nothing when caret doesn not reaches the end of last child\n */\n var caretInLastChild = false,\n caretAtTheEndOfText = false;\n\n var lastChild,\n deepestTextnode;\n\n lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ];\n\n if (editor.core.isDomNode(lastChild)) {\n\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);\n\n } else {\n\n deepestTextnode = lastChild;\n\n }\n\n caretInLastChild = selection.anchorNode == deepestTextnode;\n caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;\n\n if ( !caretInLastChild || !caretAtTheEndOfText ) {\n\n editor.core.log('arrow [down|right] : caret does not reached the end');\n return false;\n\n }\n\n editor.caret.setToNextBlock(editableElementIndex);\n\n };\n\n /**\n * LEFT or UP keydowns on block\n *\n * @param {Object} event\n * @private\n *\n * watches the selection and gets closest editable element.\n * Uses method getDeepestTextNodeFromPosition to get the last node of previous block\n * Sets caret if it is contenteditable\n *\n */\n var blockLeftOrUpArrowPressed_ = function (event) {\n\n var selection = window.getSelection(),\n inputs = editor.state.inputs,\n focusedNode = selection.anchorNode,\n focusedNodeHolder;\n\n /** Check for caret existance */\n if (!focusedNode) {\n\n return false;\n\n }\n\n /**\n * LEFT or UP not at the beginning\n */\n if ( selection.anchorOffset !== 0) {\n\n return false;\n\n }\n\n /** Looking for parent contentEditable block */\n while (focusedNode.contentEditable != 'true') {\n\n focusedNodeHolder = focusedNode.parentNode;\n focusedNode = focusedNodeHolder;\n\n }\n\n /** Input index in DOM level */\n var editableElementIndex = 0;\n\n while (focusedNode != inputs[editableElementIndex]) {\n\n editableElementIndex ++;\n\n }\n\n /**\n * Do nothing if caret is not at the beginning of first child\n */\n var caretInFirstChild = false,\n caretAtTheBeginning = false;\n\n var firstChild,\n deepestTextnode;\n\n /**\n * Founded contentEditable element doesn't have childs\n * Or maybe New created block\n */\n if (!focusedNode.textContent) {\n\n editor.caret.setToPreviousBlock(editableElementIndex);\n return;\n\n }\n\n firstChild = focusedNode.childNodes[0];\n\n if (editor.core.isDomNode(firstChild)) {\n\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);\n\n } else {\n\n deepestTextnode = firstChild;\n\n }\n\n caretInFirstChild = selection.anchorNode == deepestTextnode;\n caretAtTheBeginning = selection.anchorOffset === 0;\n\n if ( caretInFirstChild && caretAtTheBeginning ) {\n\n editor.caret.setToPreviousBlock(editableElementIndex);\n\n }\n\n };\n\n /**\n * Handles backspace keydown\n *\n * @param {Element} block\n * @param {Object} event\n * @private\n *\n * @description if block is empty, delete the block and set caret to the previous block\n * If block is not empty, try to merge two blocks - current and previous\n * But it we try'n to remove first block, then we should set caret to the next block, not previous.\n * If we removed the last block, create new one\n */\n var backspacePressed_ = function (block, event) {\n\n var currentInputIndex = editor.caret.getCurrentInputIndex(),\n range,\n selectionLength,\n firstLevelBlocksCount;\n\n if (editor.core.isNativeInput(event.target)) {\n\n /** If input value is empty - remove block */\n if (event.target.value.trim() == '') {\n\n block.remove();\n\n } else {\n\n return;\n\n }\n\n }\n\n if (block.textContent.trim()) {\n\n range = editor.content.getRange();\n selectionLength = range.endOffset - range.startOffset;\n\n if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) {\n\n editor.content.mergeBlocks(currentInputIndex);\n\n } else {\n\n return;\n\n }\n\n }\n\n if (!selectionLength) {\n\n block.remove();\n\n }\n\n\n firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\n\n /**\n * If all blocks are removed\n */\n if (firstLevelBlocksCount === 0) {\n\n /** update currentNode variable */\n editor.content.currentNode = null;\n\n /** Inserting new empty initial block */\n editor.ui.addInitialBlock();\n\n /** Updating inputs state after deleting last block */\n editor.ui.saveInputs();\n\n /** Set to current appended block */\n window.setTimeout(function () {\n\n editor.caret.setToPreviousBlock(1);\n\n }, 10);\n\n } else {\n\n if (editor.caret.inputIndex !== 0) {\n\n /** Target block is not first */\n editor.caret.setToPreviousBlock(editor.caret.inputIndex);\n\n } else {\n\n /** If we try to delete first block */\n editor.caret.setToNextBlock(editor.caret.inputIndex);\n\n }\n\n }\n\n editor.toolbar.move();\n\n if (!editor.toolbar.opened) {\n\n editor.toolbar.open();\n\n }\n\n /** Updating inputs state */\n editor.ui.saveInputs();\n\n /** Prevent default browser behaviour */\n event.preventDefault();\n\n };\n\n /**\n * used by UI module\n * Clicks on block settings button\n *\n * @param {Object} event\n * @protected\n * @description Opens toolbar settings\n */\n callbacks.showSettingsButtonClicked = function (event) {\n\n /**\n * Get type of current block\n * It uses to append settings from tool.settings property.\n * ...\n * Type is stored in data-type attribute on block\n */\n var currentToolType = editor.content.currentNode.dataset.tool;\n\n editor.toolbar.settings.toggle(currentToolType);\n\n /** Close toolbox when settings button is active */\n editor.toolbar.toolbox.close();\n editor.toolbar.settings.hideRemoveActions();\n\n };\n\n return callbacks;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/callbacks.js","/**\n * Codex Editor Draw module\n *\n * @author Codex Team\n * @version 1.0.\n */\n\nmodule.exports = (function (draw) {\n\n /**\n * Base editor wrapper\n */\n draw.wrapper = function () {\n\n var wrapper = document.createElement('div');\n\n wrapper.className += 'codex-editor';\n\n return wrapper;\n\n };\n\n /**\n * Content-editable holder\n */\n draw.redactor = function () {\n\n var redactor = document.createElement('div');\n\n redactor.className += 'ce-redactor';\n\n return redactor;\n\n };\n\n draw.ceBlock = function () {\n\n var block = document.createElement('DIV');\n\n block.className += 'ce_block';\n\n return block;\n\n };\n\n /**\n * Empty toolbar with toggler\n */\n draw.toolbar = function () {\n\n var bar = document.createElement('div');\n\n bar.className += 'ce-toolbar';\n\n return bar;\n\n };\n\n draw.toolbarContent = function () {\n\n var wrapper = document.createElement('DIV');\n\n wrapper.classList.add('ce-toolbar__content');\n\n return wrapper;\n\n };\n\n /**\n * Inline toolbar\n */\n draw.inlineToolbar = function () {\n\n var bar = document.createElement('DIV');\n\n bar.className += 'ce-toolbar-inline';\n\n return bar;\n\n };\n\n /**\n * Wrapper for inline toobar buttons\n */\n draw.inlineToolbarButtons = function () {\n\n var wrapper = document.createElement('DIV');\n\n wrapper.className += 'ce-toolbar-inline__buttons';\n\n return wrapper;\n\n };\n\n /**\n * For some actions\n */\n draw.inlineToolbarActions = function () {\n\n var wrapper = document.createElement('DIV');\n\n wrapper.className += 'ce-toolbar-inline__actions';\n\n return wrapper;\n\n };\n\n draw.inputForLink = function () {\n\n var input = document.createElement('INPUT');\n\n input.type = 'input';\n input.className += 'inputForLink';\n input.placeholder = 'Вставьте ссылку ...';\n input.setAttribute('form', 'defaultForm');\n\n input.setAttribute('autofocus', 'autofocus');\n\n return input;\n\n };\n\n /**\n * @todo Desc\n */\n draw.blockButtons = function () {\n\n var block = document.createElement('div');\n\n block.className += 'ce-toolbar__actions';\n\n return block;\n\n };\n\n /**\n * Block settings panel\n */\n draw.blockSettings = function () {\n\n var settings = document.createElement('div');\n\n settings.className += 'ce-settings';\n\n return settings;\n\n };\n\n draw.defaultSettings = function () {\n\n var div = document.createElement('div');\n\n div.classList.add('ce-settings_default');\n\n return div;\n\n };\n\n draw.pluginsSettings = function () {\n\n var div = document.createElement('div');\n\n div.classList.add('ce-settings_plugin');\n\n return div;\n\n };\n\n draw.plusButton = function () {\n\n var button = document.createElement('span');\n\n button.className = 'ce-toolbar__plus';\n // button.innerHTML = '';\n\n return button;\n\n };\n\n /**\n * Settings button in toolbar\n */\n draw.settingsButton = function () {\n\n var toggler = document.createElement('span');\n\n toggler.className = 'ce-toolbar__settings-btn';\n\n /** Toggler button*/\n toggler.innerHTML = '';\n\n return toggler;\n\n };\n\n /**\n * Redactor tools wrapper\n */\n\n draw.toolbox = function () {\n\n var wrapper = document.createElement('div');\n\n wrapper.className = 'ce-toolbar__tools';\n\n return wrapper;\n\n };\n\n /**\n * @protected\n *\n * Draws tool buttons for toolbox\n *\n * @param {String} type\n * @param {String} classname\n * @returns {Element}\n */\n draw.toolbarButton = function (type, classname) {\n\n var button = document.createElement('li'),\n toolIcon = document.createElement('i'),\n toolTitle = document.createElement('span');\n\n button.dataset.type = type;\n button.setAttribute('title', type);\n\n toolIcon.classList.add(classname);\n toolTitle.classList.add('ce_toolbar_tools--title');\n\n\n button.appendChild(toolIcon);\n button.appendChild(toolTitle);\n\n return button;\n\n };\n\n /**\n * @protected\n *\n * Draws tools for inline toolbar\n *\n * @param {String} type\n * @param {String} classname\n */\n draw.toolbarButtonInline = function (type, classname) {\n\n var button = document.createElement('BUTTON'),\n toolIcon = document.createElement('I');\n\n button.type = 'button';\n button.dataset.type = type;\n toolIcon.classList.add(classname);\n\n button.appendChild(toolIcon);\n\n return button;\n\n };\n\n /**\n * Redactor block\n */\n draw.block = function (tagName, content) {\n\n var node = document.createElement(tagName);\n\n node.innerHTML = content || '';\n\n return node;\n\n };\n\n /**\n * Creates Node with passed tagName and className\n * @param {string} tagName\n * @param {string} className\n * @param {object} properties - allow to assign properties\n */\n draw.node = function ( tagName, className, properties ) {\n\n var el = document.createElement( tagName );\n\n if ( className ) el.className = className;\n\n if ( properties ) {\n\n for (var name in properties) {\n\n el[name] = properties[name];\n\n }\n\n }\n\n return el;\n\n };\n\n /**\n * Unavailable plugin block\n */\n draw.unavailableBlock = function () {\n\n var wrapper = document.createElement('DIV');\n\n wrapper.classList.add('cdx-unavailable-block');\n\n return wrapper;\n\n };\n\n return draw;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/draw.js","/**\n * Codex Editor Caret Module\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (caret) {\n\n let editor = codex.editor;\n\n /**\n * @var {int} InputIndex - editable element in DOM\n */\n caret.inputIndex = null;\n\n /**\n * @var {int} offset - caret position in a text node.\n */\n caret.offset = null;\n\n /**\n * @var {int} focusedNodeIndex - we get index of child node from first-level block\n */\n caret.focusedNodeIndex = null;\n\n /**\n * Creates Document Range and sets caret to the element.\n * @protected\n * @uses caret.save — if you need to save caret position\n * @param {Element} el - Changed Node.\n */\n caret.set = function ( el, index, offset) {\n\n offset = offset || caret.offset || 0;\n index = index || caret.focusedNodeIndex || 0;\n\n var childs = el.childNodes,\n nodeToSet;\n\n if ( childs.length === 0 ) {\n\n nodeToSet = el;\n\n } else {\n\n nodeToSet = childs[index];\n\n }\n\n /** If Element is INPUT */\n if (el.contentEditable != 'true') {\n\n el.focus();\n return;\n\n }\n\n if (editor.core.isDomNode(nodeToSet)) {\n\n nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);\n\n }\n\n var range = document.createRange(),\n selection = window.getSelection();\n\n window.setTimeout(function () {\n\n range.setStart(nodeToSet, offset);\n range.setEnd(nodeToSet, offset);\n\n selection.removeAllRanges();\n selection.addRange(range);\n\n editor.caret.saveCurrentInputIndex();\n\n }, 20);\n\n };\n\n /**\n * @protected\n * Updates index of input and saves it in caret object\n */\n caret.saveCurrentInputIndex = function () {\n\n /** Index of Input that we paste sanitized content */\n var selection = window.getSelection(),\n inputs = editor.state.inputs,\n focusedNode = selection.anchorNode,\n focusedNodeHolder;\n\n if (!focusedNode) {\n\n return;\n\n }\n\n /** Looking for parent contentEditable block */\n while (focusedNode && focusedNode.contentEditable != 'true') {\n\n focusedNodeHolder = focusedNode.parentNode;\n focusedNode = focusedNodeHolder;\n\n }\n\n /** Input index in DOM level */\n var editableElementIndex = 0;\n\n while (focusedNode != inputs[editableElementIndex]) {\n\n editableElementIndex ++;\n\n }\n\n caret.inputIndex = editableElementIndex;\n\n };\n\n /**\n * Returns current input index (caret object)\n */\n caret.getCurrentInputIndex = function () {\n\n return caret.inputIndex;\n\n };\n\n /**\n * @param {int} index - index of first-level block after that we set caret into next input\n */\n caret.setToNextBlock = function (index) {\n\n var inputs = editor.state.inputs,\n nextInput = inputs[index + 1];\n\n if (!nextInput) {\n\n editor.core.log('We are reached the end');\n return;\n\n }\n\n /**\n * When new Block created or deleted content of input\n * We should add some text node to set caret\n */\n if (!nextInput.childNodes.length) {\n\n var emptyTextElement = document.createTextNode('');\n\n nextInput.appendChild(emptyTextElement);\n\n }\n\n editor.caret.inputIndex = index + 1;\n editor.caret.set(nextInput, 0, 0);\n editor.content.workingNodeChanged(nextInput);\n\n };\n\n /**\n * @param {int} index - index of target input.\n * Sets caret to input with this index\n */\n caret.setToBlock = function (index) {\n\n var inputs = editor.state.inputs,\n targetInput = inputs[index];\n\n if ( !targetInput ) {\n\n return;\n\n }\n\n /**\n * When new Block created or deleted content of input\n * We should add some text node to set caret\n */\n if (!targetInput.childNodes.length) {\n\n var emptyTextElement = document.createTextNode('');\n\n targetInput.appendChild(emptyTextElement);\n\n }\n\n editor.caret.inputIndex = index;\n editor.caret.set(targetInput, 0, 0);\n editor.content.workingNodeChanged(targetInput);\n\n };\n\n /**\n * @param {int} index - index of input\n */\n caret.setToPreviousBlock = function (index) {\n\n index = index || 0;\n\n var inputs = editor.state.inputs,\n previousInput = inputs[index - 1],\n lastChildNode,\n lengthOfLastChildNode,\n emptyTextElement;\n\n\n if (!previousInput) {\n\n editor.core.log('We are reached first node');\n return;\n\n }\n\n lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length);\n lengthOfLastChildNode = lastChildNode.length;\n\n /**\n * When new Block created or deleted content of input\n * We should add some text node to set caret\n */\n if (!previousInput.childNodes.length) {\n\n emptyTextElement = document.createTextNode('');\n previousInput.appendChild(emptyTextElement);\n\n }\n editor.caret.inputIndex = index - 1;\n editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode);\n editor.content.workingNodeChanged(inputs[index - 1]);\n\n };\n\n caret.position = {\n\n atStart : function () {\n\n var selection = window.getSelection(),\n anchorOffset = selection.anchorOffset,\n anchorNode = selection.anchorNode,\n firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode),\n pluginsRender = firstLevelBlock.childNodes[0];\n\n if (!editor.core.isDomNode(anchorNode)) {\n\n anchorNode = anchorNode.parentNode;\n\n }\n\n var isFirstNode = anchorNode === pluginsRender.childNodes[0],\n isOffsetZero = anchorOffset === 0;\n\n return isFirstNode && isOffsetZero;\n\n },\n\n atTheEnd : function () {\n\n var selection = window.getSelection(),\n anchorOffset = selection.anchorOffset,\n anchorNode = selection.anchorNode;\n\n /** Caret is at the end of input */\n return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;\n\n }\n };\n\n\n /**\n * Inserts node at the caret location\n * @param {HTMLElement|DocumentFragment} node\n */\n caret.insertNode = function (node) {\n\n var selection, range,\n lastNode = node;\n\n if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) {\n\n lastNode = node.lastChild;\n\n }\n\n selection = window.getSelection();\n\n range = selection.getRangeAt(0);\n range.deleteContents();\n\n range.insertNode(node);\n\n range.setStartAfter(lastNode);\n range.collapse(true);\n\n selection.removeAllRanges();\n selection.addRange(range);\n\n\n };\n\n return caret;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/caret.js","/**\n * Codex Editor Notification Module\n *\n * @author Codex Team\n * @version 1.0\n */\n\nmodule.exports = (function (notifications) {\n\n let editor = codex.editor;\n\n var queue = [];\n\n var addToQueue = function (settings) {\n\n queue.push(settings);\n\n var index = 0;\n\n while ( index < queue.length && queue.length > 5) {\n\n if (queue[index].type == 'confirm' || queue[index].type == 'prompt') {\n\n index++;\n continue;\n\n }\n\n queue[index].close();\n queue.splice(index, 1);\n\n }\n\n };\n\n notifications.createHolder = function () {\n\n var holder = editor.draw.node('DIV', 'cdx-notifications-block');\n\n editor.nodes.notifications = document.body.appendChild(holder);\n\n return holder;\n\n };\n\n\n /**\n * Error notificator. Shows block with message\n * @protected\n */\n notifications.errorThrown = function (errorMsg, event) {\n\n editor.notifications.notification({message: 'This action is not available currently', type: event.type});\n\n };\n\n /**\n *\n * Appends notification\n *\n * settings = {\n * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type\n * message - notification message\n * okMsg - confirm button text (default - 'Ok')\n * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types\n * confirm - function-handler for ok button click\n * cancel - function-handler for cancel button click. Only for confirm and prompt types\n * time - time (in seconds) after which notification will close (default - 10s)\n * }\n *\n * @param settings\n */\n notifications.notification = function (constructorSettings) {\n\n /** Private vars and methods */\n var notification = null,\n cancel = null,\n type = null,\n confirm = null,\n inputField = null;\n\n var confirmHandler = function () {\n\n close();\n\n if (typeof confirm !== 'function' ) {\n\n return;\n\n }\n\n if (type == 'prompt') {\n\n confirm(inputField.value);\n return;\n\n }\n\n confirm();\n\n };\n\n var cancelHandler = function () {\n\n close();\n\n if (typeof cancel !== 'function' ) {\n\n return;\n\n }\n\n cancel();\n\n };\n\n\n /** Public methods */\n function create(settings) {\n\n if (!(settings && settings.message)) {\n\n editor.core.log('Can\\'t create notification. Message is missed');\n return;\n\n }\n\n settings.type = settings.type || 'alert';\n settings.time = settings.time*1000 || 10000;\n\n var wrapper = editor.draw.node('DIV', 'cdx-notification'),\n message = editor.draw.node('DIV', 'cdx-notification__message'),\n input = editor.draw.node('INPUT', 'cdx-notification__input'),\n okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'),\n cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn');\n\n message.textContent = settings.message;\n okBtn.textContent = settings.okMsg || 'ОК';\n cancelBtn.textContent = settings.cancelMsg || 'Отмена';\n\n editor.listeners.add(okBtn, 'click', confirmHandler);\n editor.listeners.add(cancelBtn, 'click', cancelHandler);\n\n wrapper.appendChild(message);\n\n if (settings.type == 'prompt') {\n\n wrapper.appendChild(input);\n\n }\n\n wrapper.appendChild(okBtn);\n\n if (settings.type == 'prompt' || settings.type == 'confirm') {\n\n wrapper.appendChild(cancelBtn);\n\n }\n\n wrapper.classList.add('cdx-notification-' + settings.type);\n wrapper.dataset.type = settings.type;\n\n notification = wrapper;\n type = settings.type;\n confirm = settings.confirm;\n cancel = settings.cancel;\n inputField = input;\n\n if (settings.type != 'prompt' && settings.type != 'confirm') {\n\n window.setTimeout(close, settings.time);\n\n }\n\n };\n\n /**\n * Show notification block\n */\n function send() {\n\n editor.nodes.notifications.appendChild(notification);\n inputField.focus();\n\n editor.nodes.notifications.classList.add('cdx-notification__notification-appending');\n\n window.setTimeout(function () {\n\n editor.nodes.notifications.classList.remove('cdx-notification__notification-appending');\n\n }, 100);\n\n addToQueue({type: type, close: close});\n\n };\n\n /**\n * Remove notification block\n */\n function close() {\n\n notification.remove();\n\n };\n\n\n if (constructorSettings) {\n\n create(constructorSettings);\n send();\n\n }\n\n return {\n create: create,\n send: send,\n close: close\n };\n\n };\n\n notifications.clear = function () {\n\n editor.nodes.notifications.innerHTML = '';\n queue = [];\n\n };\n\n return notifications;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/notifications.js","/**\n * Codex Editor Parser Module\n *\n * @author Codex Team\n * @version 1.1\n */\n\nmodule.exports = (function (parser) {\n\n let editor = codex.editor;\n\n /** inserting text */\n parser.insertPastedContent = function (blockType, tag) {\n\n editor.content.insertBlock({\n type : blockType.type,\n block : blockType.render({\n text : tag.innerHTML\n })\n });\n\n };\n\n /**\n * Check DOM node for display style: separated block or child-view\n */\n parser.isFirstLevelBlock = function (node) {\n\n return node.nodeType == editor.core.nodeTypes.TAG &&\n node.classList.contains(editor.ui.className.BLOCK_CLASSNAME);\n\n };\n\n return parser;\n\n})({});\n\n\n\n// WEBPACK FOOTER //\n// ./modules/parser.js","/**\n * Codex Sanitizer\n */\n\nmodule.exports = (function (sanitizer) {\n\n /** HTML Janitor library */\n let janitor = require('html-janitor');\n\n /** Codex Editor */\n let editor = codex.editor;\n\n sanitizer.prepare = function () {\n\n if (editor.settings.sanitizer && !editor.core.isEmpty(editor.settings.sanitizer)) {\n\n Config.CUSTOM = editor.settings.sanitizer;\n\n }\n\n };\n\n /**\n * Basic config\n */\n var Config = {\n\n /** User configuration */\n CUSTOM : null,\n\n BASIC : {\n\n tags: {\n p: {},\n a: {\n href: true,\n target: '_blank',\n rel: 'nofollow'\n }\n }\n }\n };\n\n sanitizer.Config = Config;\n\n /**\n *\n * @param userCustomConfig\n * @returns {*}\n * @private\n *\n * @description If developer uses editor's API, then he can customize sane restrictions.\n * Or, sane config can be defined globally in editors initialization. That config will be used everywhere\n * At least, if there is no config overrides, that API uses BASIC Default configation\n */\n let init_ = function (userCustomConfig) {\n\n let configuration = userCustomConfig || Config.CUSTOM || Config.BASIC;\n\n return new janitor(configuration);\n\n };\n\n /**\n * Cleans string from unwanted tags\n * @protected\n * @param {String} dirtyString - taint string\n * @param {Object} customConfig - allowed tags\n */\n sanitizer.clean = function (dirtyString, customConfig) {\n\n let janitorInstance = init_(customConfig);\n\n return janitorInstance.clean(dirtyString);\n\n };\n\n return sanitizer;\n\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/sanitizer.js","(function (root, factory) {\n if (typeof define === 'function' && define.amd) {\n define('html-janitor', factory);\n } else if (typeof exports === 'object') {\n module.exports = factory();\n } else {\n root.HTMLJanitor = factory();\n }\n}(this, function () {\n\n /**\n * @param {Object} config.tags Dictionary of allowed tags.\n * @param {boolean} config.keepNestedBlockElements Default false.\n */\n function HTMLJanitor(config) {\n\n var tagDefinitions = config['tags'];\n var tags = Object.keys(tagDefinitions);\n\n var validConfigValues = tags\n .map(function(k) { return typeof tagDefinitions[k]; })\n .every(function(type) { return type === 'object' || type === 'boolean' || type === 'function'; });\n\n if(!validConfigValues) {\n throw new Error(\"The configuration was invalid\");\n }\n\n this.config = config;\n }\n\n // TODO: not exhaustive?\n var blockElementNames = ['P', 'LI', 'TD', 'TH', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'PRE'];\n function isBlockElement(node) {\n return blockElementNames.indexOf(node.nodeName) !== -1;\n }\n\n var inlineElementNames = ['A', 'B', 'STRONG', 'I', 'EM', 'SUB', 'SUP', 'U', 'STRIKE'];\n function isInlineElement(node) {\n return inlineElementNames.indexOf(node.nodeName) !== -1;\n }\n\n HTMLJanitor.prototype.clean = function (html) {\n var sandbox = document.createElement('div');\n sandbox.innerHTML = html;\n\n this._sanitize(sandbox);\n\n return sandbox.innerHTML;\n };\n\n HTMLJanitor.prototype._sanitize = function (parentNode) {\n var treeWalker = createTreeWalker(parentNode);\n var node = treeWalker.firstChild();\n if (!node) { return; }\n\n do {\n // Ignore nodes that have already been sanitized\n if (node._sanitized) {\n continue;\n }\n\n if (node.nodeType === Node.TEXT_NODE) {\n // If this text node is just whitespace and the previous or next element\n // sibling is a block element, remove it\n // N.B.: This heuristic could change. Very specific to a bug with\n // `contenteditable` in Firefox: http://jsbin.com/EyuKase/1/edit?js,output\n // FIXME: make this an option?\n if (node.data.trim() === ''\n && ((node.previousElementSibling && isBlockElement(node.previousElementSibling))\n || (node.nextElementSibling && isBlockElement(node.nextElementSibling)))) {\n parentNode.removeChild(node);\n this._sanitize(parentNode);\n break;\n } else {\n continue;\n }\n }\n\n // Remove all comments\n if (node.nodeType === Node.COMMENT_NODE) {\n parentNode.removeChild(node);\n this._sanitize(parentNode);\n break;\n }\n\n var isInline = isInlineElement(node);\n var containsBlockElement;\n if (isInline) {\n containsBlockElement = Array.prototype.some.call(node.childNodes, isBlockElement);\n }\n\n // Block elements should not be nested (e.g.

  • ...); if\n // they are, we want to unwrap the inner block element.\n var isNotTopContainer = !! parentNode.parentNode;\n var isNestedBlockElement =\n isBlockElement(parentNode) &&\n isBlockElement(node) &&\n isNotTopContainer;\n\n var nodeName = node.nodeName.toLowerCase();\n\n var allowedAttrs = getAllowedAttrs(this.config, nodeName, node);\n\n var isInvalid = isInline && containsBlockElement;\n\n // Drop tag entirely according to the whitelist *and* if the markup\n // is invalid.\n if (isInvalid || shouldRejectNode(node, allowedAttrs)\n || (!this.config.keepNestedBlockElements && isNestedBlockElement)) {\n // Do not keep the inner text of SCRIPT/STYLE elements.\n if (! (node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE')) {\n while (node.childNodes.length > 0) {\n parentNode.insertBefore(node.childNodes[0], node);\n }\n }\n parentNode.removeChild(node);\n\n this._sanitize(parentNode);\n break;\n }\n\n // Sanitize attributes\n for (var a = 0; a < node.attributes.length; a += 1) {\n var attr = node.attributes[a];\n\n if (shouldRejectAttr(attr, allowedAttrs, node)) {\n node.removeAttribute(attr.name);\n // Shift the array to continue looping.\n a = a - 1;\n }\n }\n\n // Sanitize children\n this._sanitize(node);\n\n // Mark node as sanitized so it's ignored in future runs\n node._sanitized = true;\n } while ((node = treeWalker.nextSibling()));\n };\n\n function createTreeWalker(node) {\n return document.createTreeWalker(node,\n NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT,\n null, false);\n }\n\n function getAllowedAttrs(config, nodeName, node){\n if (typeof config.tags[nodeName] === 'function') {\n return config.tags[nodeName](node);\n } else {\n return config.tags[nodeName];\n }\n }\n\n function shouldRejectNode(node, allowedAttrs){\n if (typeof allowedAttrs === 'undefined') {\n return true;\n } else if (typeof allowedAttrs === 'boolean') {\n return !allowedAttrs;\n }\n\n return false;\n }\n\n function shouldRejectAttr(attr, allowedAttrs, node){\n var attrName = attr.name.toLowerCase();\n\n if (allowedAttrs === true){\n return false;\n } else if (typeof allowedAttrs[attrName] === 'function'){\n return !allowedAttrs[attrName](attr.value, node);\n } else if (typeof allowedAttrs[attrName] === 'undefined'){\n return true;\n } else if (allowedAttrs[attrName] === false) {\n return true;\n } else if (typeof allowedAttrs[attrName] === 'string') {\n return (allowedAttrs[attrName] !== attr.value);\n }\n\n return false;\n }\n\n return HTMLJanitor;\n\n}));\n\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/html-janitor/src/html-janitor.js\n// module id = 18\n// module chunks = 0","/**\n * Codex Editor Listeners module\n *\n * @author Codex Team\n * @version 1.0\n */\n\n/**\n * Module-decorator for event listeners assignment\n */\nmodule.exports = function (listeners) {\n\n var allListeners = [];\n\n /**\n * Search methods\n *\n * byElement, byType and byHandler returns array of suitable listeners\n * one and all takes element, eventType, and handler and returns first (all) suitable listener\n *\n */\n listeners.search = function () {\n\n var byElement = function (element, context) {\n\n var listenersOnElement = [];\n\n context = context || allListeners;\n\n for (var i = 0; i < context.length; i++) {\n\n var listener = context[i];\n\n if (listener.element === element) {\n\n listenersOnElement.push(listener);\n\n }\n\n }\n\n return listenersOnElement;\n\n };\n\n var byType = function (eventType, context) {\n\n var listenersWithType = [];\n\n context = context || allListeners;\n\n for (var i = 0; i < context.length; i++) {\n\n var listener = context[i];\n\n if (listener.type === eventType) {\n\n listenersWithType.push(listener);\n\n }\n\n }\n\n return listenersWithType;\n\n };\n\n var byHandler = function (handler, context) {\n\n var listenersWithHandler = [];\n\n context = context || allListeners;\n\n for (var i = 0; i < context.length; i++) {\n\n var listener = context[i];\n\n if (listener.handler === handler) {\n\n listenersWithHandler.push(listener);\n\n }\n\n }\n\n return listenersWithHandler;\n\n };\n\n var one = function (element, eventType, handler) {\n\n var result = allListeners;\n\n if (element)\n result = byElement(element, result);\n\n if (eventType)\n result = byType(eventType, result);\n\n if (handler)\n result = byHandler(handler, result);\n\n return result[0];\n\n };\n\n var all = function (element, eventType, handler) {\n\n var result = allListeners;\n\n if (element)\n result = byElement(element, result);\n\n if (eventType)\n result = byType(eventType, result);\n\n if (handler)\n result = byHandler(handler, result);\n\n return result;\n\n };\n\n return {\n byElement : byElement,\n byType : byType,\n byHandler : byHandler,\n one : one,\n all : all\n };\n\n }();\n\n listeners.add = function (element, eventType, handler, isCapture) {\n\n element.addEventListener(eventType, handler, isCapture);\n\n var data = {\n element: element,\n type: eventType,\n handler: handler\n };\n\n var alreadyAddedListener = listeners.search.one(element, eventType, handler);\n\n if (!alreadyAddedListener) {\n\n allListeners.push(data);\n\n }\n\n };\n\n listeners.remove = function (element, eventType, handler) {\n\n element.removeEventListener(eventType, handler);\n\n var existingListeners = listeners.search.all(element, eventType, handler);\n\n for (var i = 0; i < existingListeners.length; i++) {\n\n var index = allListeners.indexOf(existingListeners[i]);\n\n if (index > 0) {\n\n allListeners.splice(index, 1);\n\n }\n\n }\n\n };\n\n listeners.removeAll = function () {\n\n allListeners.map(function (current) {\n\n listeners.remove(current.element, current.type, current.handler);\n\n });\n\n };\n\n listeners.get = function (element, eventType, handler) {\n\n return listeners.search.all(element, eventType, handler);\n\n };\n\n return listeners;\n\n}({});\n\n\n// WEBPACK FOOTER //\n// ./modules/listeners.js","/**\n * Codex Editor Destroyer module\n *\n * @auhor Codex Team\n * @version 1.0\n */\n\nmodule.exports = function (destroyer) {\n\n let editor = codex.editor;\n\n destroyer.removeNodes = function () {\n\n editor.nodes.wrapper.remove();\n editor.nodes.notifications.remove();\n\n };\n\n destroyer.destroyPlugins = function () {\n\n for (var tool in editor.tools) {\n\n if (typeof editor.tools[tool].destroy === 'function') {\n\n editor.tools[tool].destroy();\n\n }\n\n }\n\n };\n\n destroyer.destroyScripts = function () {\n\n var scripts = document.getElementsByTagName('SCRIPT');\n\n for (var i = 0; i < scripts.length; i++) {\n\n if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) {\n\n scripts[i].remove();\n i--;\n\n }\n\n }\n\n };\n\n\n /**\n * Delete editor data from webpage.\n * You should send settings argument with boolean flags:\n * @param settings.ui- remove redactor event listeners and DOM nodes\n * @param settings.scripts - remove redactor scripts from DOM\n * @param settings.plugins - remove plugin's objects\n * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true\n * }\n *\n */\n destroyer.destroy = function (settings) {\n\n if (!settings || typeof settings !== 'object') {\n\n return;\n\n }\n\n if (settings.ui) {\n\n destroyer.removeNodes();\n editor.listeners.removeAll();\n\n }\n\n if (settings.scripts) {\n\n destroyer.destroyScripts();\n\n }\n\n if (settings.plugins) {\n\n destroyer.destroyPlugins();\n\n }\n\n if (settings.ui && settings.scripts && settings.core) {\n\n delete codex.editor;\n\n }\n\n };\n\n return destroyer;\n\n}({});\n\n\n// WEBPACK FOOTER //\n// ./modules/destroyer.js","/**\n * Codex Editor Paste module\n *\n * @author Codex Team\n * @version 1.1.1\n */\n\nmodule.exports = function (paste) {\n\n let editor = codex.editor;\n\n var patterns = [];\n\n paste.prepare = function () {\n\n var tools = editor.tools;\n\n for (var tool in tools) {\n\n if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) {\n\n continue;\n\n }\n\n tools[tool].renderOnPastePatterns.map(function (pattern) {\n\n\n patterns.push(pattern);\n\n });\n\n }\n\n return Promise.resolve();\n\n };\n\n /**\n * Saves data\n * @param event\n */\n paste.pasted = function (event) {\n\n var clipBoardData = event.clipboardData || window.clipboardData,\n content = clipBoardData.getData('Text');\n\n var result = analize(content);\n\n if (result) {\n\n event.preventDefault();\n event.stopImmediatePropagation();\n\n }\n\n return result;\n\n };\n\n /**\n * Analizes pated string and calls necessary method\n */\n\n var analize = function (string) {\n\n var result = false,\n content = editor.content.currentNode,\n plugin = content.dataset.tool;\n\n patterns.map( function (pattern) {\n\n var execArray = pattern.regex.exec(string),\n match = execArray && execArray[0];\n\n if ( match && match === string.trim()) {\n\n /** current block is not empty */\n if ( content.textContent.trim() && plugin == editor.settings.initialBlockPlugin ) {\n\n pasteToNewBlock_();\n\n }\n\n pattern.callback(string, pattern);\n result = true;\n\n }\n\n });\n\n return result;\n\n };\n\n var pasteToNewBlock_ = function () {\n\n /** Create new initial block */\n editor.content.insertBlock({\n\n type : editor.settings.initialBlockPlugin,\n block : editor.tools[editor.settings.initialBlockPlugin].render({\n text : ''\n })\n\n }, false);\n\n };\n\n /**\n * This method prevents default behaviour.\n *\n * @param {Object} event\n * @protected\n *\n * @description We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.\n * Firstly, we need to memorize the caret position. We can do that by getting the range of selection.\n * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node\n */\n paste.blockPasteCallback = function (event) {\n\n\n if (!needsToHandlePasteEvent(event.target)) {\n\n return;\n\n }\n\n /** Prevent default behaviour */\n event.preventDefault();\n\n /** get html pasted data - dirty data */\n var htmlData = event.clipboardData.getData('text/html'),\n plainData = event.clipboardData.getData('text/plain');\n\n /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/\n var paragraphs = editor.draw.node('DIV', '', {}),\n cleanData,\n wrappedData;\n\n /** Create fragment, that we paste to range after proccesing */\n cleanData = editor.sanitizer.clean(htmlData);\n\n /**\n * We wrap pasted text with

    tags to split it logically\n * @type {string}\n */\n wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData);\n paragraphs.innerHTML = wrappedData;\n\n /**\n * If there only one paragraph, just insert in at the caret location\n */\n if (paragraphs.childNodes.length == 1) {\n\n emulateUserAgentBehaviour(paragraphs.firstChild);\n return;\n\n }\n\n insertPastedParagraphs(paragraphs.childNodes);\n\n };\n\n /**\n * Checks if we should handle paste event on block\n * @param block\n *\n * @return {boolean}\n */\n var needsToHandlePasteEvent = function (block) {\n\n /** If area is input or textarea then allow default behaviour */\n if ( editor.core.isNativeInput(block) ) {\n\n return false;\n\n }\n\n var editableParent = editor.content.getEditableParent(block);\n\n /** Allow paste when event target placed in Editable element */\n if (!editableParent) {\n\n return false;\n\n }\n\n return true;\n\n };\n\n /**\n * Inserts new initial plugin blocks with data in paragraphs\n *\n * @param {Array} paragraphs - array of paragraphs (

    ) whit content, that should be inserted\n */\n var insertPastedParagraphs = function (paragraphs) {\n\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin,\n currentNode = editor.content.currentNode;\n\n\n paragraphs.forEach(function (paragraph) {\n\n /** Don't allow empty paragraphs */\n if (editor.core.isBlockEmpty(paragraph)) {\n\n return;\n\n }\n\n editor.content.insertBlock({\n type : NEW_BLOCK_TYPE,\n block : editor.tools[NEW_BLOCK_TYPE].render({\n text : paragraph.innerHTML\n })\n });\n\n editor.caret.inputIndex++;\n\n });\n\n editor.caret.setToPreviousBlock(editor.caret.getCurrentInputIndex() + 1);\n\n\n /**\n * If there was no data in working node, remove it\n */\n if (editor.core.isBlockEmpty(currentNode)) {\n\n currentNode.remove();\n editor.ui.saveInputs();\n\n }\n\n\n };\n\n /**\n * Inserts node content at the caret position\n *\n * @param {Node} node - DOM node (could be DocumentFragment), that should be inserted at the caret location\n */\n var emulateUserAgentBehaviour = function (node) {\n\n var newNode;\n\n if (node.childElementCount) {\n\n newNode = document.createDocumentFragment();\n\n node.childNodes.forEach(function (current) {\n\n if (!editor.core.isDomNode(current) && current.data.trim() === '') {\n\n return;\n\n }\n\n newNode.appendChild(current.cloneNode(true));\n\n });\n\n } else {\n\n newNode = document.createTextNode(node.textContent);\n\n }\n\n editor.caret.insertNode(newNode);\n\n };\n\n\n return paste;\n\n}({});\n\n\n// WEBPACK FOOTER //\n// ./modules/paste.js"],"sourceRoot":""}