diff --git a/codex-editor.js b/codex-editor.js index 77e0aa24..5108f484 100644 --- a/codex-editor.js +++ b/codex-editor.js @@ -1,5000 +1,3 @@ -var codex = -/******/ (function(modules) { // webpackBootstrap -/******/ // The module cache -/******/ var installedModules = {}; -/******/ -/******/ // The require function -/******/ function __webpack_require__(moduleId) { -/******/ -/******/ // Check if module is in cache -/******/ if(installedModules[moduleId]) -/******/ return installedModules[moduleId].exports; -/******/ -/******/ // Create a new module (and put it into the cache) -/******/ var module = installedModules[moduleId] = { -/******/ exports: {}, -/******/ id: moduleId, -/******/ loaded: false -/******/ }; -/******/ -/******/ // Execute the module function -/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); -/******/ -/******/ // Flag the module as loaded -/******/ module.loaded = true; -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; -/******/ } -/******/ -/******/ -/******/ // expose the modules object (__webpack_modules__) -/******/ __webpack_require__.m = modules; -/******/ -/******/ // expose the module cache -/******/ __webpack_require__.c = installedModules; -/******/ -/******/ // __webpack_public_path__ -/******/ __webpack_require__.p = ""; -/******/ -/******/ // Load entry module and return exports -/******/ return __webpack_require__(0); -/******/ }) -/************************************************************************/ -/******/ ([ -/* 0 */ -/***/ function(module, exports, __webpack_require__) { - - /** - * - */ - - 'use strict'; - - var editor = __webpack_require__(1); - module.exports = editor; - -/***/ }, -/* 1 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - /** - * - * Codex Editor - * - * @author Codex Team - * @version 1.3.0 - */ - - var codex = function (codex) { - - var init = function init() { - - codex.core = __webpack_require__(2); - codex.ui = __webpack_require__(3); - codex.transport = __webpack_require__(4); - codex.renderer = __webpack_require__(5); - codex.saver = __webpack_require__(6); - codex.content = __webpack_require__(7); - codex.toolbar = __webpack_require__(8); - codex.tools = __webpack_require__(12); - codex.callback = __webpack_require__(13); - codex.draw = __webpack_require__(14); - codex.caret = __webpack_require__(15); - codex.notifications = __webpack_require__(16); - codex.parser = __webpack_require__(17); - codex.sanitizer = __webpack_require__(18); - codex.comments = __webpack_require__(20); - }; - - codex.version = ("1.4.0"); - - /** - * @public - * - * holds initial settings - */ - codex.settings = { - tools: ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'], - textareaId: 'codex-editor', - uploadImagesUrl: '/editor/transport/', - - // Type of block showing on empty editor - initialBlockPlugin: "paragraph" - }; - - /** - * public - * - * Static nodes - */ - codex.nodes = { - textarea: null, - wrapper: null, - toolbar: null, - inlineToolbar: { - wrapper: null, - buttons: null, - actions: null - }, - toolbox: null, - notifications: null, - plusButton: null, - showSettingsButton: null, - showTrashButton: null, - showCommentButton: null, - blockSettings: null, - pluginSettings: null, - defaultSettings: null, - toolbarButtons: {}, // { type : DomEl, ... } - redactor: null, - commentsSidebar: null - }; - - /** - * @public - * - * Output state - */ - codex.state = { - jsonOutput: [], - blocks: [], - inputs: [], - comments: [] - }; - - /** - * Initialization - * @uses Promise cEditor.core.prepare - * @param {} userSettings are : - * - tools [], - * - textareaId String - * ... - * - * Load user defined tools - * Tools must contain this important objects : - * @param {String} type - this is a type of plugin. It can be used as plugin name - * @param {String} iconClassname - this a icon in toolbar - * @param {Object} make - what should plugin do, when it is clicked - * @param {Object} appendCallback - callback after clicking - * @param {Element} settings - what settings does it have - * @param {Object} render - plugin get JSON, and should return HTML - * @param {Object} save - plugin gets HTML content, returns JSON - * @param {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE - * @param {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE - * - * @example - * - type : 'header', - * - iconClassname : 'ce-icon-header', - * - make : headerTool.make, - * - appendCallback : headerTool.appendCallback, - * - settings : headerTool.makeSettings(), - * - render : headerTool.render, - * - save : headerTool.save, - * - displayInToolbox : true, - * - enableLineBreaks : false - */ - codex.start = function (userSettings) { - - init(); - - this.core.prepare(userSettings) - - // If all ok, make UI, bind events and parse initial-content - .then(this.ui.make).then(this.ui.addTools).then(this.ui.bindEvents).then(this.ui.preparePlugins).then(this.transport.prepare).then(this.renderer.makeBlocksFromData).then(this.ui.saveInputs).catch(function (error) { - codex.core.log('Initialization failed with error: %o', 'warn', error); - }); - }; - - return codex; - }({}); - - module.exports = codex; - -/***/ }, -/* 2 */ -/***/ function(module, exports) { - - 'use strict'; - - var _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; }; - - /** - * Codex Editor Core - * - * @author Codex Team - * @version 1.1.2 - */ - - var editor = codex.editor; - - module.exports = function (core) { - - /** - * @public - * - * Editor preparing method - * @return Promise - */ - core.prepare = function (userSettings) { - - return new Promise(function (resolve, reject) { - - if (userSettings) { - - editor.settings.tools = userSettings.tools || editor.settings.tools; - } - - if (userSettings.data) { - - editor.state.blocks = userSettings.data; - } - - if (userSettings.initialBlockPlugin) { - - editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin; - } - - if (userSettings.uploadImagesUrl) { - - editor.settings.uploadImagesUrl = userSettings.uploadImagesUrl; - } - - editor.nodes.textarea = document.getElementById(userSettings.textareaId || editor.settings.textareaId); - - if (_typeof(editor.nodes.textarea) === undefined || editor.nodes.textarea === null) { - - reject(Error("Textarea wasn't found by ID: #" + userSettings.textareaId)); - } else { - - resolve(); - } - }); - }; - - /** - * Logging method - * @param type = ['log', 'info', 'warn'] - */ - core.log = function (msg, type, arg) { - - type = type || 'log'; - - if (!arg) { - - arg = msg || 'undefined'; - msg = '[codex-editor]: %o'; - } else { - - msg = '[codex-editor]: ' + msg; - } - - try { - - if ('console' in window && window.console[type]) { - - if (arg) window.console[type](msg, arg);else window.console[type](msg); - } - } catch (e) {} - }; - - /** - * @protected - * - * Helper for insert one element after another - */ - core.insertAfter = function (target, element) { - - target.parentNode.insertBefore(element, target.nextSibling); - }; - - /** - * @const - * - * Readable DOM-node types map - */ - core.nodeTypes = { - TAG: 1, - TEXT: 3, - COMMENT: 8 - }; - - /** - * @const - * Readable keys map - */ - 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 }; - - /** - * @protected - * - * Check object for DOM node - */ - core.isDomNode = function (el) { - - return el && (typeof el === 'undefined' ? 'undefined' : _typeof(el)) === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG; - }; - - /** - * Native Ajax - */ - core.ajax = function (data) { - - if (!data || !data.url) { - - return; - } - - var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'), - successFunction = function successFunction() {}, - params = '', - obj; - - data.async = true; - data.type = data.type || 'GET'; - data.data = data.data || ''; - data['content-type'] = data['content-type'] || 'application/json; charset=utf-8'; - successFunction = data.success || successFunction; - - if (data.type == 'GET' && data.data) { - - data.url = /\?/.test(data.url) ? data.url + '&' + data.data : data.url + '?' + data.data; - } else { - - for (obj in data.data) { - - params += obj + '=' + encodeURIComponent(data.data[obj]) + '&'; - } - } - - if (data.withCredentials) { - - XMLHTTP.withCredentials = true; - } - - if (data.beforeSend && typeof data.beforeSend == 'function') { - - data.beforeSend.call(); - } - - XMLHTTP.open(data.type, data.url, data.async); - XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded'); - - XMLHTTP.onreadystatechange = function () { - - if (XMLHTTP.readyState == 4 && XMLHTTP.status == 200) { - - successFunction(XMLHTTP.responseText); - } - }; - - XMLHTTP.send(params); - }; - - /** - * Appends script to head of document - * @return Promise - */ - core.importScript = function (scriptPath, instanceName) { - - return new Promise(function (resolve, reject) { - - var instancePrefix = 'cdx-script-'; - - var script = void 0; - - /** Script is already loaded */ - if (!instanceName) { - - reject('Instance name is missed'); - } else if (document.getElementById(instancePrefix + instanceName)) { - - resolve(scriptPath); - } - - script = document.createElement('SCRIPT'); - script.async = true; - script.defer = true; - script.id = instancePrefix + instanceName; - - script.onload = function () { - - resolve(scriptPath); - }; - - script.onerror = function () { - - reject(scriptPath); - }; - - script.src = scriptPath; - document.head.appendChild(script); - }); - }; - - return core; - }({}); - -/***/ }, -/* 3 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor UI module - * - * @author Codex Team - * @version 1.1 - */ - - var ui = function (ui) { - - /** - * Basic editor classnames - */ - ui.className = { - - /** - * @const {string} BLOCK_CLASSNAME - redactor blocks name - */ - BLOCK_CLASSNAME: 'ce-block', - - /** - * @const {String} wrapper for plugins content - */ - BLOCK_CONTENT: 'ce-block__content', - - /** - * @const {String} BLOCK_STRETCHED - makes block stretched - */ - BLOCK_STRETCHED: 'ce-block--stretched', - - /** - * @const {String} BLOCK_HIGHLIGHTED - adds background - */ - BLOCK_HIGHLIGHTED: 'ce-block--focused', - - /** - * @const {String} - highlights covered blocks - */ - BLOCK_IN_FEED_MODE: 'ce-block--feed-mode', - - /** - * @const {String} - for all default settings - */ - SETTINGS_ITEM: 'ce-settings__item' - - }; - - /** - * @protected - * - * Making main interface - */ - ui.make = function () { - - var wrapper, toolbar, toolbarContent, inlineToolbar, redactor, commentsSidebar, ceBlock, notifications, blockButtons, blockSettings, showSettingsButton, showTrashButton, showCommentButton, toolbox, plusButton; - - /** Make editor wrapper */ - wrapper = codex.draw.wrapper(); - - /** Append editor wrapper after initial textarea */ - codex.core.insertAfter(codex.nodes.textarea, wrapper); - - /** Append block with notifications to the document */ - notifications = codex.draw.alertsHolder(); - codex.nodes.notifications = document.body.appendChild(notifications); - - /** Make toolbar and content-editable redactor */ - toolbar = codex.draw.toolbar(); - toolbarContent = codex.draw.toolbarContent(); - inlineToolbar = codex.draw.inlineToolbar(); - plusButton = codex.draw.plusButton(); - showSettingsButton = codex.draw.settingsButton(); - showCommentButton = codex.draw.commentButton(); - showTrashButton = codex.toolbar.settings.makeRemoveBlockButton(); - blockSettings = codex.draw.blockSettings(); - blockButtons = codex.draw.blockButtons(); - toolbox = codex.draw.toolbox(); - redactor = codex.draw.redactor(); - commentsSidebar = codex.draw.commentsSidebar(); - - /** settings */ - var defaultSettings = codex.draw.defaultSettings(), - pluginSettings = codex.draw.pluginsSettings(); - - /** Add default and plugins settings */ - blockSettings.appendChild(pluginSettings); - blockSettings.appendChild(defaultSettings); - - /** Make blocks buttons - * This block contains settings button and remove block button - */ - blockButtons.appendChild(showSettingsButton); - blockButtons.appendChild(showTrashButton); - blockButtons.appendChild(showCommentButton); - blockButtons.appendChild(blockSettings); - - /** Append plus button */ - toolbarContent.appendChild(plusButton); - - /** Appending toolbar tools */ - toolbarContent.appendChild(toolbox); - - /** Appending first-level block buttons */ - toolbar.appendChild(blockButtons); - - /** Append toolbarContent to toolbar */ - toolbar.appendChild(toolbarContent); - - wrapper.appendChild(toolbar); - - wrapper.appendChild(commentsSidebar); - wrapper.appendChild(redactor); - - /** Save created ui-elements to static nodes state */ - codex.nodes.wrapper = wrapper; - codex.nodes.toolbar = toolbar; - codex.nodes.plusButton = plusButton; - codex.nodes.toolbox = toolbox; - codex.nodes.blockSettings = blockSettings; - codex.nodes.pluginSettings = pluginSettings; - codex.nodes.defaultSettings = defaultSettings; - codex.nodes.showSettingsButton = showSettingsButton; - codex.nodes.showTrashButton = showTrashButton; - codex.nodes.showCommentButton = showCommentButton; - codex.nodes.commentsSidebar = commentsSidebar; - - codex.nodes.redactor = redactor; - - codex.ui.makeInlineToolbar(inlineToolbar); - - /** fill in default settings */ - codex.toolbar.settings.addDefaultSettings(); - }; - - ui.makeInlineToolbar = function (container) { - - /** Append to redactor new inline block */ - codex.nodes.inlineToolbar.wrapper = container; - - /** Draw toolbar buttons */ - codex.nodes.inlineToolbar.buttons = codex.draw.inlineToolbarButtons(); - - /** Buttons action or settings */ - codex.nodes.inlineToolbar.actions = codex.draw.inlineToolbarActions(); - - /** Append to inline toolbar buttons as part of it */ - codex.nodes.inlineToolbar.wrapper.appendChild(codex.nodes.inlineToolbar.buttons); - codex.nodes.inlineToolbar.wrapper.appendChild(codex.nodes.inlineToolbar.actions); - - codex.nodes.wrapper.appendChild(codex.nodes.inlineToolbar.wrapper); - }; - - /** - * @private - * Append tools passed in codex.tools - */ - ui.addTools = function () { - - var tool, tool_button; - - for (var name in codex.settings.tools) { - tool = codex.settings.tools[name]; - codex.tools[name] = tool; - } - - /** Make toolbar buttons */ - for (var name in codex.tools) { - - tool = codex.tools[name]; - - if (!tool.displayInToolbox) { - continue; - } - - if (!tool.iconClassname) { - codex.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', name); - continue; - } - - if (typeof tool.render != 'function') { - codex.core.log('render method missed. Tool %o skipped', 'warn', name); - continue; - } - - /** - * if tools is for toolbox - */ - tool_button = codex.draw.toolbarButton(name, tool.iconClassname); - - codex.nodes.toolbox.appendChild(tool_button); - - /** Save tools to static nodes */ - codex.nodes.toolbarButtons[name] = tool_button; - } - - /** - * Add inline toolbar tools - */ - codex.ui.addInlineToolbarTools(); - }; - - ui.addInlineToolbarTools = function () { - - var tools = { - - bold: { - icon: 'ce-icon-bold', - command: 'bold' - }, - - italic: { - icon: 'ce-icon-italic', - command: 'italic' - }, - - underline: { - icon: 'ce-icon-underline', - command: 'underline' - }, - - link: { - icon: 'ce-icon-link', - command: 'createLink' - } - }; - - var toolButton, tool; - - for (var name in tools) { - - tool = tools[name]; - - toolButton = codex.draw.toolbarButtonInline(name, tool.icon); - - codex.nodes.inlineToolbar.buttons.appendChild(toolButton); - /** - * Add callbacks to this buttons - */ - codex.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command); - } - }; - - /** - * @private - * Bind editor UI events - */ - ui.bindEvents = function () { - - codex.core.log('ui.bindEvents fired', 'info'); - - // window.addEventListener('error', function (errorMsg, url, lineNumber) { - // codex.notifications.errorThrown(errorMsg, event); - // }, false ); - - /** All keydowns on Document */ - document.addEventListener('keydown', codex.callback.globalKeydown, false); - - /** All keydowns on Redactor zone */ - codex.nodes.redactor.addEventListener('keydown', codex.callback.redactorKeyDown, false); - - /** All keydowns on Document */ - document.addEventListener('keyup', codex.callback.globalKeyup, false); - - /** - * Mouse click to radactor - */ - codex.nodes.redactor.addEventListener('click', codex.callback.redactorClicked, false); - - /** - * Clicks to the Plus button - */ - codex.nodes.plusButton.addEventListener('click', codex.callback.plusButtonClicked, false); - - /** - * Clicks to SETTINGS button in toolbar - */ - codex.nodes.showSettingsButton.addEventListener('click', codex.callback.showSettingsButtonClicked, false); - - /** - * Clicks to COMMENT button in toolbar - */ - codex.nodes.showCommentButton.addEventListener('click', codex.callback.showCommentButtonClicked, false); - - /** - * @deprecated ( but now in use for syncronization ); - * Any redactor changes: keyboard input, mouse cut/paste, drag-n-drop text - */ - codex.nodes.redactor.addEventListener('input', codex.callback.redactorInputEvent, false); - - /** Bind click listeners on toolbar buttons */ - for (var button in codex.nodes.toolbarButtons) { - codex.nodes.toolbarButtons[button].addEventListener('click', codex.callback.toolbarButtonClicked, false); - } - }; - - /** - * Initialize plugins before using - * Ex. Load scripts or call some internal methods - * @return Promise - */ - ui.preparePlugins = function () { - - return new Promise(function (resolve, reject) { - - var pluginName = void 0, - plugin = void 0; - - for (pluginName in codex.tools) { - - plugin = codex.tools[pluginName]; - - if (typeof plugin.prepare != 'function') { - continue; - } - - plugin.prepare(plugin.config || {}).then(function () { - resolve(); - }).catch(function (error) { - reject(error); - }); - } - }); - }; - - ui.addBlockHandlers = function (block) { - - if (!block) return; - - /** - * Block keydowns - */ - block.addEventListener('keydown', function (event) { - codex.callback.blockKeydown(event, block); - }, false); - - /** - * Pasting content from another source - * We have two type of sanitization - * First - uses deep-first search algorithm to get sub nodes, - * sanitizes whole Block_content and replaces cleared nodes - * This method is deprecated - * Method is used in codex.callback.blockPaste(event) - * - * Secont - uses Mutation observer. - * Observer "observe" DOM changes and send changings to callback. - * Callback gets changed node, not whole Block_content. - * Inserted or changed node, which we've gotten have been cleared and replaced with diry node - * - * Method is used in codex.callback.blockPasteViaSanitize(event) - * - * @uses html-janitor - * @example codex.callback.blockPasteViaSanitize(event), the second method. - * - */ - block.addEventListener('paste', codex.callback.blockPasteCallback, false); - - block.addEventListener('mouseup', function () { - codex.toolbar.inline.show(); - }, false); - }; - - /** getting all contenteditable elements */ - ui.saveInputs = function () { - - var redactor = codex.nodes.redactor, - elements = []; - - /** Save all inputs in global variable state */ - codex.state.inputs = redactor.querySelectorAll('[contenteditable], input'); - }; - - /** - * Adds first initial block on empty redactor - */ - ui.addInitialBlock = function () { - - var initialBlockType = codex.settings.initialBlockPlugin, - initialBlock; - - if (!codex.tools[initialBlockType]) { - codex.core.log('Plugin %o was not implemented and can\'t be used as initial block', 'warn', initialBlockType); - return; - } - - initialBlock = codex.tools[initialBlockType].render(); - - initialBlock.setAttribute('data-placeholder', 'Расскажите свою историю...'); - - codex.content.insertBlock({ - type: initialBlockType, - block: initialBlock - }); - - codex.content.workingNodeChanged(initialBlock); - }; - - ui.setInlineToolbarButtonBehaviour = function (button, type) { - - button.addEventListener('mousedown', function (event) { - - codex.toolbar.inline.toolClicked(event, type); - }, false); - }; - - return ui; - }({}); - - module.exports = ui; - -/***/ }, -/* 4 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * - * Codex.Editor Transport Module - * - * @author Codex Team - * @version 1.0 - */ - var editor = codex.editor; - - module.exports = function (transport) { - - transport.input = null; - - /** - * @property {Object} arguments - keep plugin settings and defined callbacks - */ - transport.arguments = null; - - transport.prepare = function () { - - var input = document.createElement('INPUT'); - - input.type = 'file'; - input.addEventListener('change', editor.transport.fileSelected); - - editor.transport.input = input; - }; - - /** Clear input when files is uploaded */ - transport.clearInput = function () { - - /** Remove old input */ - this.input = null; - - /** Prepare new one */ - this.prepare(); - }; - - /** - * Callback for file selection - * @param {Event} event - */ - transport.fileSelected = function () { - - var input = this, - files = input.files, - formdData = new FormData(); - - formdData.append('files', files[0], files[0].name); - - editor.transport.ajax({ - data: formdData, - beforeSend: editor.transport.arguments.beforeSend, - success: editor.transport.arguments.success, - error: editor.transport.arguments.error - }); - }; - - /** - * Use plugin callbacks - * @protected - */ - transport.selectAndUpload = function (args) { - - this.arguments = args; - this.input.click(); - }; - - /** - * Ajax requests module - * @todo use core.ajax - */ - transport.ajax = function (params) { - - var xhr = new XMLHttpRequest(), - beforeSend = typeof params.beforeSend == 'function' ? params.beforeSend : function () {}, - success = typeof params.success == 'function' ? params.success : function () {}, - error = typeof params.error == 'function' ? params.error : function () {}; - - beforeSend(); - - xhr.open('POST', editor.settings.uploadImagesUrl, true); - - xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - - xhr.onload = function () { - - if (xhr.status === 200) { - - success(xhr.responseText); - } else { - - editor.core.log('request error: %o', xhr); - error(); - } - }; - - xhr.send(params.data); - this.clearInput(); - }; - - return transport; - }({}); - -/***/ }, -/* 5 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Renderer Module - * - * @author Codex Team - * @version 1.0 - */ - - var editor = codex.editor; - - module.exports = function (renderer) { - - /** - * Asyncronously parses input JSON to redactor blocks - */ - renderer.makeBlocksFromData = function () { - - /** - * If redactor is empty, add first paragraph to start writing - */ - if (!editor.state.blocks.items.length) { - - editor.ui.addInitialBlock(); - return; - } - - Promise.resolve() - - /** First, get JSON from state */ - .then(function () { - - return editor.state.blocks; - }) - - /** Then, start to iterate they */ - .then(editor.renderer.appendBlocks) - - /** Write log if something goes wrong */ - .catch(function (error) { - - editor.core.log('Error while parsing JSON: %o', 'error', error); - }); - }; - - /** - * Parses JSON to blocks - * @param {object} data - * @return Primise -> nodeList - */ - renderer.appendBlocks = function (data) { - - var blocks = data.items; - - /** - * Sequence of one-by-one blocks appending - * Uses to save blocks order after async-handler - */ - var nodeSequence = Promise.resolve(); - - for (var index = 0; index < blocks.length; index++) { - - /** Add node to sequence at specified index */ - editor.renderer.appendNodeAtIndex(nodeSequence, blocks, index); - } - }; - - /** - * Append node at specified index - */ - renderer.appendNodeAtIndex = function (nodeSequence, blocks, index) { - - /** We need to append node to sequence */ - nodeSequence - - /** first, get node async-aware */ - .then(function () { - - return editor.renderer.getNodeAsync(blocks, index); - }) - - /** - * second, compose editor-block from JSON object - */ - .then(editor.renderer.createBlockFromData) - - /** - * now insert block to redactor - */ - .then(function (blockData) { - - /** - * blockData has 'block', 'type' and 'stretched' information - */ - editor.content.insertBlock(blockData); - - /** Pass created block to next step */ - return blockData.block; - }) - - /** Log if something wrong with node */ - .catch(function (error) { - - editor.core.log('Node skipped while parsing because %o', 'error', error); - }); - }; - - /** - * Asynchronously returns block data from blocksList by index - * @return Promise to node - */ - renderer.getNodeAsync = function (blocksList, index) { - - return Promise.resolve().then(function () { - - return blocksList[index]; - }); - }; - - /** - * Creates editor block by JSON-data - * - * @uses render method of each plugin - * - * @param {object} blockData looks like - * { header : { - * text: '', - * type: 'H3', ... - * } - * } - * @return {object} with type and Element - */ - renderer.createBlockFromData = function (blockData) { - - /** New parser */ - var pluginName = blockData.type, - cover = blockData.cover; - - /** Get first key of object that stores plugin name */ - // for (var pluginName in blockData) break; - - /** Check for plugin existance */ - if (!editor.tools[pluginName]) { - - throw Error('Plugin \xAB' + pluginName + '\xBB not found'); - } - - /** Check for plugin having render method */ - if (typeof editor.tools[pluginName].render != 'function') { - - throw Error('Plugin \xAB' + pluginName + '\xBB must have \xABrender\xBB method'); - } - - /** New Parser */ - var block = editor.tools[pluginName].render(blockData.data); - - /** is first-level block stretched */ - var stretched = editor.tools[pluginName].isStretched || false; - - /** Retrun type and block */ - return { - type: pluginName, - block: block, - stretched: stretched, - cover: cover - }; - }; - - return renderer; - }({}); - -/***/ }, -/* 6 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Saver - * - * @author Codex Team - * @version 1.0.2 - */ - - var saver = function (saver) { - - /** - * Saves blocks - * @private - */ - saver.saveBlocks = function () { - - /** Save html content of redactor to memory */ - codex.state.html = codex.nodes.redactor.innerHTML; - - /** Empty jsonOutput state */ - codex.state.jsonOutput = []; - - Promise.resolve().then(function () { - return codex.nodes.redactor.childNodes; - }) - /** Making a sequence from separate blocks */ - .then(codex.saver.makeQueue).then(function () { - // codex.nodes.textarea.innerHTML = codex.state.html; - }).catch(function (error) { - console.log('Something happend'); - }); - }; - - saver.makeQueue = function (blocks) { - - var queue = Promise.resolve(); - - for (var index = 0; index < blocks.length; index++) { - - /** Add node to sequence at specified index */ - codex.saver.getBlockData(queue, blocks, index); - } - }; - - /** Gets every block and makes From Data */ - saver.getBlockData = function (queue, blocks, index) { - - queue.then(function () { - return codex.saver.getNodeAsync(blocks, index); - }).then(codex.saver.makeFormDataFromBlocks); - }; - - /** - * Asynchronously returns block data from blocksList by index - * @return Promise to node - */ - saver.getNodeAsync = function (blocksList, index) { - - return Promise.resolve().then(function () { - - return blocksList[index]; - }); - }; - - saver.makeFormDataFromBlocks = function (block) { - - var pluginName = block.dataset.tool, - id = block.dataset.id; - - /** Check for plugin existance */ - if (!codex.tools[pluginName]) { - throw Error('Plugin \xAB' + pluginName + '\xBB not found'); - } - - /** Check for plugin having render method */ - if (typeof codex.tools[pluginName].save != 'function') { - - throw Error('Plugin \xAB' + pluginName + '\xBB must have save method'); - } - - /** Result saver */ - var blockContent = block.childNodes[0], - pluginsContent = blockContent.childNodes[0], - savedData = codex.tools[pluginName].save(pluginsContent), - output; - - output = { - id: id, - type: pluginName, - data: savedData - }; - - if (codex.tools[pluginName].validate) { - var result = codex.tools[pluginName].validate(savedData); - - /** - * Do not allow invalid data - */ - if (!result) return; - } - - /** Marks Blocks that will be in main page */ - output.cover = block.classList.contains(codex.ui.className.BLOCK_IN_FEED_MODE); - - codex.state.jsonOutput.push(output); - }; - - saver.saveComments = function () { - - var fields = codex.nodes.commentsSidebar.querySelectorAll('.ce-comments-field'); - - for (var i = 0; i < fields.length; i++) { - - var comments = fields[i].querySelectorAll('.ce-comment'), - commentsData = []; - - for (var j = 0; j < comments.length; j++) { - - var text = comments[j].querySelector('.ce-comment__text'), - time = comments[j].querySelector('.ce-comment__time'); - - if (!text) continue; - - var comment = { - edited: comments[j].dataset.edited, - text: text.textContent, - time: time.textContent - }; - - commentsData.push(comment); - } - - if (!commentsData.length) continue; - - var fieldData = { - blockId: fields[i].dataset.blockId, - comments: commentsData - }; - - codex.state.comments.push(fieldData); - } - }; - - return saver; - }({}); - - module.exports = saver; - -/***/ }, -/* 7 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Content Module - * Works with DOM - * - * @author Codex Team - * @version 1.3.11 - */ - - var content = function (content) { - - /** - * Links to current active block - * @type {null | Element} - */ - content.currentNode = null; - - /** - * clicked in redactor area - * @type {null | Boolean} - */ - content.editorAreaHightlighted = null; - - /** - * Synchronizes redactor with original textarea - */ - content.sync = function () { - - codex.core.log('syncing...'); - - /** - * Save redactor content to codex.state - */ - codex.state.html = codex.nodes.redactor.innerHTML; - }; - - /** - * @deprecated - */ - content.getNodeFocused = function () { - - var selection = window.getSelection(), - focused; - - if (selection.anchorNode === null) { - return null; - } - - if (selection.anchorNode.nodeType == codex.core.nodeTypes.TAG) { - focused = selection.anchorNode; - } else { - focused = selection.focusNode.parentElement; - } - - if (!codex.parser.isFirstLevelBlock(focused)) { - - /** Iterate with parent nodes to find first-level*/ - var parent = focused.parentNode; - - while (parent && !codex.parser.isFirstLevelBlock(parent)) { - parent = parent.parentNode; - } - - focused = parent; - } - - if (focused != codex.nodes.redactor) { - return focused; - } - - return null; - }; - - /** - * Appends background to the block - */ - content.markBlock = function () { - - codex.content.currentNode.classList.add(codex.ui.className.BLOCK_HIGHLIGHTED); - }; - - /** - * Clear background - */ - content.clearMark = function () { - - if (codex.content.currentNode) { - codex.content.currentNode.classList.remove(codex.ui.className.BLOCK_HIGHLIGHTED); - } - }; - - /** - * @private - * - * Finds first-level block - * @param {Element} node - selected or clicked in redactors area node - */ - content.getFirstLevelBlock = function (node) { - - if (!codex.core.isDomNode(node)) { - node = node.parentNode; - } - - if (node === codex.nodes.redactor || node === document.body) { - - return null; - } else { - - while (!node.classList.contains(codex.ui.className.BLOCK_CLASSNAME)) { - node = node.parentNode; - } - - return node; - } - }; - - /** - * Trigger this event when working node changed - * @param {Element} targetNode - first-level of this node will be current - * If targetNode is first-level then we set it as current else we look for parents to find first-level - */ - content.workingNodeChanged = function (targetNode) { - - /** Clear background from previous marked block before we change */ - codex.content.clearMark(); - - if (!targetNode) { - return; - } - - this.currentNode = this.getFirstLevelBlock(targetNode); - }; - - /** - * Replaces one redactor block with another - * @protected - * @param {Element} targetBlock - block to replace. Mostly currentNode. - * @param {Element} newBlock - * @param {string} newBlockType - type of new block; we need to store it to data-attribute - * - * [!] Function does not saves old block content. - * You can get it manually and pass with newBlock.innerHTML - */ - content.replaceBlock = function function_name(targetBlock, newBlock) { - - if (!targetBlock || !newBlock) { - codex.core.log('replaceBlock: missed params'); - return; - } - - /** If target-block is not a frist-level block, then we iterate parents to find it */ - while (!targetBlock.classList.contains(codex.ui.className.BLOCK_CLASSNAME)) { - targetBlock = targetBlock.parentNode; - } - - /** - * Check is this block was in feed - * If true, than set switched block also covered - */ - if (targetBlock.classList.contains(codex.ui.className.BLOCK_IN_FEED_MODE)) { - newBlock.classList.add(codex.ui.className.BLOCK_IN_FEED_MODE); - } - - /** Saving id */ - newBlock.dataset.id = targetBlock.dataset.id; - - /** Replacing */ - codex.nodes.redactor.replaceChild(newBlock, targetBlock); - - /** - * Set new node as current - */ - codex.content.workingNodeChanged(newBlock); - - /** - * Add block handlers - */ - codex.ui.addBlockHandlers(newBlock); - - /** - * Save changes - */ - codex.ui.saveInputs(); - }; - - /** - * @private - * - * Inserts new block to redactor - * Wrapps block into a DIV with BLOCK_CLASSNAME class - * - * @param blockData {object} - * @param blockData.block {Element} element with block content - * @param blockData.type {string} block plugin - * @param needPlaceCaret {bool} pass true to set caret in new block - * - */ - content.insertBlock = function (blockData, needPlaceCaret) { - - var workingBlock = codex.content.currentNode, - newBlockContent = blockData.block, - blockType = blockData.type, - cover = blockData.cover, - isStretched = blockData.stretched; - - var newBlock = codex.content.composeNewBlock(newBlockContent, blockType, isStretched); - - if (cover === true) { - newBlock.classList.add(codex.ui.className.BLOCK_IN_FEED_MODE); - } - - if (workingBlock) { - - codex.core.insertAfter(workingBlock, newBlock); - } else { - /** - * If redactor is empty, append as first child - */ - codex.nodes.redactor.appendChild(newBlock); - } - - /** - * Block handler - */ - codex.ui.addBlockHandlers(newBlock); - - /** - * Set new node as current - */ - codex.content.workingNodeChanged(newBlock); - - /** - * Save changes - */ - codex.ui.saveInputs(); - - if (needPlaceCaret) { - - /** - * If we don't know input index then we set default value -1 - */ - var currentInputIndex = codex.caret.getCurrentInputIndex() || -1; - - if (currentInputIndex == -1) { - - var editableElement = newBlock.querySelector('[contenteditable]'), - emptyText = document.createTextNode(''); - - editableElement.appendChild(emptyText); - codex.caret.set(editableElement, 0, 0); - - codex.toolbar.move(); - codex.toolbar.showPlusButton(); - } else { - - if (currentInputIndex === codex.state.inputs.length - 1) return; - - /** Timeout for browsers execution */ - setTimeout(function () { - - /** Setting to the new input */ - codex.caret.setToNextBlock(currentInputIndex); - codex.toolbar.move(); - codex.toolbar.open(); - }, 10); - } - } - - /** - * Block is inserted, wait for new click that defined focusing on editors area - * @type {boolean} - */ - content.editorAreaHightlighted = false; - }; - - /** - * Replaces blocks with saving content - * @protected - * @param {Element} noteToReplace - * @param {Element} newNode - * @param {Element} blockType - */ - content.switchBlock = function (blockToReplace, newBlock, tool) { - - var newBlockComposed = codex.content.composeNewBlock(newBlock, tool); - - /** Replacing */ - codex.content.replaceBlock(blockToReplace, newBlockComposed); - - /** Save new Inputs when block is changed */ - codex.ui.saveInputs(); - }; - - /** - * Iterates between child noted and looking for #text node on deepest level - * @private - * @param {Element} block - node where find - * @param {int} postiton - starting postion - * Example: childNodex.length to find from the end - * or 0 to find from the start - * @return {Text} block - * @uses DFS - */ - content.getDeepestTextNodeFromPosition = function (block, position) { - - /** - * Clear Block from empty and useless spaces with trim. - * Such nodes we should remove - */ - var blockChilds = block.childNodes, - index, - node, - text; - - for (index = 0; index < blockChilds.length; index++) { - node = blockChilds[index]; - - if (node.nodeType == codex.core.nodeTypes.TEXT) { - - text = node.textContent.trim(); - - /** Text is empty. We should remove this child from node before we start DFS - * decrease the quantity of childs. - */ - if (text === '') { - - block.removeChild(node); - position--; - } - } - } - - if (block.childNodes.length === 0) { - return document.createTextNode(''); - } - - /** Setting default position when we deleted all empty nodes */ - if (position < 0) position = 1; - - var looking_from_start = false; - - /** For looking from START */ - if (position === 0) { - looking_from_start = true; - position = 1; - } - - while (position) { - - /** initial verticle of node. */ - if (looking_from_start) { - block = block.childNodes[0]; - } else { - block = block.childNodes[position - 1]; - } - - if (block.nodeType == codex.core.nodeTypes.TAG) { - - position = block.childNodes.length; - } else if (block.nodeType == codex.core.nodeTypes.TEXT) { - - position = 0; - } - } - - return block; - }; - - /** - * @private - */ - content.composeNewBlock = function (block, tool, isStretched) { - - var newBlock = codex.draw.node('DIV', codex.ui.className.BLOCK_CLASSNAME, {}), - blockContent = codex.draw.node('DIV', codex.ui.className.BLOCK_CONTENT, {}); - - blockContent.appendChild(block); - newBlock.appendChild(blockContent); - - if (isStretched) { - blockContent.classList.add(codex.ui.className.BLOCK_STRETCHED); - } - - newBlock.dataset.tool = tool; - newBlock.dataset.id = +new Date(); - - return newBlock; - }; - - /** - * Returns Range object of current selection - */ - content.getRange = function () { - - var selection = window.getSelection().getRangeAt(0); - - return selection; - }; - - /** - * Divides block in two blocks (after and before caret) - * @private - * @param {Int} inputIndex - target input index - */ - content.splitBlock = function (inputIndex) { - - var selection = window.getSelection(), - anchorNode = selection.anchorNode, - anchorNodeText = anchorNode.textContent, - caretOffset = selection.anchorOffset, - textBeforeCaret, - textNodeBeforeCaret, - textAfterCaret, - textNodeAfterCaret; - - var currentBlock = codex.content.currentNode.querySelector('[contentEditable]'); - - textBeforeCaret = anchorNodeText.substring(0, caretOffset); - textAfterCaret = anchorNodeText.substring(caretOffset); - - textNodeBeforeCaret = document.createTextNode(textBeforeCaret); - - if (textAfterCaret) { - textNodeAfterCaret = document.createTextNode(textAfterCaret); - } - - var previousChilds = [], - nextChilds = [], - reachedCurrent = false; - - if (textNodeAfterCaret) { - nextChilds.push(textNodeAfterCaret); - } - - for (var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) { - - if (child != anchorNode) { - if (!reachedCurrent) { - previousChilds.push(child); - } else { - nextChilds.push(child); - } - } else { - reachedCurrent = true; - } - } - - /** Clear current input */ - codex.state.inputs[inputIndex].innerHTML = ''; - - /** - * Append all childs founded before anchorNode - */ - var previousChildsLength = previousChilds.length; - - for (i = 0; i < previousChildsLength; i++) { - codex.state.inputs[inputIndex].appendChild(previousChilds[i]); - } - - codex.state.inputs[inputIndex].appendChild(textNodeBeforeCaret); - - /** - * Append text node which is after caret - */ - var nextChildsLength = nextChilds.length, - newNode = document.createElement('div'); - - for (i = 0; i < nextChildsLength; i++) { - newNode.appendChild(nextChilds[i]); - } - - newNode = newNode.innerHTML; - - /** This type of block creates when enter is pressed */ - var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin; - - /** - * Make new paragraph with text after caret - */ - codex.content.insertBlock({ - type: NEW_BLOCK_TYPE, - block: codex.tools[NEW_BLOCK_TYPE].render({ - text: newNode - }) - }, true); - }; - - /** - * Merges two blocks — current and target - * If target index is not exist, then previous will be as target - */ - content.mergeBlocks = function (currentInputIndex, targetInputIndex) { - - /** If current input index is zero, then prevent method execution */ - if (currentInputIndex === 0) { - return; - } - - var targetInput, - currentInputContent = codex.state.inputs[currentInputIndex].innerHTML; - - if (!targetInputIndex) { - - targetInput = codex.state.inputs[currentInputIndex - 1]; - } else { - - targetInput = codex.state.inputs[targetInputIndex]; - } - - targetInput.innerHTML += currentInputContent; - }; - - /** - * @private - * - * Callback for HTML Mutations - * @param {Array} mutation - Mutation Record - */ - content.paste = function (mutation) { - - var workingNode = codex.content.currentNode, - tool = workingNode.dataset.tool; - - if (codex.tools[tool].allowedToPaste) { - codex.content.sanitize.call(this, mutation.target); - } else { - codex.content.pasteTextContent(mutation.addedNodes); - } - }; - - /** - * @private - * - * gets only text/plain content of node - * @param {Element} target - HTML node - */ - content.pasteTextContent = function (nodes) { - - var node = nodes[0], - textNode; - - if (!node) { - return; - } - - if (node.nodeType == codex.core.nodeTypes.TEXT) { - textNode = document.createTextNode(node); - } else { - textNode = document.createTextNode(node.textContent); - } - - if (codex.core.isDomNode(node)) { - node.parentNode.replaceChild(textNode, node); - } - }; - - /** - * @private - * - * Sanitizes HTML content - * @param {Element} target - inserted element - * @uses Sanitize library html-janitor - */ - content.sanitize = function (target) { - - if (!target) { - return; - } - - var node = target[0]; - - if (!node) { - return; - } - - /** - * Disconnect Observer - * hierarchy of function calls inherits context of observer - */ - this.disconnect(); - - /** - * Don't sanitize text node - */ - if (node.nodeType == codex.core.nodeTypes.TEXT) { - return; - } - - /** - * Clear dirty content - */ - var cleaner = codex.sanitizer.init(codex.satinizer.Config.BASIC), - clean = cleaner.clean(target.outerHTML); - - var div = codex.draw.node('DIV', [], { innerHTML: clean }); - node.replaceWith(div.childNodes[0]); - }; - - /** - * Iterates all right siblings and parents, which has right siblings - * while it does not reached the first-level block - * - * @param {Element} node - * @return {boolean} - */ - content.isLastNode = function (node) { - - // console.log('погнали перебор родителей'); - - var allChecked = false; - - while (!allChecked) { - - // console.log('Смотрим на %o', node); - // console.log('Проверим, пустые ли соседи справа'); - - if (!allSiblingsEmpty_(node)) { - - // console.log('Есть непустые соседи. Узел не последний. Выходим.'); - return false; - } - - node = node.parentNode; - - /** - * Проверяем родителей до тех пор, пока не найдем блок первого уровня - */ - if (node.classList.contains(codex.ui.className.BLOCK_CONTENT)) { - allChecked = true; - } - } - - return true; - }; - - /** - * Checks if all element right siblings is empty - * @param node - */ - var allSiblingsEmpty_ = function allSiblingsEmpty_(node) { - - /** - * Нужно убедиться, что после пустого соседа ничего нет - */ - var sibling = node.nextSibling; - - while (sibling) { - - if (sibling.textContent.length) { - - return false; - } - - sibling = sibling.nextSibling; - } - - return true; - }; - - /** - * @public - * - * @param [String] htmlString - html content as string - * @return {string} - html content as string - */ - content.wrapTextWithParagraphs = function (htmlString) { - - var wrapper = document.createElement('DIV'), - newWrapper = document.createElement('DIV'), - i, - paragraph, - firstLevelBlocks = ['DIV', 'P'], - blockTyped, - node; - - /** - * Make HTML Element to Wrap Text - * It allows us to work with input data as HTML content - */ - wrapper.innerHTML = htmlString; - paragraph = document.createElement('P'); - - for (i = 0; i < wrapper.childNodes.length; i++) { - - node = wrapper.childNodes[i]; - - blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1; - - /** - * If node is first-levet - * we add this node to our new wrapper - */ - if (blockTyped) { - - /** - * If we had splitted inline nodes to paragraph before - */ - if (paragraph.childNodes.length) { - - newWrapper.appendChild(paragraph.cloneNode(true)); - - /** empty paragraph */ - paragraph = null; - paragraph = document.createElement('P'); - } - - newWrapper.appendChild(node.cloneNode(true)); - } else { - - /** Collect all inline nodes to one as paragraph */ - paragraph.appendChild(node.cloneNode(true)); - - /** if node is last we should append this node to paragraph and paragraph to new wrapper */ - if (i == wrapper.childNodes.length - 1) { - newWrapper.appendChild(paragraph.cloneNode(true)); - } - } - } - - return newWrapper.innerHTML; - }; - - return content; - }({}); - - module.exports = content; - -/***/ }, -/* 8 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - /** - * Codex Editor toolbar module - * - * Contains: - * - Inline toolbox - * - Toolbox within plus button - * - Settings section - * - * @author Codex Team - * @version 1.0 - */ - - var editor = codex.editor; - - module.exports = function (toolbar) { - - toolbar.settings = __webpack_require__(9); - toolbar.inline = __webpack_require__(10); - toolbar.toolbox = __webpack_require__(11); - - /** - * Margin between focused node and toolbar - */ - toolbar.defaultToolbarHeight = 49; - - toolbar.defaultOffset = 34; - - toolbar.opened = false; - - toolbar.current = null; - - /** - * @protected - */ - toolbar.open = function () { - - editor.nodes.toolbar.classList.add('opened'); - this.opened = true; - }; - - /** - * @protected - */ - toolbar.close = function () { - - editor.nodes.toolbar.classList.remove('opened'); - - toolbar.opened = false; - toolbar.current = null; - - for (var button in editor.nodes.toolbarButtons) { - - editor.nodes.toolbarButtons[button].classList.remove('selected'); - } - - /** Close toolbox when toolbar is not displayed */ - editor.toolbar.toolbox.close(); - editor.toolbar.settings.close(); - }; - - toolbar.toggle = function () { - - if (!this.opened) { - - this.open(); - } else { - - this.close(); - } - }; - - toolbar.hidePlusButton = function () { - - editor.nodes.plusButton.classList.add('hide'); - }; - - toolbar.showPlusButton = function () { - - editor.nodes.plusButton.classList.remove('hide'); - }; - - /** - * Moving toolbar to the specified node - */ - toolbar.move = function () { - - /** Close Toolbox when we move toolbar */ - editor.toolbar.toolbox.close(); - - if (!editor.content.currentNode) { - - return; - } - - var newYCoordinate = editor.content.currentNode.offsetTop - editor.toolbar.defaultToolbarHeight / 2 + editor.toolbar.defaultOffset; - - editor.nodes.toolbar.style.transform = 'translate3D(0, ' + Math.floor(newYCoordinate) + 'px, 0)'; - - /** Close trash actions */ - editor.toolbar.settings.hideRemoveActions(); - }; - - return toolbar; - }({}); - -/***/ }, -/* 9 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Toolbar settings - * - * @version 1.0.4 - */ - - var editor = codex.editor; - - module.exports = function (settings) { - - settings.opened = false; - - settings.setting = null; - settings.actions = null; - - settings.cover = null; - - /** - * Append and open settings - */ - settings.open = function (toolType) { - - /** - * Append settings content - * It's stored in tool.settings - */ - if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings) { - - editor.core.log('Plugin \xAB' + toolType + '\xBB has no settings', 'warn'); - // editor.nodes.pluginSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`; - } else { - - /** - * Draw settings block - */ - var settingsBlock = editor.tools[toolType].makeSettings(); - - editor.nodes.pluginSettings.appendChild(settingsBlock); - } - - /** Open settings block */ - editor.nodes.blockSettings.classList.add('opened'); - editor.toolbar.settings.addDefaultSettings(); - this.opened = true; - }; - - /** - * Close and clear settings - */ - settings.close = function () { - - editor.nodes.blockSettings.classList.remove('opened'); - editor.nodes.pluginSettings.innerHTML = ''; - - this.opened = false; - }; - - /** - * @param {string} toolType - plugin type - */ - settings.toggle = function (toolType) { - - if (!this.opened) { - - this.open(toolType); - } else { - - this.close(); - } - }; - - /** - * This function adds default core settings - */ - settings.addDefaultSettings = function () { - - /** list of default settings */ - var feedModeToggler; - - /** Clear block and append initialized settings */ - editor.nodes.defaultSettings.innerHTML = ''; - - /** Init all default setting buttons */ - feedModeToggler = editor.toolbar.settings.makeFeedModeToggler(); - - /** - * Fill defaultSettings - */ - - /** - * Button that enables/disables Feed-mode - * Feed-mode means that block will be showed in articles-feed like cover - */ - editor.nodes.defaultSettings.appendChild(feedModeToggler); - }; - - /** - * Cover setting. - * This tune highlights block, so that it may be used for showing target block on main page - * Draw different setting when block is marked for main page - * If TRUE, then we show button that removes this selection - * Also defined setting "Click" events will be listened and have separate callbacks - * - * @return {Element} node/button that we place in default settings block - */ - settings.makeFeedModeToggler = function () { - - var isFeedModeActivated = editor.toolbar.settings.isFeedModeActivated(), - setting, - data; - - if (!isFeedModeActivated) { - - data = { - innerHTML: 'Вывести в ленте' - }; - } else { - - data = { - innerHTML: 'Не выводить в ленте' - }; - } - - setting = editor.draw.node('DIV', editor.ui.className.SETTINGS_ITEM, data); - setting.addEventListener('click', editor.toolbar.settings.updateFeedMode, false); - - return setting; - }; - - /** - * Updates Feed-mode - */ - settings.updateFeedMode = function () { - - var currentNode = editor.content.currentNode; - - currentNode.classList.toggle(editor.ui.className.BLOCK_IN_FEED_MODE); - - editor.toolbar.settings.close(); - }; - - settings.isFeedModeActivated = function () { - - var currentBlock = editor.content.currentNode; - - if (currentBlock) { - - return currentBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE); - } else { - - return false; - } - }; - - /** - * Here we will draw buttons and add listeners to components - */ - settings.makeRemoveBlockButton = function () { - - var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}), - settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML: '' }), - actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}), - confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent: 'Удалить блок' }), - cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent: 'Отмена' }); - - settingButton.addEventListener('click', editor.toolbar.settings.removeButtonClicked, false); - - confirmAction.addEventListener('click', editor.toolbar.settings.confirmRemovingRequest, false); - - cancelAction.addEventListener('click', editor.toolbar.settings.cancelRemovingRequest, false); - - actionWrapper.appendChild(confirmAction); - actionWrapper.appendChild(cancelAction); - - removeBlockWrapper.appendChild(settingButton); - removeBlockWrapper.appendChild(actionWrapper); - - /** Save setting */ - editor.toolbar.settings.setting = settingButton; - editor.toolbar.settings.actions = actionWrapper; - - return removeBlockWrapper; - }; - - settings.removeButtonClicked = function () { - - var action = editor.toolbar.settings.actions; - - if (action.classList.contains('opened')) { - - editor.toolbar.settings.hideRemoveActions(); - } else { - - editor.toolbar.settings.showRemoveActions(); - } - - editor.toolbar.toolbox.close(); - editor.toolbar.settings.close(); - }; - - settings.cancelRemovingRequest = function () { - - editor.toolbar.settings.actions.classList.remove('opened'); - }; - - settings.confirmRemovingRequest = function () { - - var currentBlock = editor.content.currentNode, - firstLevelBlocksCount; - - currentBlock.remove(); - - firstLevelBlocksCount = editor.nodes.redactor.childNodes.length; - - /** - * If all blocks are removed - */ - if (firstLevelBlocksCount === 0) { - - /** update currentNode variable */ - editor.content.currentNode = null; - - /** Inserting new empty initial block */ - editor.ui.addInitialBlock(); - } - - editor.ui.saveInputs(); - - editor.toolbar.close(); - }; - - settings.showRemoveActions = function () { - - editor.toolbar.settings.actions.classList.add('opened'); - }; - - settings.hideRemoveActions = function () { - - editor.toolbar.settings.actions.classList.remove('opened'); - }; - - return settings; - }({}); - -/***/ }, -/* 10 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Inline toolbar - * - * Contains from tools: - * Bold, Italic, Underline and Anchor - * - * @author Codex Team - * @version 1.0 - */ - - var editor = codex.editor; - - module.exports = function (inline) { - - inline.buttonsOpened = null; - inline.actionsOpened = null; - inline.wrappersOffset = null; - - /** - * saving selection that need for execCommand for styling - * - */ - inline.storedSelection = null; - - /** - * @protected - * - * Open inline toobar - */ - inline.show = function () { - - var currentNode = editor.content.currentNode, - tool = currentNode.dataset.tool, - plugin; - - /** - * tool allowed to open inline toolbar - */ - plugin = editor.tools[tool]; - - if (!plugin.showInlineToolbar) return; - - var selectedText = inline.getSelectionText(), - toolbar = editor.nodes.inlineToolbar.wrapper; - - if (selectedText.length > 0) { - - /** Move toolbar and open */ - editor.toolbar.inline.move(); - - /** Open inline toolbar */ - toolbar.classList.add('opened'); - - /** show buttons of inline toolbar */ - editor.toolbar.inline.showButtons(); - } - }; - - /** - * @protected - * - * Closes inline toolbar - */ - inline.close = function () { - - var toolbar = editor.nodes.inlineToolbar.wrapper; - - toolbar.classList.remove('opened'); - }; - - /** - * @private - * - * Moving toolbar - */ - inline.move = function () { - - if (!this.wrappersOffset) { - - this.wrappersOffset = this.getWrappersOffset(); - } - - var coords = this.getSelectionCoords(), - defaultOffset = 0, - toolbar = editor.nodes.inlineToolbar.wrapper, - newCoordinateX, - newCoordinateY; - - if (toolbar.offsetHeight === 0) { - - defaultOffset = 40; - } - - newCoordinateX = coords.x - this.wrappersOffset.left; - newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight; - - toolbar.style.transform = 'translate3D(' + Math.floor(newCoordinateX) + 'px, ' + Math.floor(newCoordinateY) + 'px, 0)'; - - /** Close everything */ - editor.toolbar.inline.closeButtons(); - editor.toolbar.inline.closeAction(); - }; - - /** - * @private - * - * Tool Clicked - */ - - inline.toolClicked = function (event, type) { - - /** - * For simple tools we use default browser function - * For more complicated tools, we should write our own behavior - */ - switch (type) { - case 'createLink': - editor.toolbar.inline.createLinkAction(event, type);break; - default: - editor.toolbar.inline.defaultToolAction(type);break; - } - - /** - * highlight buttons - * after making some action - */ - editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); - }; - - /** - * @private - * - * Saving wrappers offset in DOM - */ - inline.getWrappersOffset = function () { - - var wrapper = editor.nodes.wrapper, - offset = this.getOffset(wrapper); - - this.wrappersOffset = offset; - return offset; - }; - - /** - * @private - * - * Calculates offset of DOM element - * - * @param el - * @returns {{top: number, left: number}} - */ - inline.getOffset = function (el) { - - var _x = 0; - var _y = 0; - - while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) { - - _x += el.offsetLeft + el.clientLeft; - _y += el.offsetTop + el.clientTop; - el = el.offsetParent; - } - return { top: _y, left: _x }; - }; - - /** - * @private - * - * Calculates position of selected text - * @returns {{x: number, y: number}} - */ - inline.getSelectionCoords = function () { - - var sel = document.selection, - range; - var x = 0, - y = 0; - - if (sel) { - - if (sel.type != 'Control') { - - range = sel.createRange(); - range.collapse(true); - x = range.boundingLeft; - y = range.boundingTop; - } - } else if (window.getSelection) { - - sel = window.getSelection(); - - if (sel.rangeCount) { - - range = sel.getRangeAt(0).cloneRange(); - if (range.getClientRects) { - - range.collapse(true); - var rect = range.getClientRects()[0]; - - if (!rect) { - - return; - } - - x = rect.left; - y = rect.top; - } - } - } - return { x: x, y: y }; - }; - - /** - * @private - * - * Returns selected text as String - * @returns {string} - */ - inline.getSelectionText = function () { - - var selectedText = ''; - - // all modern browsers and IE9+ - if (window.getSelection) { - - selectedText = window.getSelection().toString(); - } - - return selectedText; - }; - - /** Opens buttons block */ - inline.showButtons = function () { - - var buttons = editor.nodes.inlineToolbar.buttons; - - buttons.classList.add('opened'); - - editor.toolbar.inline.buttonsOpened = true; - - /** highlight buttons */ - editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight); - }; - - /** Makes buttons disappear */ - inline.closeButtons = function () { - - var buttons = editor.nodes.inlineToolbar.buttons; - - buttons.classList.remove('opened'); - - editor.toolbar.inline.buttonsOpened = false; - }; - - /** Open buttons defined action if exist */ - inline.showActions = function () { - - var action = editor.nodes.inlineToolbar.actions; - - action.classList.add('opened'); - - editor.toolbar.inline.actionsOpened = true; - }; - - /** Close actions block */ - inline.closeAction = function () { - - var action = editor.nodes.inlineToolbar.actions; - - action.innerHTML = ''; - action.classList.remove('opened'); - editor.toolbar.inline.actionsOpened = false; - }; - - /** - * Callback for keydowns in inline toolbar "Insert link..." input - */ - var inlineToolbarAnchorInputKeydown_ = function inlineToolbarAnchorInputKeydown_(event) { - - if (event.keyCode != editor.core.keys.ENTER) { - - return; - } - - var editable = editor.content.currentNode, - storedSelection = editor.toolbar.inline.storedSelection; - - editor.toolbar.inline.restoreSelection(editable, storedSelection); - editor.toolbar.inline.setAnchor(this.value); - - /** - * Preventing events that will be able to happen - */ - event.preventDefault(); - event.stopImmediatePropagation(); - - editor.toolbar.inline.clearRange(); - }; - - /** Action for link creation or for setting anchor */ - inline.createLinkAction = function (event) { - - var isActive = this.isLinkActive(); - - var editable = editor.content.currentNode, - storedSelection = editor.toolbar.inline.saveSelection(editable); - - /** Save globally selection */ - editor.toolbar.inline.storedSelection = storedSelection; - - if (isActive) { - - /** - * Changing stored selection. if we want to remove anchor from word - * we should remove anchor from whole word, not only selected part. - * The solution is than we get the length of current link - * Change start position to - end of selection minus length of anchor - */ - editor.toolbar.inline.restoreSelection(editable, storedSelection); - - editor.toolbar.inline.defaultToolAction('unlink'); - } else { - - /** Create input and close buttons */ - var action = editor.draw.inputForLink(); - - editor.nodes.inlineToolbar.actions.appendChild(action); - - editor.toolbar.inline.closeButtons(); - editor.toolbar.inline.showActions(); - - /** - * focus to input - * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus - * Prevents event after showing input and when we need to focus an input which is in unexisted form - */ - action.focus(); - event.preventDefault(); - - /** Callback to link action */ - action.addEventListener('keydown', inlineToolbarAnchorInputKeydown_, false); - } - }; - - inline.isLinkActive = function () { - - var isActive = false; - - editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) { - - var dataType = tool.dataset.type; - - if (dataType == 'link' && tool.classList.contains('hightlighted')) { - - isActive = true; - } - }); - - return isActive; - }; - - /** default action behavior of tool */ - inline.defaultToolAction = function (type) { - - document.execCommand(type, false, null); - }; - - /** - * @private - * - * Sets URL - * - * @param {String} url - URL - */ - inline.setAnchor = function (url) { - - document.execCommand('createLink', false, url); - - /** Close after URL inserting */ - editor.toolbar.inline.closeAction(); - }; - - /** - * @private - * - * Saves selection - */ - inline.saveSelection = function (containerEl) { - - var range = window.getSelection().getRangeAt(0), - preSelectionRange = range.cloneRange(), - start; - - preSelectionRange.selectNodeContents(containerEl); - preSelectionRange.setEnd(range.startContainer, range.startOffset); - - start = preSelectionRange.toString().length; - - return { - start: start, - end: start + range.toString().length - }; - }; - - /** - * @private - * - * Sets to previous selection (Range) - * - * @param {Element} containerEl - editable element where we restore range - * @param {Object} savedSel - range basic information to restore - */ - inline.restoreSelection = function (containerEl, savedSel) { - - var range = document.createRange(), - charIndex = 0; - - range.setStart(containerEl, 0); - range.collapse(true); - - var nodeStack = [containerEl], - node, - foundStart = false, - stop = false, - nextCharIndex; - - while (!stop && (node = nodeStack.pop())) { - - if (node.nodeType == 3) { - - nextCharIndex = charIndex + node.length; - - if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { - - range.setStart(node, savedSel.start - charIndex); - foundStart = true; - } - if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { - - range.setEnd(node, savedSel.end - charIndex); - stop = true; - } - charIndex = nextCharIndex; - } else { - - var i = node.childNodes.length; - - while (i--) { - - nodeStack.push(node.childNodes[i]); - } - } - } - - var sel = window.getSelection(); - - sel.removeAllRanges(); - sel.addRange(range); - }; - - /** - * @private - * - * Removes all ranges from window selection - */ - inline.clearRange = function () { - - var selection = window.getSelection(); - - selection.removeAllRanges(); - }; - - /** - * @private - * - * sets or removes hightlight - */ - inline.hightlight = function (tool) { - - var dataType = tool.dataset.type; - - if (document.queryCommandState(dataType)) { - - editor.toolbar.inline.setButtonHighlighted(tool); - } else { - - editor.toolbar.inline.removeButtonsHighLight(tool); - } - - /** - * - * hightlight for anchors - */ - var selection = window.getSelection(), - tag = selection.anchorNode.parentNode; - - if (tag.tagName == 'A' && dataType == 'link') { - - editor.toolbar.inline.setButtonHighlighted(tool); - } - }; - - /** - * @private - * - * Mark button if text is already executed - */ - inline.setButtonHighlighted = function (button) { - - button.classList.add('hightlighted'); - - /** At link tool we also change icon */ - if (button.dataset.type == 'link') { - - var icon = button.childNodes[0]; - - icon.classList.remove('ce-icon-link'); - icon.classList.add('ce-icon-unlink'); - } - }; - - /** - * @private - * - * Removes hightlight - */ - inline.removeButtonsHighLight = function (button) { - - button.classList.remove('hightlighted'); - - /** At link tool we also change icon */ - if (button.dataset.type == 'link') { - - var icon = button.childNodes[0]; - - icon.classList.remove('ce-icon-unlink'); - icon.classList.add('ce-icon-link'); - } - }; - - return inline; - }({}); - -/***/ }, -/* 11 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor toolbox - * - * All tools be able to appended here - * - * @author Codex Team - * @version 1.0 - */ - - var editor = codex.editor; - - module.exports = function (toolbox) { - - toolbox.opened = false; - - /** Shows toolbox */ - toolbox.open = function () { - - /** Close setting if toolbox is opened */ - if (editor.toolbar.settings.opened) { - - editor.toolbar.settings.close(); - } - - /** display toolbox */ - editor.nodes.toolbox.classList.add('opened'); - - /** Animate plus button */ - editor.nodes.plusButton.classList.add('clicked'); - - /** toolbox state */ - editor.toolbar.toolbox.opened = true; - }; - - /** Closes toolbox */ - toolbox.close = function () { - - /** Makes toolbox disapear */ - editor.nodes.toolbox.classList.remove('opened'); - - /** Rotate plus button */ - editor.nodes.plusButton.classList.remove('clicked'); - - /** toolbox state */ - editor.toolbar.toolbox.opened = false; - }; - - toolbox.leaf = function () { - - var currentTool = editor.toolbar.current, - tools = Object.keys(editor.tools), - barButtons = editor.nodes.toolbarButtons, - nextToolIndex = 0, - toolToSelect = void 0, - visibleTool = void 0, - tool = void 0; - - if (!currentTool) { - - /** Get first tool from object*/ - for (tool in editor.tools) { - - if (editor.tools[tool].displayInToolbox) { - - break; - } - - nextToolIndex++; - } - } else { - - nextToolIndex = tools.indexOf(currentTool) + 1; - visibleTool = tools[nextToolIndex]; - - while (!editor.tools[visibleTool].displayInToolbox) { - - nextToolIndex++; - visibleTool = tools[nextToolIndex]; - - if (nextToolIndex == tools.length) { - - nextToolIndex = 0; - visibleTool = tools[nextToolIndex]; - } - } - } - - toolToSelect = tools[nextToolIndex]; - - for (var button in barButtons) { - - barButtons[button].classList.remove('selected'); - } - - barButtons[toolToSelect].classList.add('selected'); - editor.toolbar.current = toolToSelect; - }; - - /** - * Transforming selected node type into selected toolbar element type - * @param {event} event - */ - toolbox.toolClicked = function (event) { - - /** - * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty - */ - var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'], - tool = editor.tools[editor.toolbar.current], - workingNode = editor.content.currentNode, - currentInputIndex = editor.caret.inputIndex, - newBlockContent, - appendCallback, - blockData; - - /** Make block from plugin */ - newBlockContent = tool.render(); - - /** information about block */ - blockData = { - block: newBlockContent, - type: tool.type, - stretched: false - }; - - if (workingNode && UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 && workingNode.textContent.trim() === '') { - - /** Replace current block */ - editor.content.switchBlock(workingNode, newBlockContent, tool.type); - } else { - - /** Insert new Block from plugin */ - editor.content.insertBlock(blockData); - - /** increase input index */ - currentInputIndex++; - } - - /** Fire tool append callback */ - appendCallback = tool.appendCallback; - - if (appendCallback && typeof appendCallback == 'function') { - - appendCallback.call(event); - } - - window.setTimeout(function () { - - /** Set caret to current block */ - editor.caret.setToBlock(currentInputIndex); - }, 10); - - /** - * Changing current Node - */ - editor.content.workingNodeChanged(); - - /** - * Move toolbar when node is changed - */ - editor.toolbar.move(); - }; - - return toolbox; - }({}); - -/***/ }, -/* 12 */ -/***/ function(module, exports) { - - "use strict"; - - /** - * Codex Editor tools - * This tools will be appended in toolbox - * - * @author Codex Team - * @version 1.0 - */ - - var tools = function (tools) { - - return tools; - }({}); - - module.exports = tools; - -/***/ }, -/* 13 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor callbacks module - * - * @author Codex Team - * @version 1.3.5 - */ - - var callbacks = function (callbacks) { - - callbacks.redactorSyncTimeout = null; - - callbacks.globalKeydown = function (event) { - switch (event.keyCode) { - case codex.core.keys.ENTER: - codex.callback.enterKeyPressed(event);break; - } - }; - - callbacks.redactorKeyDown = function (event) { - switch (event.keyCode) { - case codex.core.keys.TAB: - codex.callback.tabKeyPressed(event);break; - case codex.core.keys.ENTER: - codex.callback.enterKeyPressedOnRedactorZone(event);break; - case codex.core.keys.ESC: - codex.callback.escapeKeyPressed(event);break; - default: - codex.callback.defaultKeyPressed(event);break; - } - }; - - callbacks.globalKeyup = function (event) { - switch (event.keyCode) { - case codex.core.keys.UP: - case codex.core.keys.LEFT: - case codex.core.keys.RIGHT: - case codex.core.keys.DOWN: - codex.callback.arrowKeyPressed(event);break; - } - }; - - callbacks.tabKeyPressed = function (event) { - - if (!codex.toolbar.opened) { - codex.toolbar.open(); - } - - if (codex.toolbar.opened && !codex.toolbar.toolbox.opened) { - codex.toolbar.toolbox.open(); - } else { - codex.toolbar.toolbox.leaf(); - } - - event.preventDefault(); - }; - - callbacks.enterKeyPressed = function (event) { - - if (codex.content.editorAreaHightlighted) { - - /** - * it means that we lose input index, saved index before is not correct - * therefore we need to set caret when we insert new block - */ - codex.caret.inputIndex = -1; - - codex.callback.enterPressedOnBlock(); - } - }; - - /** - * ENTER key handler - * Makes new paragraph block - */ - callbacks.enterKeyPressedOnRedactorZone = function (event) { - - if (event.target.contentEditable == 'true') { - - /** Update input index */ - codex.caret.saveCurrentInputIndex(); - } - - var currentInputIndex = codex.caret.getCurrentInputIndex() || 0, - workingNode = codex.content.currentNode, - tool = workingNode.dataset.tool, - isEnterPressedOnToolbar = codex.toolbar.opened && codex.toolbar.current && event.target == codex.state.inputs[currentInputIndex]; - - /** The list of tools which needs the default browser behaviour */ - var enableLineBreaks = codex.tools[tool].enableLineBreaks; - - /** This type of block creates when enter is pressed */ - var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin; - - /** - * When toolbar is opened, select tool instead of making new paragraph - */ - if (isEnterPressedOnToolbar) { - - event.preventDefault(); - - codex.toolbar.toolbox.toolClicked(event); - - codex.toolbar.close(); - - /** - * Stop other listeners callback executions - */ - event.stopPropagation(); - event.stopImmediatePropagation(); - - return; - } - - /** - * Allow paragraph lineBreaks with shift enter - * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation - */ - if (event.shiftKey || enableLineBreaks) { - - event.stopPropagation(); - event.stopImmediatePropagation(); - return; - } - - var isLastTextNode = false, - currentSelection = window.getSelection(), - currentSelectedNode = currentSelection.anchorNode, - caretAtTheEndOfText = codex.caret.position.atTheEnd(), - isTextNodeHasParentBetweenContenteditable = false; - - /** - * Allow making new
in same block by SHIFT+ENTER and forbids to prevent default browser behaviour - */ - if (event.shiftKey && !enableLineBreaks) { - codex.callback.enterPressedOnBlock(codex.content.currentBlock, event); - event.preventDefault(); - return; - } - - /** - * Workaround situation when caret at the Text node that has some wrapper Elements - * Split block cant handle this. - * We need to save default behavior - */ - isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != "true"; - - /** - * Split blocks when input has several nodes and caret placed in textNode - */ - if (currentSelectedNode.nodeType == codex.core.nodeTypes.TEXT && !isTextNodeHasParentBetweenContenteditable && !caretAtTheEndOfText) { - - event.preventDefault(); - - codex.core.log('Splitting Text node...'); - - codex.content.splitBlock(currentInputIndex); - - /** Show plus button when next input after split is empty*/ - if (!codex.state.inputs[currentInputIndex + 1].textContent.trim()) { - codex.toolbar.showPlusButton(); - } - } else { - - var islastNode = codex.content.isLastNode(currentSelectedNode); - - if (islastNode && caretAtTheEndOfText) { - - event.preventDefault(); - event.stopPropagation(); - event.stopImmediatePropagation(); - - codex.core.log('ENTER clicked in last textNode. Create new BLOCK'); - - codex.content.insertBlock({ - type: NEW_BLOCK_TYPE, - block: codex.tools[NEW_BLOCK_TYPE].render() - }, true); - - codex.toolbar.move(); - codex.toolbar.open(); - - /** Show plus button with empty block */ - codex.toolbar.showPlusButton(); - } - } - - /** get all inputs after new appending block */ - codex.ui.saveInputs(); - }; - - callbacks.escapeKeyPressed = function (event) { - - /** Close all toolbar */ - codex.toolbar.close(); - - /** Close toolbox */ - codex.toolbar.toolbox.close(); - - event.preventDefault(); - }; - - callbacks.arrowKeyPressed = function (event) { - - codex.content.workingNodeChanged(); - - /* Closing toolbar */ - codex.toolbar.close(); - codex.toolbar.move(); - }; - - callbacks.defaultKeyPressed = function (event) { - - codex.toolbar.close(); - - if (!codex.toolbar.inline.actionsOpened) { - codex.toolbar.inline.close(); - codex.content.clearMark(); - } - }; - - callbacks.redactorClicked = function (event) { - - callbacks.detectWhenClickedOnFirstLevelBlockArea(); - - codex.content.workingNodeChanged(event.target); - - codex.ui.saveInputs(); - - var selectedText = codex.toolbar.inline.getSelectionText(); - - /** - * If selection range took off, then we hide inline toolbar - */ - if (selectedText.length === 0) { - codex.toolbar.inline.close(); - } - - /** Update current input index in memory when caret focused into existed input */ - if (event.target.contentEditable == 'true') { - - codex.caret.saveCurrentInputIndex(); - } - - if (codex.content.currentNode === null) { - - /** - * If inputs in redactor does not exits, then we put input index 0 not -1 - */ - var indexOfLastInput = codex.state.inputs.length > 0 ? codex.state.inputs.length - 1 : 0; - - /** If we have any inputs */ - if (codex.state.inputs.length) { - - /** getting firstlevel parent of input */ - var firstLevelBlock = codex.content.getFirstLevelBlock(codex.state.inputs[indexOfLastInput]); - } - - /** If input is empty, then we set caret to the last input */ - if (codex.state.inputs.length && codex.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == codex.settings.initialBlockPlugin) { - - codex.caret.setToBlock(indexOfLastInput); - } else { - - /** Create new input when caret clicked in redactors area */ - var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin; - - codex.content.insertBlock({ - type: NEW_BLOCK_TYPE, - block: codex.tools[NEW_BLOCK_TYPE].render() - }); - - /** If there is no inputs except inserted */ - if (codex.state.inputs.length === 1) { - - codex.caret.setToBlock(indexOfLastInput); - } else { - - /** Set caret to this appended input */ - codex.caret.setToNextBlock(indexOfLastInput); - } - } - - /** - * Move toolbar to the right position and open - */ - codex.toolbar.move(); - - codex.toolbar.open(); - } else { - - /** - * Move toolbar to the new position and open - */ - codex.toolbar.move(); - - codex.toolbar.open(); - - /** Close all panels */ - codex.toolbar.settings.close(); - codex.toolbar.toolbox.close(); - } - - var inputIsEmpty = !codex.content.currentNode.textContent.trim(), - currentNodeType = codex.content.currentNode.dataset.tool, - isInitialType = currentNodeType == codex.settings.initialBlockPlugin; - - /** Hide plus buttons */ - codex.toolbar.hidePlusButton(); - - /** Mark current block */ - codex.content.markBlock(); - - if (isInitialType && inputIsEmpty) { - - /** Show plus button */ - codex.toolbar.showPlusButton(); - } - }; - - /** - * This method allows to define, is caret in contenteditable element or not. - * Otherwise, if we get TEXT node from range container, that will means we have input index. - * In this case we use default browsers behaviour (if plugin allows that) or overwritten action. - * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always - * specifies to the first-level block. Other cases we just ignore. - */ - callbacks.detectWhenClickedOnFirstLevelBlockArea = function () { - - var selection = window.getSelection(), - anchorNode = selection.anchorNode, - flag = false; - - if (selection.rangeCount == 0) { - - codex.content.editorAreaHightlighted = true; - } else { - - if (!codex.core.isDomNode(anchorNode)) { - anchorNode = anchorNode.parentNode; - } - - /** Already founded, without loop */ - if (anchorNode.contentEditable == 'true') { - flag = true; - } - - while (anchorNode.contentEditable != 'true') { - anchorNode = anchorNode.parentNode; - - if (anchorNode.contentEditable == 'true') { - flag = true; - } - - if (anchorNode == document.body) { - break; - } - } - - /** If editable element founded, flag is "TRUE", Therefore we return "FALSE" */ - codex.content.editorAreaHightlighted = flag ? false : true; - } - }; - - /** - * Toolbar button click handler - * @param this - cursor to the button - */ - callbacks.toolbarButtonClicked = function (event) { - - var button = this; - - codex.toolbar.current = button.dataset.type; - - codex.toolbar.toolbox.toolClicked(event); - codex.toolbar.close(); - }; - - callbacks.redactorInputEvent = function (event) { - - /** - * Clear previous sync-timeout - */ - if (this.redactorSyncTimeout) { - clearTimeout(this.redactorSyncTimeout); - } - - /** - * Start waiting to input finish and sync redactor - */ - this.redactorSyncTimeout = setTimeout(function () { - - codex.content.sync(); - }, 500); - }; - - /** Show or Hide toolbox when plus button is clicked */ - callbacks.plusButtonClicked = function () { - - if (!codex.nodes.toolbox.classList.contains('opened')) { - - codex.toolbar.toolbox.open(); - } else { - - codex.toolbar.toolbox.close(); - } - }; - - /** - * Block handlers for KeyDown events - */ - callbacks.blockKeydown = function (event, block) { - - switch (event.keyCode) { - - case codex.core.keys.DOWN: - case codex.core.keys.RIGHT: - codex.callback.blockRightOrDownArrowPressed(block); - break; - - case codex.core.keys.BACKSPACE: - codex.callback.backspacePressed(block); - break; - - case codex.core.keys.UP: - case codex.core.keys.LEFT: - codex.callback.blockLeftOrUpArrowPressed(block); - break; - - } - }; - - /** - * RIGHT or DOWN keydowns on block - */ - callbacks.blockRightOrDownArrowPressed = function (block) { - - var selection = window.getSelection(), - inputs = codex.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - /** Check for caret existance */ - if (!focusedNode) { - return false; - } - - /** Looking for closest (parent) contentEditable element of focused node */ - while (focusedNode.contentEditable != 'true') { - - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - while (focusedNode != inputs[editableElementIndex]) { - editableElementIndex++; - } - - /** - * Founded contentEditable element doesn't have childs - * Or maybe New created block - */ - if (!focusedNode.textContent) { - codex.caret.setToNextBlock(editableElementIndex); - return; - } - - /** - * Do nothing when caret doesn not reaches the end of last child - */ - var caretInLastChild = false, - caretAtTheEndOfText = false; - - var lastChild, deepestTextnode; - - lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1]; - - if (codex.core.isDomNode(lastChild)) { - - deepestTextnode = codex.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length); - } else { - - deepestTextnode = lastChild; - } - - caretInLastChild = selection.anchorNode == deepestTextnode; - caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset; - - if (!caretInLastChild || !caretAtTheEndOfText) { - codex.core.log('arrow [down|right] : caret does not reached the end'); - return false; - } - - codex.caret.setToNextBlock(editableElementIndex); - }; - - /** - * LEFT or UP keydowns on block - */ - callbacks.blockLeftOrUpArrowPressed = function (block) { - - var selection = window.getSelection(), - inputs = codex.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - /** Check for caret existance */ - if (!focusedNode) { - return false; - } - - /** - * LEFT or UP not at the beginning - */ - if (selection.anchorOffset !== 0) { - return false; - } - - /** Looking for parent contentEditable block */ - while (focusedNode.contentEditable != 'true') { - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - while (focusedNode != inputs[editableElementIndex]) { - editableElementIndex++; - } - - /** - * Do nothing if caret is not at the beginning of first child - */ - var caretInFirstChild = false, - caretAtTheBeginning = false; - - var firstChild, deepestTextnode; - - /** - * Founded contentEditable element doesn't have childs - * Or maybe New created block - */ - if (!focusedNode.textContent) { - codex.caret.setToPreviousBlock(editableElementIndex); - return; - } - - firstChild = focusedNode.childNodes[0]; - - if (codex.core.isDomNode(firstChild)) { - - deepestTextnode = codex.content.getDeepestTextNodeFromPosition(firstChild, 0); - } else { - - deepestTextnode = firstChild; - } - - caretInFirstChild = selection.anchorNode == deepestTextnode; - caretAtTheBeginning = selection.anchorOffset === 0; - - if (caretInFirstChild && caretAtTheBeginning) { - - codex.caret.setToPreviousBlock(editableElementIndex); - } - }; - - /** - * Callback for enter key pressing in first-level block area - */ - callbacks.enterPressedOnBlock = function (event) { - - var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin; - - codex.content.insertBlock({ - type: NEW_BLOCK_TYPE, - block: codex.tools[NEW_BLOCK_TYPE].render() - }, true); - - codex.toolbar.move(); - codex.toolbar.open(); - }; - - callbacks.backspacePressed = function (block) { - - var currentInputIndex = codex.caret.getCurrentInputIndex(), - range, - selectionLength, - firstLevelBlocksCount; - - if (block.textContent.trim()) { - - range = codex.content.getRange(); - selectionLength = range.endOffset - range.startOffset; - - if (codex.caret.position.atStart() && !selectionLength && codex.state.inputs[currentInputIndex - 1]) { - - codex.content.mergeBlocks(currentInputIndex); - } else { - - return; - } - } - - if (!selectionLength) { - block.remove(); - } - - firstLevelBlocksCount = codex.nodes.redactor.childNodes.length; - - /** - * If all blocks are removed - */ - if (firstLevelBlocksCount === 0) { - - /** update currentNode variable */ - codex.content.currentNode = null; - - /** Inserting new empty initial block */ - codex.ui.addInitialBlock(); - - /** Updating inputs state after deleting last block */ - codex.ui.saveInputs(); - - /** Set to current appended block */ - setTimeout(function () { - - codex.caret.setToPreviousBlock(1); - }, 10); - } else { - - if (codex.caret.inputIndex !== 0) { - - /** Target block is not first */ - codex.caret.setToPreviousBlock(codex.caret.inputIndex); - } else { - - /** If we try to delete first block */ - codex.caret.setToNextBlock(codex.caret.inputIndex); - } - } - - codex.toolbar.move(); - - if (!codex.toolbar.opened) { - codex.toolbar.open(); - } - - /** Updating inputs state */ - codex.ui.saveInputs(); - - /** Prevent default browser behaviour */ - event.preventDefault(); - }; - - /** - * @deprecated - * - * @param event - */ - callbacks.blockPaste = function (event) { - - var currentInputIndex = codex.caret.getCurrentInputIndex(), - node = codex.state.inputs[currentInputIndex]; - - setTimeout(function () { - - codex.content.sanitize(node); - - event.preventDefault(); - }, 10); - - event.stopImmediatePropagation(); - }; - - /** - * This method is used to observe pasted dirty data. - * - * Mutation handlers send to separate observers each mutation (added, changed and so on), which will be - * passed from handler that sanitizes and replaces data. - * - * Probably won't be used - * - * @deprecated - * - * @param event - * @private - */ - callbacks._blockPasteCallback = function (event) { - - var currentInputIndex = codex.caret.getCurrentInputIndex(); - - /** - * create an observer instance - */ - var observer = new MutationObserver(codex.callback.handleMutationsOnPaste); - - /** - * configuration of the observer: - */ - var config = { - attributes: true, - childList: false, - characterData: false, - subtree: true - }; - - // pass in the target node, as well as the observer options - observer.observe(codex.state.inputs[currentInputIndex], config); - }; - - /** - * This method prevents default behaviour. - * - * We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes. - * Firstly, we need to memorize the caret position. We can do that by getting the range of selection. - * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node - * - * @param event - */ - callbacks.blockPasteCallback = function (event) { - - /** Prevent default behaviour */ - event.preventDefault(); - - /** get html pasted data - dirty data */ - var data = event.clipboardData.getData('text/html') || event.clipboardData.getData('text/plain'); - - /** Temporary DIV that is used to work with childs as arrays item */ - var div = codex.draw.node('DIV', '', {}), - cleaner = new codex.sanitizer.init(codex.sanitizer.Config.BASIC), - cleanData, - fragment; - - /** Create fragment, that we paste to range after proccesing */ - fragment = document.createDocumentFragment(); - - cleanData = cleaner.clean(data); - - div.innerHTML = cleanData; - - var node, lastNode; - - /** - * and fill in fragment - */ - while (node = div.firstChild) { - lastNode = fragment.appendChild(node); - } - - /** - * work with selection and range - */ - var selection, range; - selection = window.getSelection(); - - range = selection.getRangeAt(0); - range.deleteContents(); - - range.insertNode(fragment); - // document.execCommand('insertParagraph', false, "
"); - - /** Preserve the selection */ - if (lastNode) { - range = range.cloneRange(); - range.setStartAfter(lastNode); - range.collapse(true); - selection.removeAllRanges(); - selection.addRange(range); - } - }; - - /** - * Sends all mutations to paste handler - */ - callbacks.handleMutationsOnPaste = function (mutations) { - - var self = this; - - /** - * Calling function with context of this function. - * Also, we should sanitize pasted or changed data one time and ignore - * changings which makes sanitize method. - * For that, we need to send Context, MutationObserver.__proto__ that contains - * observer disconnect method. - */ - mutations.forEach(function (mutation) { - codex.content.paste.call(self, mutation); - }); - }; - - /** - * Clicks on block settings button - */ - callbacks.showSettingsButtonClicked = function () { - - /** - * Get type of current block - * It uses to append settings from tool.settings property. - * ... - * Type is stored in data-type attribute on block - */ - var currentToolType = codex.content.currentNode.dataset.tool; - - codex.toolbar.settings.toggle(currentToolType); - - /** Close toolbox when settings button is active */ - codex.toolbar.toolbox.close(); - codex.toolbar.settings.hideRemoveActions(); - }; - - callbacks.showCommentButtonClicked = function () { - - var block = codex.content.currentNode; - - codex.comments.add(block); - }; - - return callbacks; - }({}); - - module.exports = callbacks; - -/***/ }, -/* 14 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Draw module - * - * @author Codex Team - * @version 1.0. - */ - - var draw = function (draw) { - - /** - * Base editor wrapper - */ - draw.wrapper = function () { - - var wrapper = document.createElement('div'); - - wrapper.className += 'codex-editor'; - - return wrapper; - }; - - /** - * Content-editable holder - */ - draw.redactor = function () { - - var redactor = document.createElement('div'); - - redactor.className += 'ce-redactor'; - - return redactor; - }; - - /** - * Comments side bar - */ - draw.commentsSidebar = function () { - - var sidebar = draw.node('DIV', 'ce-comments-sidebar'); - - return sidebar; - }; - - draw.ceBlock = function () { - - var block = document.createElement('DIV'); - - block.className += 'ce_block'; - - return block; - }; - - /** - * Empty toolbar with toggler - */ - draw.toolbar = function () { - - var bar = document.createElement('div'); - - bar.className += 'ce-toolbar'; - - return bar; - }; - - draw.toolbarContent = function () { - - var wrapper = document.createElement('DIV'); - wrapper.classList.add('ce-toolbar__content'); - - return wrapper; - }; - - /** - * Inline toolbar - */ - draw.inlineToolbar = function () { - - var bar = document.createElement('DIV'); - - bar.className += 'ce-toolbar-inline'; - - return bar; - }; - - /** - * Wrapper for inline toobar buttons - */ - draw.inlineToolbarButtons = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.className += 'ce-toolbar-inline__buttons'; - - return wrapper; - }; - - /** - * For some actions - */ - draw.inlineToolbarActions = function () { - - var wrapper = document.createElement('DIV'); - - wrapper.className += 'ce-toolbar-inline__actions'; - - return wrapper; - }; - - draw.inputForLink = function () { - - var input = document.createElement('INPUT'); - - input.type = 'input'; - input.className += 'inputForLink'; - input.placeholder = 'Вставьте ссылку ...'; - input.setAttribute('form', 'defaultForm'); - - input.setAttribute('autofocus', 'autofocus'); - - return input; - }; - - /** - * Block with notifications - */ - draw.alertsHolder = function () { - - var block = document.createElement('div'); - - block.classList.add('ce_notifications-block'); - - return block; - }; - - /** - * @todo Desc - */ - draw.blockButtons = function () { - - var block = document.createElement('div'); - - block.className += 'ce-toolbar__actions'; - - return block; - }; - - /** - * Block settings panel - */ - draw.blockSettings = function () { - - var settings = document.createElement('div'); - - settings.className += 'ce-settings'; - - return settings; - }; - - draw.defaultSettings = function () { - - var div = document.createElement('div'); - - div.classList.add('ce-settings_default'); - - return div; - }; - - draw.pluginsSettings = function () { - - var div = document.createElement('div'); - - div.classList.add('ce-settings_plugin'); - - return div; - }; - - draw.plusButton = function () { - - var button = document.createElement('span'); - - button.className = 'ce-toolbar__plus'; - // button.innerHTML = ''; - - return button; - }; - - /** - * Settings button in toolbar - */ - draw.settingsButton = function () { - - var toggler = document.createElement('span'); - - toggler.className = 'ce-toolbar__settings-btn'; - - /** Toggler button*/ - toggler.innerHTML = ''; - - return toggler; - }; - - /** - * Comment button in toolbar - */ - draw.commentButton = function () { - - var toggler = document.createElement('span'); - - toggler.className = 'ce-toolbar__comment-btn'; - - /** Toggler button*/ - toggler.innerHTML = ''; - - return toggler; - }; - - /** - * Redactor tools wrapper - */ - - draw.toolbox = function () { - - var wrapper = document.createElement('div'); - - wrapper.className = 'ce-toolbar__tools'; - - return wrapper; - }; - - /** - * @protected - * - * Draws tool buttons for toolbox - * - * @param {String} type - * @param {String} classname - * @returns {Element} - */ - draw.toolbarButton = function (type, classname) { - - var button = document.createElement("li"), - tool_icon = document.createElement("i"), - tool_title = document.createElement("span"); - - button.dataset.type = type; - button.setAttribute('title', type); - - tool_icon.classList.add(classname); - tool_title.classList.add('ce_toolbar_tools--title'); - - button.appendChild(tool_icon); - button.appendChild(tool_title); - - return button; - }; - - /** - * @protected - * - * Draws tools for inline toolbar - * - * @param {String} type - * @param {String} classname - */ - draw.toolbarButtonInline = function (type, classname) { - var button = document.createElement("BUTTON"), - tool_icon = document.createElement("I"); - - button.type = "button"; - button.dataset.type = type; - tool_icon.classList.add(classname); - - button.appendChild(tool_icon); - - return button; - }; - - /** - * Redactor block - */ - draw.block = function (tagName, content) { - - var node = document.createElement(tagName); - - node.innerHTML = content || ''; - - return node; - }; - - /** - * Creates Node with passed tagName and className - * @param {string} tagName - * @param {string} className - * @param {object} properties - allow to assign properties - */ - draw.node = function (tagName, className, properties) { - - var el = document.createElement(tagName); - - if (className) el.className = className; - - if (properties) { - - for (var name in properties) { - el[name] = properties[name]; - } - } - - return el; - }; - - draw.pluginsRender = function (type, content) { - - return { - type: type, - block: cEditor.tools[type].render({ - text: content - }) - }; - }; - - return draw; - }({}); - - module.exports = draw; - -/***/ }, -/* 15 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Caret Module - * - * @author Codex Team - * @version 1.0 - */ - var editor = codex.editor; - - module.exports = function (caret) { - - /** - * @var {int} InputIndex - editable element in DOM - */ - caret.inputIndex = null; - - /** - * @var {int} offset - caret position in a text node. - */ - caret.offset = null; - - /** - * @var {int} focusedNodeIndex - we get index of child node from first-level block - */ - caret.focusedNodeIndex = null; - - /** - * Creates Document Range and sets caret to the element. - * @protected - * @uses caret.save — if you need to save caret position - * @param {Element} el - Changed Node. - */ - caret.set = function (el, index, offset) { - - offset = offset || caret.offset || 0; - index = index || caret.focusedNodeIndex || 0; - - var childs = el.childNodes, - nodeToSet; - - if (childs.length === 0) { - - nodeToSet = el; - } else { - - nodeToSet = childs[index]; - } - - /** If Element is INPUT */ - if (el.tagName == 'INPUT') { - - el.focus(); - return; - } - - if (editor.core.isDomNode(nodeToSet)) { - - nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length); - } - - var range = document.createRange(), - selection = window.getSelection(); - - window.setTimeout(function () { - - range.setStart(nodeToSet, offset); - range.setEnd(nodeToSet, offset); - - selection.removeAllRanges(); - selection.addRange(range); - - editor.caret.saveCurrentInputIndex(); - }, 20); - }; - - /** - * @protected - * Updates index of input and saves it in caret object - */ - caret.saveCurrentInputIndex = function () { - - /** Index of Input that we paste sanitized content */ - var selection = window.getSelection(), - inputs = editor.state.inputs, - focusedNode = selection.anchorNode, - focusedNodeHolder; - - if (!focusedNode) { - - return; - } - - /** Looking for parent contentEditable block */ - while (focusedNode.contentEditable != 'true') { - - focusedNodeHolder = focusedNode.parentNode; - focusedNode = focusedNodeHolder; - } - - /** Input index in DOM level */ - var editableElementIndex = 0; - - while (focusedNode != inputs[editableElementIndex]) { - - editableElementIndex++; - } - - caret.inputIndex = editableElementIndex; - }; - - /** - * Returns current input index (caret object) - */ - caret.getCurrentInputIndex = function () { - - return caret.inputIndex; - }; - - /** - * @param {int} index - index of first-level block after that we set caret into next input - */ - caret.setToNextBlock = function (index) { - - var inputs = editor.state.inputs, - nextInput = inputs[index + 1]; - - if (!nextInput) { - - editor.core.log('We are reached the end'); - return; - } - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!nextInput.childNodes.length) { - - var emptyTextElement = document.createTextNode(''); - - nextInput.appendChild(emptyTextElement); - } - - editor.caret.inputIndex = index + 1; - editor.caret.set(nextInput, 0, 0); - editor.content.workingNodeChanged(nextInput); - }; - - /** - * @param {int} index - index of target input. - * Sets caret to input with this index - */ - caret.setToBlock = function (index) { - - var inputs = editor.state.inputs, - targetInput = inputs[index]; - - if (!targetInput) { - - return; - } - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!targetInput.childNodes.length) { - - var emptyTextElement = document.createTextNode(''); - - targetInput.appendChild(emptyTextElement); - } - - editor.caret.inputIndex = index; - editor.caret.set(targetInput, 0, 0); - editor.content.workingNodeChanged(targetInput); - }; - - /** - * @param {int} index - index of input - */ - caret.setToPreviousBlock = function (index) { - - index = index || 0; - - var inputs = editor.state.inputs, - previousInput = inputs[index - 1], - lastChildNode, - lengthOfLastChildNode, - emptyTextElement; - - if (!previousInput) { - - editor.core.log('We are reached first node'); - return; - } - - lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length); - lengthOfLastChildNode = lastChildNode.length; - - /** - * When new Block created or deleted content of input - * We should add some text node to set caret - */ - if (!previousInput.childNodes.length) { - - emptyTextElement = document.createTextNode(''); - previousInput.appendChild(emptyTextElement); - } - editor.caret.inputIndex = index - 1; - editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode); - editor.content.workingNodeChanged(inputs[index - 1]); - }; - - caret.position = { - - atStart: function atStart() { - - var selection = window.getSelection(), - anchorOffset = selection.anchorOffset, - anchorNode = selection.anchorNode, - firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode), - pluginsRender = firstLevelBlock.childNodes[0]; - - if (!editor.core.isDomNode(anchorNode)) { - - anchorNode = anchorNode.parentNode; - } - - var isFirstNode = anchorNode === pluginsRender.childNodes[0], - isOffsetZero = anchorOffset === 0; - - return isFirstNode && isOffsetZero; - }, - - atTheEnd: function atTheEnd() { - - var selection = window.getSelection(), - anchorOffset = selection.anchorOffset, - anchorNode = selection.anchorNode; - - /** Caret is at the end of input */ - return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length; - } - }; - - return caret; - }({}); - -/***/ }, -/* 16 */ -/***/ function(module, exports) { - - 'use strict'; - - /** - * Codex Editor Notification Module - * - * @author Codex Team - * @version 1.0 - */ - var editor = codex.editor; - - module.exports = function (notifications) { - - /** - * Error notificator. Shows block with message - * @protected - */ - notifications.errorThrown = function (errorMsg, event) { - - editor.notifications.send('This action is not available currently', event.type, false); - }; - - /** - * Appends notification with different types - * @param message {string} - Error or alert message - * @param type {string} - Type of message notification. Ex: Error, Warning, Danger ... - * @param append {boolean} - can be True or False when notification should be inserted after - */ - notifications.send = function (message, type, append) { - - var notification = editor.draw.block('div'); - - notification.textContent = message; - notification.classList.add('ce_notification-item', 'ce_notification-' + type, 'flipInX'); - - if (!append) { - - editor.nodes.notifications.innerHTML = ''; - } - - editor.nodes.notifications.appendChild(notification); - - window.setTimeout(function () { - - notification.remove(); - }, 3000); - }; - - return notifications; - }({}); - -/***/ }, -/* 17 */ -/***/ function(module, exports) { - - "use strict"; - - /** - * Codex Editor Parser Module - * - * @author Codex Team - * @version 1.1 - */ - var editor = codex.editor; - - module.exports = function (parser) { - - /** inserting text */ - parser.insertPastedContent = function (blockType, tag) { - - editor.content.insertBlock({ - type: blockType.type, - block: blockType.render({ - text: tag.innerHTML - }) - }); - }; - - /** - * Check DOM node for display style: separated block or child-view - */ - parser.isFirstLevelBlock = function (node) { - - return node.nodeType == editor.core.nodeTypes.TAG && node.classList.contains(editor.ui.className.BLOCK_CLASSNAME); - }; - - return parser; - }({}); - -/***/ }, -/* 18 */ -/***/ function(module, exports, __webpack_require__) { - - 'use strict'; - - /** - * Codex Sanitizer - */ - - var janitor = __webpack_require__(19); - - module.exports = function (sanitizer) { - - /** - * Basic config - */ - var Config = { - - BASIC: { - - tags: { - p: {}, - a: { - href: true, - target: '_blank', - rel: 'nofollow' - }, - i: {}, - b: {}, - strong: {}, - em: {}, - span: {} - } - } - }; - - sanitizer.Config = Config; - - sanitizer.init = janitor; - - return sanitizer; - }({}); - -/***/ }, -/* 19 */ -/***/ function(module, exports, __webpack_require__) { - - var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_RESULT__;(function (root, factory) { - if (true) { - !(__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__)); - } else if (typeof exports === 'object') { - module.exports = factory(); - } else { - root.HTMLJanitor = factory(); - } - }(this, function () { - - /** - * @param {Object} config.tags Dictionary of allowed tags. - * @param {boolean} config.keepNestedBlockElements Default false. - */ - function HTMLJanitor(config) { - - var tagDefinitions = config['tags']; - var tags = Object.keys(tagDefinitions); - - var validConfigValues = tags - .map(function(k) { return typeof tagDefinitions[k]; }) - .every(function(type) { return type === 'object' || type === 'boolean' || type === 'function'; }); - - if(!validConfigValues) { - throw new Error("The configuration was invalid"); - } - - this.config = config; - } - - // TODO: not exhaustive? - var blockElementNames = ['P', 'LI', 'TD', 'TH', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'PRE']; - function isBlockElement(node) { - return blockElementNames.indexOf(node.nodeName) !== -1; - } - - var inlineElementNames = ['A', 'B', 'STRONG', 'I', 'EM', 'SUB', 'SUP', 'U', 'STRIKE']; - function isInlineElement(node) { - return inlineElementNames.indexOf(node.nodeName) !== -1; - } - - HTMLJanitor.prototype.clean = function (html) { - var sandbox = document.createElement('div'); - sandbox.innerHTML = html; - - this._sanitize(sandbox); - - return sandbox.innerHTML; - }; - - HTMLJanitor.prototype._sanitize = function (parentNode) { - var treeWalker = createTreeWalker(parentNode); - var node = treeWalker.firstChild(); - if (!node) { return; } - - do { - // Ignore nodes that have already been sanitized - if (node._sanitized) { - continue; - } - - if (node.nodeType === Node.TEXT_NODE) { - // If this text node is just whitespace and the previous or next element - // sibling is a block element, remove it - // N.B.: This heuristic could change. Very specific to a bug with - // `contenteditable` in Firefox: http://jsbin.com/EyuKase/1/edit?js,output - // FIXME: make this an option? - if (node.data.trim() === '' - && ((node.previousElementSibling && isBlockElement(node.previousElementSibling)) - || (node.nextElementSibling && isBlockElement(node.nextElementSibling)))) { - parentNode.removeChild(node); - this._sanitize(parentNode); - break; - } else { - continue; - } - } - - // Remove all comments - if (node.nodeType === Node.COMMENT_NODE) { - parentNode.removeChild(node); - this._sanitize(parentNode); - break; - } - - var isInline = isInlineElement(node); - var containsBlockElement; - if (isInline) { - containsBlockElement = Array.prototype.some.call(node.childNodes, isBlockElement); - } - - // Block elements should not be nested (e.g.
...); if
- // they are, we want to unwrap the inner block element.
- var isNotTopContainer = !! parentNode.parentNode;
- var isNestedBlockElement =
- isBlockElement(parentNode) &&
- isBlockElement(node) &&
- isNotTopContainer;
-
- var nodeName = node.nodeName.toLowerCase();
-
- var allowedAttrs = getAllowedAttrs(this.config, nodeName, node);
-
- var isInvalid = isInline && containsBlockElement;
-
- // Drop tag entirely according to the whitelist *and* if the markup
- // is invalid.
- if (isInvalid || shouldRejectNode(node, allowedAttrs)
- || (!this.config.keepNestedBlockElements && isNestedBlockElement)) {
- // Do not keep the inner text of SCRIPT/STYLE elements.
- if (! (node.nodeName === 'SCRIPT' || node.nodeName === 'STYLE')) {
- while (node.childNodes.length > 0) {
- parentNode.insertBefore(node.childNodes[0], node);
- }
- }
- parentNode.removeChild(node);
-
- this._sanitize(parentNode);
- break;
- }
-
- // Sanitize attributes
- for (var a = 0; a < node.attributes.length; a += 1) {
- var attr = node.attributes[a];
-
- if (shouldRejectAttr(attr, allowedAttrs, node)) {
- node.removeAttribute(attr.name);
- // Shift the array to continue looping.
- a = a - 1;
- }
- }
-
- // Sanitize children
- this._sanitize(node);
-
- // Mark node as sanitized so it's ignored in future runs
- node._sanitized = true;
- } while ((node = treeWalker.nextSibling()));
- };
-
- function createTreeWalker(node) {
- return document.createTreeWalker(node,
- NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT,
- null, false);
- }
-
- function getAllowedAttrs(config, nodeName, node){
- if (typeof config.tags[nodeName] === 'function') {
- return config.tags[nodeName](node);
- } else {
- return config.tags[nodeName];
- }
- }
-
- function shouldRejectNode(node, allowedAttrs){
- if (typeof allowedAttrs === 'undefined') {
- return true;
- } else if (typeof allowedAttrs === 'boolean') {
- return !allowedAttrs;
- }
-
- return false;
- }
-
- function shouldRejectAttr(attr, allowedAttrs, node){
- var attrName = attr.name.toLowerCase();
-
- if (allowedAttrs === true){
- return false;
- } else if (typeof allowedAttrs[attrName] === 'function'){
- return !allowedAttrs[attrName](attr.value, node);
- } else if (typeof allowedAttrs[attrName] === 'undefined'){
- return true;
- } else if (allowedAttrs[attrName] === false) {
- return true;
- } else if (typeof allowedAttrs[attrName] === 'string') {
- return (allowedAttrs[attrName] !== attr.value);
- }
-
- return false;
- }
-
- return HTMLJanitor;
-
- }));
-
-
-/***/ },
-/* 20 */
-/***/ function(module, exports) {
-
- 'use strict';
-
- var comments = function (comments) {
-
- var draw = {
-
- commentsField: function commentsField(blockId) {
-
- var field = codex.draw.node('DIV', 'ce-comments-field');
-
- field.dataset.blockId = blockId;
-
- return field;
- },
-
- input: function input(text) {
-
- var wrapper = codex.draw.node('DIV', 'ce-comment'),
- input = codex.draw.node('DIV', 'ce-comment__input', { 'contentEditable': 'true', 'textContent': text || '' }),
- deleteBtn = codex.draw.node('DIV', 'ce-comment__delete', { 'textContent': 'Delete' }),
- postBtn = codex.draw.node('DIV', 'ce-comment__post', { 'textContent': text ? 'Save' : 'Comment' });
-
- postBtn.addEventListener('click', callbacks.commentClicked);
- deleteBtn.addEventListener('click', callbacks.deleteClicked);
-
- wrapper.appendChild(input);
- wrapper.appendChild(postBtn);
- wrapper.appendChild(deleteBtn);
-
- wrapper.dataset.edited = text ? 'edited ' : '';
-
- return wrapper;
- },
-
- comment: function comment(data) {
-
- if (!data.text) return;
-
- var wrapper = codex.draw.node('DIV', 'ce-comment'),
- text = codex.draw.node('DIV', 'ce-comment__text', { 'textContent': data.text }),
- date = new Date().toLocaleDateString('en-US', {
- month: 'short',
- day: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- hour12: false
- }),
- time = codex.draw.node('DIV', 'ce-comment__time', { 'textContent': date }),
- deleteBtn = codex.draw.node('DIV', 'ce-comment__delete', { 'textContent': 'Delete' }),
- editBtn = codex.draw.node('DIV', 'ce-comment__edit', { 'textContent': 'Edit' });
-
- editBtn.addEventListener('click', callbacks.editClicked);
- deleteBtn.addEventListener('click', callbacks.deleteClicked);
-
- wrapper.dataset.edited = data.edited;
- time.dataset.edited = data.edited;
-
- wrapper.appendChild(text);
- wrapper.appendChild(time);
- wrapper.appendChild(editBtn);
- wrapper.appendChild(deleteBtn);
-
- return wrapper;
- }
-
- };
-
- var callbacks = {
-
- commentClicked: function commentClicked(e) {
-
- var field = e.path[2],
- wrapper = e.path[1],
- input = wrapper.querySelector('.ce-comment__input');
-
- if (input.textContent.trim() == '') return;
-
- var comment = draw.comment({
- text: input.textContent,
- edited: wrapper.dataset.edited
- });
-
- field.replaceChild(comment, wrapper);
- },
-
- editClicked: function editClicked(e) {
-
- var field = e.path[2],
- wrapper = e.path[1],
- text = wrapper.querySelector('.ce-comment__text');
-
- var comment = draw.input(text.textContent);
-
- field.replaceChild(comment, wrapper);
- },
-
- deleteClicked: function deleteClicked(e) {
-
- var field = e.path[2],
- wrapper = e.path[1];
-
- field.removeChild(wrapper);
- }
-
- };
-
- var methods = {
-
- getCoords: function getCoords(block) {
-
- var rect = block.getBoundingClientRect();
-
- return {
- x: pageXOffset + rect.left,
- y: pageYOffset + rect.top
- };
- }
-
- };
-
- comments.add = function (block) {
-
- var blockId = block.dataset.id;
-
- var field = codex.nodes.commentsSidebar.querySelector('.ce-comments-field[data-block-id="' + blockId + '"]') || draw.commentsField(blockId);
-
- var comment = draw.input();
-
- comment.querySelector('.ce-comment__input').focus();
-
- field.appendChild(comment);
-
- var blockCoords = methods.getCoords(block);
-
- field.style.top = blockCoords.y + 'px';
-
- codex.nodes.commentsSidebar.appendChild(field);
- };
-
- return comments;
- }({});
-
- module.exports = comments;
-
-/***/ }
-/******/ ]);
+var codex=codex||{};codex.editor=function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={exports:{},id:o,loaded:!1};return e[o].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var n={};return t.m=e,t.c=n,t.p="",t(0)}([function(e,t,n){"use strict";e.exports=function(e){e.version="1.4.0";var t=function(){e.core=n(1),e.ui=n(2),e.transport=n(3),e.renderer=n(4),e.saver=n(5),e.content=n(6),e.toolbar=n(7),e.callback=n(11),e.draw=n(12),e.caret=n(13),e.notifications=n(14),e.parser=n(15),e.sanitizer=n(16),e.comments=n(18)};return e.settings={tools:["paragraph","header","picture","list","quote","code","twitter","instagram","smile"],textareaId:"codex-editor",uploadImagesUrl:"/editor/transport/",initialBlockPlugin:"paragraph"},e.nodes={textarea:null,wrapper:null,toolbar:null,inlineToolbar:{wrapper:null,buttons:null,actions:null},toolbox:null,notifications:null,plusButton:null,showSettingsButton:null,showTrashButton:null,showCommentButton:null,blockSettings:null,pluginSettings:null,defaultSettings:null,toolbarButtons:{},redactor:null,commentsSidebar:null},e.state={jsonOutput:[],blocks:[],inputs:[],comments:[]},e.tools={},e.start=function(n){t(),e.core.prepare(n).then(e.ui.make).then(e.ui.addTools).then(e.ui.bindEvents).then(e.ui.preparePlugins).then(e.transport.prepare).then(e.renderer.makeBlocksFromData).then(e.ui.saveInputs).catch(function(t){e.core.log("Initialization failed with error: %o","warn",t)})},e}({})},function(e,t){"use strict";var n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},o=codex.editor;e.exports=function(e){return e.prepare=function(e){return new Promise(function(t,r){e&&(o.settings.tools=e.tools||o.settings.tools),e.data&&(o.state.blocks=e.data),e.initialBlockPlugin&&(o.settings.initialBlockPlugin=e.initialBlockPlugin),e.uploadImagesUrl&&(o.settings.uploadImagesUrl=e.uploadImagesUrl),o.nodes.textarea=document.getElementById(e.textareaId||o.settings.textareaId),void 0===n(o.nodes.textarea)||null===o.nodes.textarea?r(Error("Textarea wasn't found by ID: #"+e.textareaId)):t()})},e.log=function(e,t,n){t=t||"log",n?e="[codex-editor]: "+e:(n=e||"undefined",e="[codex-editor]: %o");try{"console"in window&&window.console[t]&&(n?window.console[t](e,n):window.console[t](e))}catch(e){}},e.insertAfter=function(e,t){e.parentNode.insertBefore(t,e.nextSibling)},e.nodeTypes={TAG:1,TEXT:3,COMMENT:8},e.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},e.isDomNode=function(e){return e&&"object"===("undefined"==typeof e?"undefined":n(e))&&e.nodeType&&e.nodeType==this.nodeTypes.TAG},e.ajax=function(e){if(e&&e.url){var t,n=window.XMLHttpRequest?new XMLHttpRequest:new ActiveXObject("Microsoft.XMLHTTP"),o=function(){},r="";if(e.async=!0,e.type=e.type||"GET",e.data=e.data||"",e["content-type"]=e["content-type"]||"application/json; charset=utf-8",o=e.success||o,"GET"==e.type&&e.data)e.url=/\?/.test(e.url)?e.url+"&"+e.data:e.url+"?"+e.data;else for(t in e.data)r+=t+"="+encodeURIComponent(e.data[t])+"&";e.withCredentials&&(n.withCredentials=!0),e.beforeSend&&"function"==typeof e.beforeSend&&e.beforeSend.call(),n.open(e.type,e.url,e.async),n.setRequestHeader("X-Requested-With","XMLHttpRequest"),n.setRequestHeader("Content-type","application/x-www-form-urlencoded"),n.onreadystatechange=function(){4==n.readyState&&200==n.status&&o(n.responseText)},n.send(r)}},e.importScript=function(e,t){return new Promise(function(n,o){var r="cdx-script-",i=void 0;t?document.getElementById(r+t)&&n(e):o("Instance name is missed"),i=document.createElement("SCRIPT"),i.async=!0,i.defer=!0,i.id=r+t,i.onload=function(){n(e)},i.onerror=function(){o(e)},i.src=e,document.head.appendChild(i)})},e}({})},function(e,t){"use strict";var n=codex.editor;e.exports=function(e){return e.className={BLOCK_CLASSNAME:"ce-block",BLOCK_CONTENT:"ce-block__content",BLOCK_STRETCHED:"ce-block--stretched",BLOCK_HIGHLIGHTED:"ce-block--focused",BLOCK_IN_FEED_MODE:"ce-block--feed-mode",SETTINGS_ITEM:"ce-settings__item"},e.make=function(){var e,t,o,r,i,a,s,c,l,d,u,p,f;e=n.draw.wrapper(),n.core.insertAfter(n.nodes.textarea,e),a=n.draw.alertsHolder(),n.nodes.notifications=document.body.appendChild(a),t=n.draw.toolbar(),o=n.draw.toolbarContent(),f=n.draw.plusButton(),l=n.draw.settingsButton(),u=n.draw.commentButton(),d=n.toolbar.settings.makeRemoveBlockButton(),c=n.draw.blockSettings(),s=n.draw.blockButtons(),p=n.draw.toolbox(),r=n.draw.redactor(),i=n.draw.commentsSidebar();var m=n.draw.defaultSettings(),h=n.draw.pluginsSettings();c.appendChild(h),c.appendChild(m),s.appendChild(l),s.appendChild(d),s.appendChild(u),s.appendChild(c),o.appendChild(f),o.appendChild(p),t.appendChild(s),t.appendChild(o),e.appendChild(t),e.appendChild(i),e.appendChild(r),n.nodes.wrapper=e,n.nodes.toolbar=t,n.nodes.plusButton=f,n.nodes.toolbox=p,n.nodes.blockSettings=c,n.nodes.pluginSettings=h,n.nodes.defaultSettings=m,n.nodes.showSettingsButton=l,n.nodes.showTrashButton=d,n.nodes.showCommentButton=u,n.nodes.commentsSidebar=i,n.nodes.redactor=r,n.ui.makeInlineToolbar(),n.toolbar.settings.addDefaultSettings()},e.makeInlineToolbar=function(){var e=n.draw.inlineToolbar();n.nodes.inlineToolbar.wrapper=e,n.nodes.inlineToolbar.buttons=n.draw.inlineToolbarButtons(),n.nodes.inlineToolbar.actions=n.draw.inlineToolbarActions(),n.nodes.inlineToolbar.wrapper.appendChild(n.nodes.inlineToolbar.buttons),n.nodes.inlineToolbar.wrapper.appendChild(n.nodes.inlineToolbar.actions),n.nodes.wrapper.appendChild(n.nodes.inlineToolbar.wrapper)},e.addTools=function(){var e,t,o;for(t in n.settings.tools)e=n.settings.tools[t],n.tools[t]=e,e.iconClassname?"function"==typeof e.render?e.displayInToolbox&&(o=n.draw.toolbarButton(t,e.iconClassname),n.nodes.toolbox.appendChild(o),n.nodes.toolbarButtons[t]=o):n.core.log("render method missed. Tool %o skipped","warn",t):n.core.log("Toolbar icon classname missed. Tool %o skipped","warn",t);n.ui.addInlineToolbarTools()},e.addInlineToolbarTools=function(){var e,t,o={bold:{icon:"ce-icon-bold",command:"bold"},italic:{icon:"ce-icon-italic",command:"italic"},underline:{icon:"ce-icon-underline",command:"underline"},link:{icon:"ce-icon-link",command:"createLink"}};for(var r in o)t=o[r],e=n.draw.toolbarButtonInline(r,t.icon),n.nodes.inlineToolbar.buttons.appendChild(e),n.ui.setInlineToolbarButtonBehaviour(e,t.command)},e.bindEvents=function(){n.core.log("ui.bindEvents fired","info"),document.addEventListener("keydown",n.callback.globalKeydown,!1),n.nodes.redactor.addEventListener("keydown",n.callback.redactorKeyDown,!1),document.addEventListener("keyup",n.callback.globalKeyup,!1),n.nodes.redactor.addEventListener("click",n.callback.redactorClicked,!1),n.nodes.plusButton.addEventListener("click",n.callback.plusButtonClicked,!1),n.nodes.showSettingsButton.addEventListener("click",n.callback.showSettingsButtonClicked,!1),n.nodes.showCommentButton.addEventListener("click",n.callback.showCommentButtonClicked,!1);for(var e in n.nodes.toolbarButtons)n.nodes.toolbarButtons[e].addEventListener("click",n.callback.toolbarButtonClicked,!1)},e.preparePlugins=function(){return new Promise(function(e,t){var o=void 0,r=void 0;for(o in n.tools)r=n.tools[o],"function"==typeof r.prepare&&r.prepare(r.config||{}).then(function(){e()}).catch(function(e){t(e)})})},e.addBlockHandlers=function(e){e&&(e.addEventListener("keydown",n.callback.blockKeydown,!1),e.addEventListener("paste",n.callback.blockPasteCallback,!1),e.addEventListener("mouseup",n.toolbar.inline.show,!1))},e.saveInputs=function(){var e=n.nodes.redactor;n.state.inputs=e.querySelectorAll("[contenteditable], input")},e.addInitialBlock=function(){var e,t=n.settings.initialBlockPlugin;return n.tools[t]?(e=n.tools[t].render(),e.setAttribute("data-placeholder","Расскажите свою историю..."),n.content.insertBlock({type:t,block:e}),void n.content.workingNodeChanged(e)):void n.core.log("Plugin %o was not implemented and can't be used as initial block","warn",t)},e.setInlineToolbarButtonBehaviour=function(e,t){e.addEventListener("mousedown",function(e){n.toolbar.inline.toolClicked(e,t)},!1)},e}({})},function(e,t){"use strict";var n=codex.editor;e.exports=function(e){return e.input=null,e.arguments=null,e.prepare=function(){var e=document.createElement("INPUT");e.type="file",e.addEventListener("change",n.transport.fileSelected),n.transport.input=e},e.clearInput=function(){this.input=null,this.prepare()},e.fileSelected=function(){var e=this,t=e.files,o=new FormData;o.append("files",t[0],t[0].name),n.transport.ajax({data:o,beforeSend:n.transport.arguments.beforeSend,success:n.transport.arguments.success,error:n.transport.arguments.error})},e.selectAndUpload=function(e){this.arguments=e,this.input.click()},e.ajax=function(e){var t=new XMLHttpRequest,o="function"==typeof e.beforeSend?e.beforeSend:function(){},r="function"==typeof e.success?e.success:function(){},i="function"==typeof e.error?e.error:function(){};o(),t.open("POST",n.settings.uploadImagesUrl,!0),t.setRequestHeader("X-Requested-With","XMLHttpRequest"),t.onload=function(){200===t.status?r(t.responseText):(n.core.log("request error: %o",t),i())},t.send(e.data),this.clearInput()},e}({})},function(e,t){"use strict";var n=codex.editor;e.exports=function(e){return e.makeBlocksFromData=function(){return n.state.blocks.items.length?void Promise.resolve().then(function(){return n.state.blocks}).then(n.renderer.appendBlocks).catch(function(e){n.core.log("Error while parsing JSON: %o","error",e)}):void n.ui.addInitialBlock()},e.appendBlocks=function(e){for(var t=e.items,o=Promise.resolve(),r=0;r in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\r\n */\r\n if ( event.shiftKey && !enableLineBreaks ) {\r\n codex.callback.enterPressedOnBlock(codex.content.currentBlock, event);\r\n event.preventDefault();\r\n return;\r\n }\r\n\r\n /**\r\n * Workaround situation when caret at the Text node that has some wrapper Elements\r\n * Split block cant handle this.\r\n * We need to save default behavior\r\n */\r\n isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != \"true\";\r\n\r\n /**\r\n * Split blocks when input has several nodes and caret placed in textNode\r\n */\r\n if (\r\n currentSelectedNode.nodeType == codex.core.nodeTypes.TEXT &&\r\n !isTextNodeHasParentBetweenContenteditable &&\r\n !caretAtTheEndOfText\r\n ){\r\n\r\n event.preventDefault();\r\n\r\n codex.core.log('Splitting Text node...');\r\n\r\n codex.content.splitBlock(currentInputIndex);\r\n\r\n /** Show plus button when next input after split is empty*/\r\n if (!codex.state.inputs[currentInputIndex + 1].textContent.trim()) {\r\n codex.toolbar.showPlusButton();\r\n }\r\n\r\n } else {\r\n\r\n var islastNode = codex.content.isLastNode(currentSelectedNode);\r\n\r\n if ( islastNode && caretAtTheEndOfText ) {\r\n\r\n event.preventDefault();\r\n event.stopPropagation();\r\n event.stopImmediatePropagation();\r\n\r\n codex.core.log('ENTER clicked in last textNode. Create new BLOCK');\r\n\r\n codex.content.insertBlock({\r\n type: NEW_BLOCK_TYPE,\r\n block: codex.tools[NEW_BLOCK_TYPE].render()\r\n }, true);\r\n\r\n codex.toolbar.move();\r\n codex.toolbar.open();\r\n\r\n /** Show plus button with empty block */\r\n codex.toolbar.showPlusButton();\r\n\r\n }\r\n\r\n }\r\n\r\n /** get all inputs after new appending block */\r\n codex.ui.saveInputs();\r\n\r\n };\r\n\r\n callbacks.escapeKeyPressed = function(event){\r\n\r\n /** Close all toolbar */\r\n codex.toolbar.close();\r\n\r\n /** Close toolbox */\r\n codex.toolbar.toolbox.close();\r\n\r\n event.preventDefault();\r\n\r\n };\r\n\r\n callbacks.arrowKeyPressed = function(event){\r\n\r\n codex.content.workingNodeChanged();\r\n\r\n /* Closing toolbar */\r\n codex.toolbar.close();\r\n codex.toolbar.move();\r\n\r\n };\r\n\r\n callbacks.defaultKeyPressed = function(event) {\r\n\r\n codex.toolbar.close();\r\n\r\n if (!codex.toolbar.inline.actionsOpened) {\r\n codex.toolbar.inline.close();\r\n codex.content.clearMark();\r\n }\r\n };\r\n\r\n callbacks.redactorClicked = function (event) {\r\n\r\n callbacks.detectWhenClickedOnFirstLevelBlockArea();\r\n\r\n codex.content.workingNodeChanged(event.target);\r\n\r\n codex.ui.saveInputs();\r\n\r\n var selectedText = codex.toolbar.inline.getSelectionText();\r\n\r\n /**\r\n * If selection range took off, then we hide inline toolbar\r\n */\r\n if (selectedText.length === 0) {\r\n codex.toolbar.inline.close();\r\n }\r\n\r\n /** Update current input index in memory when caret focused into existed input */\r\n if (event.target.contentEditable == 'true') {\r\n\r\n codex.caret.saveCurrentInputIndex();\r\n\r\n }\r\n\r\n if (codex.content.currentNode === null) {\r\n\r\n /**\r\n * If inputs in redactor does not exits, then we put input index 0 not -1\r\n */\r\n var indexOfLastInput = codex.state.inputs.length > 0 ? codex.state.inputs.length - 1 : 0;\r\n\r\n /** If we have any inputs */\r\n if (codex.state.inputs.length) {\r\n\r\n /** getting firstlevel parent of input */\r\n var firstLevelBlock = codex.content.getFirstLevelBlock(codex.state.inputs[indexOfLastInput]);\r\n }\r\n\r\n /** If input is empty, then we set caret to the last input */\r\n if (codex.state.inputs.length && codex.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == codex.settings.initialBlockPlugin) {\r\n\r\n codex.caret.setToBlock(indexOfLastInput);\r\n\r\n } else {\r\n\r\n /** Create new input when caret clicked in redactors area */\r\n var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin;\r\n\r\n codex.content.insertBlock({\r\n type : NEW_BLOCK_TYPE,\r\n block : codex.tools[NEW_BLOCK_TYPE].render()\r\n });\r\n\r\n /** If there is no inputs except inserted */\r\n if (codex.state.inputs.length === 1) {\r\n\r\n codex.caret.setToBlock(indexOfLastInput);\r\n\r\n } else {\r\n\r\n /** Set caret to this appended input */\r\n codex.caret.setToNextBlock(indexOfLastInput);\r\n }\r\n }\r\n\r\n /**\r\n * Move toolbar to the right position and open\r\n */\r\n codex.toolbar.move();\r\n\r\n\r\n codex.toolbar.open();\r\n\r\n } else {\r\n\r\n /**\r\n * Move toolbar to the new position and open\r\n */\r\n codex.toolbar.move();\r\n\r\n codex.toolbar.open();\r\n\r\n /** Close all panels */\r\n codex.toolbar.settings.close();\r\n codex.toolbar.toolbox.close();\r\n }\r\n\r\n\r\n var inputIsEmpty = !codex.content.currentNode.textContent.trim(),\r\n currentNodeType = codex.content.currentNode.dataset.tool,\r\n isInitialType = currentNodeType == codex.settings.initialBlockPlugin;\r\n\r\n\r\n /** Hide plus buttons */\r\n codex.toolbar.hidePlusButton();\r\n\r\n /** Mark current block */\r\n codex.content.markBlock();\r\n\r\n\r\n if ( isInitialType && inputIsEmpty ) {\r\n\r\n /** Show plus button */\r\n codex.toolbar.showPlusButton();\r\n\r\n }\r\n\r\n\r\n };\r\n\r\n /**\r\n * This method allows to define, is caret in contenteditable element or not.\r\n * Otherwise, if we get TEXT node from range container, that will means we have input index.\r\n * In this case we use default browsers behaviour (if plugin allows that) or overwritten action.\r\n * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always\r\n * specifies to the first-level block. Other cases we just ignore.\r\n */\r\n callbacks.detectWhenClickedOnFirstLevelBlockArea = function() {\r\n\r\n var selection = window.getSelection(),\r\n anchorNode = selection.anchorNode,\r\n flag = false;\r\n\r\n if (selection.rangeCount == 0) {\r\n\r\n codex.content.editorAreaHightlighted = true;\r\n\r\n } else {\r\n\r\n if (!codex.core.isDomNode(anchorNode)) {\r\n anchorNode = anchorNode.parentNode;\r\n }\r\n\r\n /** Already founded, without loop */\r\n if (anchorNode.contentEditable == 'true') {\r\n flag = true;\r\n }\r\n\r\n while (anchorNode.contentEditable != 'true') {\r\n anchorNode = anchorNode.parentNode;\r\n\r\n if (anchorNode.contentEditable == 'true') {\r\n flag = true;\r\n }\r\n\r\n if (anchorNode == document.body) {\r\n break;\r\n }\r\n }\r\n\r\n /** If editable element founded, flag is \"TRUE\", Therefore we return \"FALSE\" */\r\n codex.content.editorAreaHightlighted = flag ? false : true;\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Toolbar button click handler\r\n * @param this - cursor to the button\r\n */\r\n callbacks.toolbarButtonClicked = function (event) {\r\n\r\n var button = this;\r\n\r\n codex.toolbar.current = button.dataset.type;\r\n\r\n codex.toolbar.toolbox.toolClicked(event);\r\n codex.toolbar.close();\r\n\r\n };\r\n\r\n callbacks.redactorInputEvent = function (event) {\r\n\r\n /**\r\n * Clear previous sync-timeout\r\n */\r\n if (this.redactorSyncTimeout){\r\n clearTimeout(this.redactorSyncTimeout);\r\n }\r\n\r\n /**\r\n * Start waiting to input finish and sync redactor\r\n */\r\n this.redactorSyncTimeout = setTimeout(function() {\r\n\r\n codex.content.sync();\r\n\r\n }, 500);\r\n\r\n };\r\n\r\n /** Show or Hide toolbox when plus button is clicked */\r\n callbacks.plusButtonClicked = function() {\r\n\r\n if (!codex.nodes.toolbox.classList.contains('opened')) {\r\n\r\n codex.toolbar.toolbox.open();\r\n\r\n } else {\r\n\r\n codex.toolbar.toolbox.close();\r\n\r\n }\r\n };\r\n\r\n /**\r\n * Block handlers for KeyDown events\r\n */\r\n callbacks.blockKeydown = function(event, block) {\r\n\r\n switch (event.keyCode){\r\n\r\n case codex.core.keys.DOWN:\r\n case codex.core.keys.RIGHT:\r\n codex.callback.blockRightOrDownArrowPressed(block);\r\n break;\r\n\r\n case codex.core.keys.BACKSPACE:\r\n codex.callback.backspacePressed(block);\r\n break;\r\n\r\n case codex.core.keys.UP:\r\n case codex.core.keys.LEFT:\r\n codex.callback.blockLeftOrUpArrowPressed(block);\r\n break;\r\n\r\n }\r\n };\r\n\r\n /**\r\n * RIGHT or DOWN keydowns on block\r\n */\r\n callbacks.blockRightOrDownArrowPressed = function (block) {\r\n\r\n var selection = window.getSelection(),\r\n inputs = codex.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n /** Check for caret existance */\r\n if (!focusedNode){\r\n return false;\r\n }\r\n\r\n /** Looking for closest (parent) contentEditable element of focused node */\r\n while (focusedNode.contentEditable != 'true') {\r\n\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n editableElementIndex ++;\r\n }\r\n\r\n /**\r\n * Founded contentEditable element doesn't have childs\r\n * Or maybe New created block\r\n */\r\n if (!focusedNode.textContent)\r\n {\r\n codex.caret.setToNextBlock(editableElementIndex);\r\n return;\r\n }\r\n\r\n /**\r\n * Do nothing when caret doesn not reaches the end of last child\r\n */\r\n var caretInLastChild = false,\r\n caretAtTheEndOfText = false;\r\n\r\n var lastChild,\r\n deepestTextnode;\r\n\r\n lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ];\r\n\r\n if (codex.core.isDomNode(lastChild)) {\r\n\r\n deepestTextnode = codex.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);\r\n\r\n } else {\r\n\r\n deepestTextnode = lastChild;\r\n\r\n }\r\n\r\n caretInLastChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;\r\n\r\n if ( !caretInLastChild || !caretAtTheEndOfText ) {\r\n codex.core.log('arrow [down|right] : caret does not reached the end');\r\n return false;\r\n }\r\n\r\n codex.caret.setToNextBlock(editableElementIndex);\r\n\r\n };\r\n\r\n /**\r\n * LEFT or UP keydowns on block\r\n */\r\n callbacks.blockLeftOrUpArrowPressed = function (block) {\r\n\r\n var selection = window.getSelection(),\r\n inputs = codex.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n /** Check for caret existance */\r\n if (!focusedNode){\r\n return false;\r\n }\r\n\r\n /**\r\n * LEFT or UP not at the beginning\r\n */\r\n if ( selection.anchorOffset !== 0) {\r\n return false;\r\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n editableElementIndex ++;\r\n }\r\n\r\n /**\r\n * Do nothing if caret is not at the beginning of first child\r\n */\r\n var caretInFirstChild = false,\r\n caretAtTheBeginning = false;\r\n\r\n var firstChild,\r\n deepestTextnode;\r\n\r\n /**\r\n * Founded contentEditable element doesn't have childs\r\n * Or maybe New created block\r\n */\r\n if (!focusedNode.textContent) {\r\n codex.caret.setToPreviousBlock(editableElementIndex);\r\n return;\r\n }\r\n\r\n firstChild = focusedNode.childNodes[0];\r\n\r\n if (codex.core.isDomNode(firstChild)) {\r\n\r\n deepestTextnode = codex.content.getDeepestTextNodeFromPosition(firstChild, 0);\r\n\r\n } else {\r\n\r\n deepestTextnode = firstChild;\r\n\r\n }\r\n\r\n caretInFirstChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheBeginning = selection.anchorOffset === 0;\r\n\r\n if ( caretInFirstChild && caretAtTheBeginning ) {\r\n\r\n codex.caret.setToPreviousBlock(editableElementIndex);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Callback for enter key pressing in first-level block area\r\n */\r\n callbacks.enterPressedOnBlock = function (event) {\r\n\r\n var NEW_BLOCK_TYPE = codex.settings.initialBlockPlugin;\r\n\r\n codex.content.insertBlock({\r\n type : NEW_BLOCK_TYPE,\r\n block : codex.tools[NEW_BLOCK_TYPE].render()\r\n }, true );\r\n\r\n codex.toolbar.move();\r\n codex.toolbar.open();\r\n\r\n };\r\n\r\n callbacks.backspacePressed = function (block) {\r\n\r\n var currentInputIndex = codex.caret.getCurrentInputIndex(),\r\n range,\r\n selectionLength,\r\n firstLevelBlocksCount;\r\n\r\n if (block.textContent.trim()) {\r\n\r\n range = codex.content.getRange();\r\n selectionLength = range.endOffset - range.startOffset;\r\n\r\n\r\n if (codex.caret.position.atStart() && !selectionLength && codex.state.inputs[currentInputIndex - 1]) {\r\n\r\n codex.content.mergeBlocks(currentInputIndex);\r\n\r\n } else {\r\n\r\n return;\r\n\r\n }\r\n }\r\n\r\n if (!selectionLength) {\r\n block.remove();\r\n }\r\n\r\n\r\n firstLevelBlocksCount = codex.nodes.redactor.childNodes.length;\r\n\r\n /**\r\n * If all blocks are removed\r\n */\r\n if (firstLevelBlocksCount === 0) {\r\n\r\n /** update currentNode variable */\r\n codex.content.currentNode = null;\r\n\r\n /** Inserting new empty initial block */\r\n codex.ui.addInitialBlock();\r\n\r\n /** Updating inputs state after deleting last block */\r\n codex.ui.saveInputs();\r\n\r\n /** Set to current appended block */\r\n setTimeout(function () {\r\n\r\n codex.caret.setToPreviousBlock(1);\r\n\r\n }, 10);\r\n\r\n } else {\r\n\r\n if (codex.caret.inputIndex !== 0) {\r\n\r\n /** Target block is not first */\r\n codex.caret.setToPreviousBlock(codex.caret.inputIndex);\r\n\r\n } else {\r\n\r\n /** If we try to delete first block */\r\n codex.caret.setToNextBlock(codex.caret.inputIndex);\r\n\r\n }\r\n }\r\n\r\n codex.toolbar.move();\r\n\r\n if (!codex.toolbar.opened) {\r\n codex.toolbar.open();\r\n }\r\n\r\n /** Updating inputs state */\r\n codex.ui.saveInputs();\r\n\r\n /** Prevent default browser behaviour */\r\n event.preventDefault();\r\n\r\n };\r\n\r\n /**\r\n * @deprecated\r\n *\r\n * @param event\r\n */\r\n callbacks.blockPaste = function(event) {\r\n\r\n var currentInputIndex = codex.caret.getCurrentInputIndex(),\r\n node = codex.state.inputs[currentInputIndex];\r\n\r\n setTimeout(function() {\r\n\r\n codex.content.sanitize(node);\r\n\r\n event.preventDefault();\r\n\r\n }, 10);\r\n\r\n event.stopImmediatePropagation();\r\n\r\n };\r\n\r\n /**\r\n * This method is used to observe pasted dirty data.\r\n *\r\n * Mutation handlers send to separate observers each mutation (added, changed and so on), which will be\r\n * passed from handler that sanitizes and replaces data.\r\n *\r\n * Probably won't be used\r\n *\r\n * @deprecated\r\n *\r\n * @param event\r\n * @private\r\n */\r\n callbacks._blockPasteCallback = function(event) {\r\n\r\n var currentInputIndex = codex.caret.getCurrentInputIndex();\r\n\r\n /**\r\n * create an observer instance\r\n */\r\n var observer = new MutationObserver(codex.callback.handleMutationsOnPaste);\r\n\r\n /**\r\n * configuration of the observer:\r\n */\r\n var config = {\r\n attributes: true,\r\n childList: false,\r\n characterData: false,\r\n subtree : true\r\n };\r\n\r\n // pass in the target node, as well as the observer options\r\n observer.observe(codex.state.inputs[currentInputIndex], config);\r\n };\r\n\r\n /**\r\n * This method prevents default behaviour.\r\n *\r\n * We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.\r\n * Firstly, we need to memorize the caret position. We can do that by getting the range of selection.\r\n * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node\r\n *\r\n * @param event\r\n */\r\n callbacks.blockPasteCallback = function(event) {\r\n\r\n /** Prevent default behaviour */\r\n event.preventDefault();\r\n\r\n /** get html pasted data - dirty data */\r\n var data = event.clipboardData.getData('text/html') || event.clipboardData.getData('text/plain');\r\n\r\n /** Temporary DIV that is used to work with childs as arrays item */\r\n var div = codex.draw.node('DIV', '', {}),\r\n cleaner = new codex.sanitizer.init(codex.sanitizer.Config.BASIC),\r\n cleanData,\r\n fragment;\r\n\r\n /** Create fragment, that we paste to range after proccesing */\r\n fragment = document.createDocumentFragment();\r\n\r\n cleanData = cleaner.clean(data);\r\n\r\n div.innerHTML = cleanData;\r\n\r\n var node, lastNode;\r\n\r\n /**\r\n * and fill in fragment\r\n */\r\n while (( node = div.firstChild) ) {\r\n lastNode = fragment.appendChild(node);\r\n }\r\n\r\n /**\r\n * work with selection and range\r\n */\r\n var selection, range;\r\n selection = window.getSelection();\r\n\r\n range = selection.getRangeAt(0);\r\n range.deleteContents();\r\n\r\n range.insertNode(fragment);\r\n // document.execCommand('insertParagraph', false, \" \");\r\n\r\n /** Preserve the selection */\r\n if (lastNode) {\r\n range = range.cloneRange();\r\n range.setStartAfter(lastNode);\r\n range.collapse(true);\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Sends all mutations to paste handler\r\n */\r\n callbacks.handleMutationsOnPaste = function(mutations) {\r\n\r\n var self = this;\r\n\r\n /**\r\n * Calling function with context of this function.\r\n * Also, we should sanitize pasted or changed data one time and ignore\r\n * changings which makes sanitize method.\r\n * For that, we need to send Context, MutationObserver.__proto__ that contains\r\n * observer disconnect method.\r\n */\r\n mutations.forEach(function(mutation) {\r\n codex.content.paste.call(self, mutation);\r\n });\r\n };\r\n\r\n /**\r\n * Clicks on block settings button\r\n */\r\n callbacks.showSettingsButtonClicked = function(){\r\n\r\n /**\r\n * Get type of current block\r\n * It uses to append settings from tool.settings property.\r\n * ...\r\n * Type is stored in data-type attribute on block\r\n */\r\n var currentToolType = codex.content.currentNode.dataset.tool;\r\n\r\n codex.toolbar.settings.toggle(currentToolType);\r\n\r\n /** Close toolbox when settings button is active */\r\n codex.toolbar.toolbox.close();\r\n codex.toolbar.settings.hideRemoveActions();\r\n\r\n };\r\n\r\n callbacks.showCommentButtonClicked = function() {\r\n\r\n var block = codex.content.currentNode;\r\n\r\n codex.comments.add(block);\r\n\r\n }\r\n\r\n return callbacks;\r\n\r\n})({});\r\n\r\nmodule.exports = callbacks;\r\n\n\n\n// WEBPACK FOOTER //\n// ./modules/callbacks.js","/**\r\n * Codex Editor Draw module\r\n *\r\n * @author Codex Team\r\n * @version 1.0.\r\n */\r\n\r\nvar draw = (function(draw) {\r\n\r\n /**\r\n * Base editor wrapper\r\n */\r\n draw.wrapper = function () {\r\n\r\n var wrapper = document.createElement('div');\r\n\r\n wrapper.className += 'codex-editor';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n /**\r\n * Content-editable holder\r\n */\r\n draw.redactor = function () {\r\n\r\n var redactor = document.createElement('div');\r\n\r\n redactor.className += 'ce-redactor';\r\n\r\n return redactor;\r\n\r\n };\r\n\r\n /**\r\n * Comments side bar\r\n */\r\n draw.commentsSidebar = function() {\r\n\r\n var sidebar = draw.node('DIV', 'ce-comments-sidebar');\r\n\r\n return sidebar;\r\n\r\n }\r\n\r\n draw.ceBlock = function() {\r\n\r\n var block = document.createElement('DIV');\r\n\r\n block.className += 'ce_block';\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * Empty toolbar with toggler\r\n */\r\n draw.toolbar = function () {\r\n\r\n var bar = document.createElement('div');\r\n\r\n bar.className += 'ce-toolbar';\r\n\r\n return bar;\r\n };\r\n\r\n draw.toolbarContent = function() {\r\n\r\n var wrapper = document.createElement('DIV');\r\n wrapper.classList.add('ce-toolbar__content');\r\n\r\n return wrapper;\r\n };\r\n\r\n /**\r\n * Inline toolbar\r\n */\r\n draw.inlineToolbar = function() {\r\n\r\n var bar = document.createElement('DIV');\r\n\r\n bar.className += 'ce-toolbar-inline';\r\n\r\n return bar;\r\n\r\n };\r\n\r\n /**\r\n * Wrapper for inline toobar buttons\r\n */\r\n draw.inlineToolbarButtons = function() {\r\n\r\n var wrapper = document.createElement('DIV');\r\n\r\n wrapper.className += 'ce-toolbar-inline__buttons';\r\n\r\n return wrapper;\r\n };\r\n\r\n /**\r\n * For some actions\r\n */\r\n draw.inlineToolbarActions = function() {\r\n\r\n var wrapper = document.createElement('DIV');\r\n\r\n wrapper.className += 'ce-toolbar-inline__actions';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n draw.inputForLink = function() {\r\n\r\n var input = document.createElement('INPUT');\r\n\r\n input.type = 'input';\r\n input.className += 'inputForLink';\r\n input.placeholder = 'Вставьте ссылку ...';\r\n input.setAttribute('form', 'defaultForm');\r\n\r\n input.setAttribute('autofocus', 'autofocus');\r\n\r\n return input;\r\n\r\n };\r\n\r\n /**\r\n * Block with notifications\r\n */\r\n draw.alertsHolder = function() {\r\n\r\n var block = document.createElement('div');\r\n\r\n block.classList.add('ce_notifications-block');\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * @todo Desc\r\n */\r\n draw.blockButtons = function() {\r\n\r\n var block = document.createElement('div');\r\n\r\n block.className += 'ce-toolbar__actions';\r\n\r\n return block;\r\n };\r\n\r\n /**\r\n * Block settings panel\r\n */\r\n draw.blockSettings = function () {\r\n\r\n var settings = document.createElement('div');\r\n\r\n settings.className += 'ce-settings';\r\n\r\n return settings;\r\n };\r\n\r\n draw.defaultSettings = function() {\r\n\r\n var div = document.createElement('div');\r\n\r\n div.classList.add('ce-settings_default');\r\n\r\n return div;\r\n };\r\n\r\n draw.pluginsSettings = function() {\r\n\r\n var div = document.createElement('div');\r\n\r\n div.classList.add('ce-settings_plugin');\r\n\r\n return div;\r\n\r\n };\r\n\r\n draw.plusButton = function() {\r\n\r\n var button = document.createElement('span');\r\n\r\n button.className = 'ce-toolbar__plus';\r\n // button.innerHTML = '';\r\n\r\n return button;\r\n };\r\n\r\n /**\r\n * Settings button in toolbar\r\n */\r\n draw.settingsButton = function () {\r\n\r\n var toggler = document.createElement('span');\r\n\r\n toggler.className = 'ce-toolbar__settings-btn';\r\n\r\n /** Toggler button*/\r\n toggler.innerHTML = '';\r\n\r\n return toggler;\r\n };\r\n\r\n /**\r\n * Comment button in toolbar\r\n */\r\n draw.commentButton = function () {\r\n\r\n var toggler = document.createElement('span');\r\n\r\n toggler.className = 'ce-toolbar__comment-btn';\r\n\r\n /** Toggler button*/\r\n toggler.innerHTML = '';\r\n\r\n return toggler;\r\n };\r\n\r\n /**\r\n * Redactor tools wrapper\r\n */\r\n\r\n draw.toolbox = function() {\r\n\r\n var wrapper = document.createElement('div');\r\n\r\n wrapper.className = 'ce-toolbar__tools';\r\n\r\n return wrapper;\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Draws tool buttons for toolbox\r\n *\r\n * @param {String} type\r\n * @param {String} classname\r\n * @returns {Element}\r\n */\r\n draw.toolbarButton = function (type, classname) {\r\n\r\n var button = document.createElement(\"li\"),\r\n tool_icon = document.createElement(\"i\"),\r\n tool_title = document.createElement(\"span\");\r\n\r\n button.dataset.type = type;\r\n button.setAttribute('title', type);\r\n\r\n tool_icon.classList.add(classname);\r\n tool_title.classList.add('ce_toolbar_tools--title');\r\n\r\n\r\n button.appendChild(tool_icon);\r\n button.appendChild(tool_title);\r\n\r\n return button;\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Draws tools for inline toolbar\r\n *\r\n * @param {String} type\r\n * @param {String} classname\r\n */\r\n draw.toolbarButtonInline = function(type, classname) {\r\n var button = document.createElement(\"BUTTON\"),\r\n tool_icon = document.createElement(\"I\");\r\n\r\n button.type = \"button\";\r\n button.dataset.type = type;\r\n tool_icon.classList.add(classname);\r\n\r\n button.appendChild(tool_icon);\r\n\r\n return button;\r\n };\r\n\r\n /**\r\n * Redactor block\r\n */\r\n draw.block = function (tagName, content) {\r\n\r\n var node = document.createElement(tagName);\r\n\r\n node.innerHTML = content || '';\r\n\r\n return node;\r\n\r\n };\r\n\r\n /**\r\n * Creates Node with passed tagName and className\r\n * @param {string} tagName\r\n * @param {string} className\r\n * @param {object} properties - allow to assign properties\r\n */\r\n draw.node = function( tagName , className , properties ){\r\n\r\n var el = document.createElement( tagName );\r\n\r\n if ( className ) el.className = className;\r\n\r\n if ( properties ) {\r\n\r\n for (var name in properties){\r\n el[name] = properties[name];\r\n }\r\n }\r\n\r\n return el;\r\n };\r\n\r\n draw.pluginsRender = function(type, content) {\r\n\r\n return {\r\n type : type,\r\n block : cEditor.tools[type].render({\r\n text : content\r\n })\r\n };\r\n };\r\n\r\n return draw;\r\n\r\n})({});\r\n\r\nmodule.exports = draw;\r\n\r\n\r\n\n\n\n// WEBPACK FOOTER //\n// ./modules/draw.js","/**\r\n * Codex Editor Caret Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (caret) {\r\n\r\n /**\r\n * @var {int} InputIndex - editable element in DOM\r\n */\r\n caret.inputIndex = null;\r\n\r\n /**\r\n * @var {int} offset - caret position in a text node.\r\n */\r\n caret.offset = null;\r\n\r\n /**\r\n * @var {int} focusedNodeIndex - we get index of child node from first-level block\r\n */\r\n caret.focusedNodeIndex = null;\r\n\r\n /**\r\n * Creates Document Range and sets caret to the element.\r\n * @protected\r\n * @uses caret.save — if you need to save caret position\r\n * @param {Element} el - Changed Node.\r\n */\r\n caret.set = function ( el, index, offset) {\r\n\r\n offset = offset || caret.offset || 0;\r\n index = index || caret.focusedNodeIndex || 0;\r\n\r\n var childs = el.childNodes,\r\n nodeToSet;\r\n\r\n if ( childs.length === 0 ) {\r\n\r\n nodeToSet = el;\r\n\r\n } else {\r\n\r\n nodeToSet = childs[index];\r\n\r\n }\r\n\r\n /** If Element is INPUT */\r\n if (el.tagName == 'INPUT') {\r\n\r\n el.focus();\r\n return;\r\n\r\n }\r\n\r\n if (editor.core.isDomNode(nodeToSet)) {\r\n\r\n nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);\r\n\r\n }\r\n\r\n var range = document.createRange(),\r\n selection = window.getSelection();\r\n\r\n window.setTimeout(function () {\r\n\r\n range.setStart(nodeToSet, offset);\r\n range.setEnd(nodeToSet, offset);\r\n\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n\r\n editor.caret.saveCurrentInputIndex();\r\n\r\n }, 20);\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n * Updates index of input and saves it in caret object\r\n */\r\n caret.saveCurrentInputIndex = function () {\r\n\r\n /** Index of Input that we paste sanitized content */\r\n var selection = window.getSelection(),\r\n inputs = editor.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n if (!focusedNode) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\r\n\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n\r\n editableElementIndex ++;\r\n\r\n }\r\n\r\n caret.inputIndex = editableElementIndex;\r\n\r\n };\r\n\r\n /**\r\n * Returns current input index (caret object)\r\n */\r\n caret.getCurrentInputIndex = function () {\r\n\r\n return caret.inputIndex;\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of first-level block after that we set caret into next input\r\n */\r\n caret.setToNextBlock = function (index) {\r\n\r\n var inputs = editor.state.inputs,\r\n nextInput = inputs[index + 1];\r\n\r\n if (!nextInput) {\r\n\r\n editor.core.log('We are reached the end');\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!nextInput.childNodes.length) {\r\n\r\n var emptyTextElement = document.createTextNode('');\r\n\r\n nextInput.appendChild(emptyTextElement);\r\n\r\n }\r\n\r\n editor.caret.inputIndex = index + 1;\r\n editor.caret.set(nextInput, 0, 0);\r\n editor.content.workingNodeChanged(nextInput);\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of target input.\r\n * Sets caret to input with this index\r\n */\r\n caret.setToBlock = function (index) {\r\n\r\n var inputs = editor.state.inputs,\r\n targetInput = inputs[index];\r\n\r\n if ( !targetInput ) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!targetInput.childNodes.length) {\r\n\r\n var emptyTextElement = document.createTextNode('');\r\n\r\n targetInput.appendChild(emptyTextElement);\r\n\r\n }\r\n\r\n editor.caret.inputIndex = index;\r\n editor.caret.set(targetInput, 0, 0);\r\n editor.content.workingNodeChanged(targetInput);\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of input\r\n */\r\n caret.setToPreviousBlock = function (index) {\r\n\r\n index = index || 0;\r\n\r\n var inputs = editor.state.inputs,\r\n previousInput = inputs[index - 1],\r\n lastChildNode,\r\n lengthOfLastChildNode,\r\n emptyTextElement;\r\n\r\n\r\n if (!previousInput) {\r\n\r\n editor.core.log('We are reached first node');\r\n return;\r\n\r\n }\r\n\r\n lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length);\r\n lengthOfLastChildNode = lastChildNode.length;\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!previousInput.childNodes.length) {\r\n\r\n emptyTextElement = document.createTextNode('');\r\n previousInput.appendChild(emptyTextElement);\r\n\r\n }\r\n editor.caret.inputIndex = index - 1;\r\n editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode);\r\n editor.content.workingNodeChanged(inputs[index - 1]);\r\n\r\n };\r\n\r\n caret.position = {\r\n\r\n atStart : function () {\r\n\r\n var selection = window.getSelection(),\r\n anchorOffset = selection.anchorOffset,\r\n anchorNode = selection.anchorNode,\r\n firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode),\r\n pluginsRender = firstLevelBlock.childNodes[0];\r\n\r\n if (!editor.core.isDomNode(anchorNode)) {\r\n\r\n anchorNode = anchorNode.parentNode;\r\n\r\n }\r\n\r\n var isFirstNode = anchorNode === pluginsRender.childNodes[0],\r\n isOffsetZero = anchorOffset === 0;\r\n\r\n return isFirstNode && isOffsetZero;\r\n\r\n },\r\n\r\n atTheEnd : function () {\r\n\r\n var selection = window.getSelection(),\r\n anchorOffset = selection.anchorOffset,\r\n anchorNode = selection.anchorNode;\r\n\r\n /** Caret is at the end of input */\r\n return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;\r\n\r\n }\r\n };\r\n\r\n return caret;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/caret.js","/**\r\n * Codex Editor Notification Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (notifications) {\r\n\r\n /**\r\n * Error notificator. Shows block with message\r\n * @protected\r\n */\r\n notifications.errorThrown = function (errorMsg, event) {\r\n\r\n editor.notifications.send('This action is not available currently', event.type, false);\r\n\r\n };\r\n\r\n /**\r\n * Appends notification with different types\r\n * @param message {string} - Error or alert message\r\n * @param type {string} - Type of message notification. Ex: Error, Warning, Danger ...\r\n * @param append {boolean} - can be True or False when notification should be inserted after\r\n */\r\n notifications.send = function (message, type, append) {\r\n\r\n var notification = editor.draw.block('div');\r\n\r\n notification.textContent = message;\r\n notification.classList.add('ce_notification-item', 'ce_notification-' + type, 'flipInX');\r\n\r\n if (!append) {\r\n\r\n editor.nodes.notifications.innerHTML = '';\r\n\r\n }\r\n\r\n editor.nodes.notifications.appendChild(notification);\r\n\r\n window.setTimeout(function () {\r\n\r\n notification.remove();\r\n\r\n }, 3000);\r\n\r\n };\r\n\r\n return notifications;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/notifications.js","/**\r\n * Codex Editor Parser Module\r\n *\r\n * @author Codex Team\r\n * @version 1.1\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (parser) {\r\n\r\n /** inserting text */\r\n parser.insertPastedContent = function (blockType, tag) {\r\n\r\n editor.content.insertBlock({\r\n type : blockType.type,\r\n block : blockType.render({\r\n text : tag.innerHTML\r\n })\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Check DOM node for display style: separated block or child-view\r\n */\r\n parser.isFirstLevelBlock = function (node) {\r\n\r\n return node.nodeType == editor.core.nodeTypes.TAG &&\r\n node.classList.contains(editor.ui.className.BLOCK_CLASSNAME);\r\n\r\n };\r\n\r\n return parser;\r\n\r\n})({});\r\n\n\n\n// WEBPACK FOOTER //\n// ./modules/parser.js","/**\r\n * Codex Sanitizer\r\n */\r\n\r\nvar janitor = require('html-janitor');\r\n\r\nmodule.exports = (function (sanitizer) {\r\n\r\n /**\r\n * Basic config\r\n */\r\n var Config = {\r\n\r\n BASIC : {\r\n\r\n tags: {\r\n p: {},\r\n a: {\r\n href: true,\r\n target: '_blank',\r\n rel: 'nofollow'\r\n },\r\n i: {},\r\n b: {},\r\n strong: {},\r\n em: {},\r\n span: {}\r\n }\r\n }\r\n };\r\n\r\n sanitizer.Config = Config;\r\n\r\n sanitizer.init = janitor;\r\n\r\n return sanitizer;\r\n\r\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 = 19\n// module chunks = 0","var comments = function(comments) {\r\n\r\n var draw = {\r\n\r\n commentsField: function(blockId) {\r\n\r\n var field = codex.draw.node('DIV', 'ce-comments-field');\r\n\r\n field.dataset.blockId = blockId;\r\n\r\n return field;\r\n\r\n },\r\n\r\n input: function(text) {\r\n\r\n var wrapper = codex.draw.node('DIV', 'ce-comment'),\r\n input = codex.draw.node('DIV', 'ce-comment__input', {'contentEditable': 'true', 'textContent':text||''}),\r\n deleteBtn = codex.draw.node('DIV', 'ce-comment__delete', {'textContent': 'Delete'}),\r\n postBtn = codex.draw.node('DIV', 'ce-comment__post', {'textContent': text?'Save':'Comment'});\r\n\r\n postBtn.addEventListener('click', callbacks.commentClicked);\r\n deleteBtn.addEventListener('click', callbacks.deleteClicked);\r\n\r\n wrapper.appendChild(input);\r\n wrapper.appendChild(postBtn);\r\n wrapper.appendChild(deleteBtn);\r\n\r\n wrapper.dataset.edited = text?'edited ':'';\r\n\r\n return wrapper;\r\n\r\n },\r\n\r\n comment: function(data) {\r\n\r\n if (!data.text) return;\r\n\r\n var wrapper = codex.draw.node('DIV', 'ce-comment'),\r\n text = codex.draw.node('DIV', 'ce-comment__text', {'textContent': data.text}),\r\n date = new Date().toLocaleDateString('en-US',{\r\n month: 'short',\r\n day: 'numeric',\r\n hour: 'numeric',\r\n minute: 'numeric',\r\n hour12: false\r\n }),\r\n time = codex.draw.node('DIV', 'ce-comment__time', {'textContent': date}),\r\n deleteBtn = codex.draw.node('DIV', 'ce-comment__delete', {'textContent': 'Delete'}),\r\n editBtn = codex.draw.node('DIV', 'ce-comment__edit', {'textContent': 'Edit'});\r\n\r\n editBtn.addEventListener('click', callbacks.editClicked);\r\n deleteBtn.addEventListener('click', callbacks.deleteClicked);\r\n\r\n wrapper.dataset.edited = data.edited;\r\n time.dataset.edited = data.edited;\r\n\r\n wrapper.appendChild(text);\r\n wrapper.appendChild(time);\r\n wrapper.appendChild(editBtn);\r\n wrapper.appendChild(deleteBtn);\r\n\r\n return wrapper;\r\n\r\n }\r\n\r\n };\r\n\r\n var callbacks = {\r\n\r\n commentClicked: function(e) {\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1],\r\n input = wrapper.querySelector('.ce-comment__input');\r\n\r\n if (input.textContent.trim() == '') return;\r\n\r\n var comment = draw.comment({\r\n text: input.textContent,\r\n edited: wrapper.dataset.edited\r\n });\r\n\r\n field.replaceChild(comment, wrapper);\r\n\r\n },\r\n\r\n editClicked: function(e) {\r\n\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1],\r\n text = wrapper.querySelector('.ce-comment__text');\r\n\r\n var comment = draw.input(text.textContent);\r\n\r\n field.replaceChild(comment, wrapper);\r\n\r\n },\r\n\r\n deleteClicked: function(e) {\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1];\r\n\r\n field.removeChild(wrapper);\r\n\r\n }\r\n\r\n };\r\n\r\n var methods = {\r\n\r\n getCoords: function(block) {\r\n\r\n var rect = block.getBoundingClientRect();\r\n\r\n return {\r\n x: pageXOffset + rect.left,\r\n y: pageYOffset + rect.top\r\n }\r\n\r\n }\r\n\r\n }\r\n\r\n comments.add = function(block) {\r\n\r\n var blockId = block.dataset.id;\r\n\r\n var field = codex.nodes.commentsSidebar.querySelector('.ce-comments-field[data-block-id=\"'+blockId+'\"]') ||\r\n draw.commentsField(blockId);\r\n\r\n var comment = draw.input();\r\n\r\n comment.querySelector('.ce-comment__input').focus();\r\n\r\n field.appendChild(comment);\r\n\r\n var blockCoords = methods.getCoords(block);\r\n\r\n field.style.top = blockCoords.y + 'px';\r\n\r\n codex.nodes.commentsSidebar.appendChild(field);\r\n\r\n };\r\n\r\n\r\n return comments;\r\n\r\n}({});\r\n\r\nmodule.exports = comments;\n\n\n// WEBPACK FOOTER //\n// ./modules/comments.js"],"sourceRoot":""}
\ No newline at end of file
+{"version":3,"sources":["webpack:///codex-editor.js","webpack:///webpack/bootstrap c48085d2245608162342","webpack:///./codex.js","webpack:///./modules/core.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/comments.js"],"names":["codex","modules","__webpack_require__","moduleId","installedModules","exports","module","id","loaded","call","m","c","p","editor","version","init","core","ui","transport","renderer","saver","content","toolbar","callback","draw","caret","notifications","parser","sanitizer","comments","settings","tools","textareaId","uploadImagesUrl","initialBlockPlugin","nodes","textarea","wrapper","inlineToolbar","buttons","actions","toolbox","plusButton","showSettingsButton","showTrashButton","showCommentButton","blockSettings","pluginSettings","defaultSettings","toolbarButtons","redactor","commentsSidebar","state","jsonOutput","blocks","inputs","start","userSettings","prepare","then","make","addTools","bindEvents","preparePlugins","makeBlocksFromData","saveInputs","catch","error","log","_typeof","Symbol","iterator","obj","constructor","prototype","Promise","resolve","reject","data","document","getElementById","undefined","Error","msg","type","arg","window","console","e","insertAfter","target","element","parentNode","insertBefore","nextSibling","nodeTypes","TAG","TEXT","COMMENT","keys","BACKSPACE","TAB","ENTER","SHIFT","CTRL","ALT","ESC","SPACE","LEFT","UP","DOWN","RIGHT","DELETE","META","isDomNode","el","nodeType","this","ajax","url","XMLHTTP","XMLHttpRequest","ActiveXObject","successFunction","params","async","success","test","encodeURIComponent","withCredentials","beforeSend","open","setRequestHeader","onreadystatechange","readyState","status","responseText","send","importScript","scriptPath","instanceName","instancePrefix","script","createElement","defer","onload","onerror","src","head","appendChild","className","BLOCK_CLASSNAME","BLOCK_CONTENT","BLOCK_STRETCHED","BLOCK_HIGHLIGHTED","BLOCK_IN_FEED_MODE","SETTINGS_ITEM","toolbarContent","blockButtons","alertsHolder","body","settingsButton","commentButton","makeRemoveBlockButton","pluginsSettings","makeInlineToolbar","addDefaultSettings","container","inlineToolbarButtons","inlineToolbarActions","tool","toolName","toolButton","iconClassname","render","displayInToolbox","toolbarButton","addInlineToolbarTools","bold","icon","command","italic","underline","link","name","toolbarButtonInline","setInlineToolbarButtonBehaviour","addEventListener","globalKeydown","redactorKeyDown","globalKeyup","redactorClicked","plusButtonClicked","showSettingsButtonClicked","showCommentButtonClicked","button","toolbarButtonClicked","pluginName","plugin","config","addBlockHandlers","block","blockKeydown","blockPasteCallback","inline","show","querySelectorAll","addInitialBlock","initialBlock","initialBlockType","setAttribute","insertBlock","workingNodeChanged","event","toolClicked","input","arguments","fileSelected","clearInput","files","formdData","FormData","append","selectAndUpload","args","click","xhr","items","length","appendBlocks","nodeSequence","index","appendNodeAtIndex","getNodeAsync","createBlockFromData","blockData","blocksList","cover","stretched","isStretched","saveBlocks","html","innerHTML","childNodes","makeQueue","queue","getBlockData","makeFormDataFromBlocks","dataset","save","output","blockContent","pluginsContent","savedData","validate","result","classList","contains","push","saveComments","fields","i","commentsData","j","text","querySelector","time","comment","edited","textContent","fieldData","blockId","currentNode","editorAreaHightlighted","sync","getNodeFocused","focused","selection","getSelection","anchorNode","focusNode","parentElement","isFirstLevelBlock","parent","markBlock","add","clearMark","remove","getFirstLevelBlock","node","targetNode","replaceBlock","targetBlock","newBlock","replaceChild","needPlaceCaret","workingBlock","newBlockContent","blockType","composeNewBlock","currentInputIndex","getCurrentInputIndex","editableElement","emptyText","createTextNode","set","move","showPlusButton","setTimeout","setToNextBlock","switchBlock","blockToReplace","newBlockComposed","getDeepestTextNodeFromPosition","position","blockChilds","trim","removeChild","lookingFromStart","Date","getRange","getRangeAt","splitBlock","inputIndex","textBeforeCaret","textNodeBeforeCaret","textAfterCaret","textNodeAfterCaret","anchorNodeText","caretOffset","anchorOffset","currentBlock","substring","previousChilds","nextChilds","reachedCurrent","child","previousChildsLength","nextChildsLength","newNode","NEW_BLOCK_TYPE","mergeBlocks","targetInputIndex","targetInput","currentInputContent","paste","mutation","workingNode","allowedToPaste","sanitize","pasteTextContent","addedNodes","textNode","disconnect","cleaner","satinizer","Config","BASIC","clean","outerHTML","div","replaceWith","isLastNode","allChecked","allSiblingsEmpty_","sibling","wrapTextWithParagraphs","htmlString","paragraph","blockTyped","newWrapper","firstLevelBlocks","indexOf","tagName","cloneNode","defaultToolbarHeight","defaultOffset","opened","current","close","toggle","hidePlusButton","newYCoordinate","offsetTop","style","transform","Math","floor","hideRemoveActions","setting","toolType","makeSettings","settingsBlock","feedModeToggler","makeFeedModeToggler","isFeedModeActivated","updateFeedMode","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","forEach","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","value","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","leaf","currentTool","Object","barButtons","nextToolIndex","toolToSelect","visibleTool","appendCallback","UNREPLACEBLE_TOOLS","setToBlock","callbacks","enterKeyPressed","tabKeyPressed","enterKeyPressedOnRedactorZone","escapeKeyPressed","defaultKeyPressed","arrowKeyPressed","enterPressedOnBlock","contentEditable","saveCurrentInputIndex","isEnterPressedOnToolbar","enableLineBreaks","stopPropagation","shiftKey","currentSelection","currentSelectedNode","caretAtTheEndOfText","atTheEnd","isTextNodeHasParentBetweenContenteditable","islastNode","detectWhenClickedOnFirstLevelBlockArea","firstLevelBlock","indexOfLastInput","inputIsEmpty","currentNodeType","isInitialType","flag","blockRightOrDownArrowPressed","backspacePressed","blockLeftOrUpArrowPressed","focusedNodeHolder","focusedNode","editableElementIndex","lastChild","deepestTextnode","caretInLastChild","firstChild","caretInFirstChild","caretAtTheBeginning","setToPreviousBlock","selectionLength","endOffset","atStart","blockPaste","_blockPasteCallback","observer","MutationObserver","handleMutationsOnPaste","attributes","childList","characterData","subtree","observe","cleanData","fragment","clipboardData","getData","createDocumentFragment","lastNode","deleteContents","insertNode","setStartAfter","mutations","self","currentToolType","sidebar","ceBlock","bar","placeholder","toggler","classname","toolIcon","toolTitle","properties","focusedNodeIndex","nodeToSet","childs","nextInput","emptyTextElement","lastChildNode","lengthOfLastChildNode","previousInput","pluginsRender","isFirstNode","isOffsetZero","errorThrown","errorMsg","message","notification","insertPastedContent","janitor","tags","a","href","rel","b","strong","em","span","__WEBPACK_AMD_DEFINE_FACTORY__","__WEBPACK_AMD_DEFINE_RESULT__","root","factory","HTMLJanitor","tagDefinitions","validConfigValues","map","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","Array","some","isNotTopContainer","isNestedBlockElement","isInvalid","keepNestedBlockElements","removeAttribute","previousElementSibling","nextElementSibling","commentsField","field","deleteBtn","postBtn","commentClicked","deleteClicked","date","toLocaleDateString","month","day","hour","minute","hour12","editBtn","editClicked","path","methods","getCoords","getBoundingClientRect","pageXOffset","pageYOffset","blockCoords"],"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/**\r\n\t *\r\n\t * Codex Editor\r\n\t *\r\n\t * @author Codex Team\r\n\t */\n\t\n\tmodule.exports = function (editor) {\n\t\n\t 'use strict';\n\t\n\t editor.version = (\"1.4.0\");\n\t\n\t var init = function init() {\n\t\n\t editor.core = __webpack_require__(1);\n\t editor.ui = __webpack_require__(2);\n\t editor.transport = __webpack_require__(3);\n\t editor.renderer = __webpack_require__(4);\n\t editor.saver = __webpack_require__(5);\n\t editor.content = __webpack_require__(6);\n\t editor.toolbar = __webpack_require__(7);\n\t editor.callback = __webpack_require__(11);\n\t editor.draw = __webpack_require__(12);\n\t editor.caret = __webpack_require__(13);\n\t editor.notifications = __webpack_require__(14);\n\t editor.parser = __webpack_require__(15);\n\t editor.sanitizer = __webpack_require__(16);\n\t editor.comments = __webpack_require__(18);\n\t };\n\t\n\t /**\r\n\t * @public\r\n\t *\r\n\t * holds initial settings\r\n\t */\n\t editor.settings = {\n\t tools: ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'],\n\t textareaId: 'codex-editor',\n\t uploadImagesUrl: '/editor/transport/',\n\t\n\t // Type of block showing on empty editor\n\t initialBlockPlugin: 'paragraph'\n\t };\n\t\n\t /**\r\n\t * public\r\n\t *\r\n\t * Static nodes\r\n\t */\n\t editor.nodes = {\n\t textarea: 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 showCommentButton: null,\n\t blockSettings: null,\n\t pluginSettings: null,\n\t defaultSettings: null,\n\t toolbarButtons: {}, // { type : DomEl, ... }\n\t redactor: null,\n\t commentsSidebar: null\n\t };\n\t\n\t /**\r\n\t * @public\r\n\t *\r\n\t * Output state\r\n\t */\n\t editor.state = {\n\t jsonOutput: [],\n\t blocks: [],\n\t inputs: [],\n\t comments: []\n\t };\n\t\n\t /**\r\n\t * @public\r\n\t * Editor plugins\r\n\t */\n\t editor.tools = {};\n\t\n\t /**\r\n\t * Initialization\r\n\t * @uses Promise cEditor.core.prepare\r\n\t * @param {} userSettings are :\r\n\t * - tools [],\r\n\t * - textareaId String\r\n\t * ...\r\n\t *\r\n\t * Load user defined tools\r\n\t * Tools must contain this important objects :\r\n\t * @param {String} type - this is a type of plugin. It can be used as plugin name\r\n\t * @param {String} iconClassname - this a icon in toolbar\r\n\t * @param {Object} make - what should plugin do, when it is clicked\r\n\t * @param {Object} appendCallback - callback after clicking\r\n\t * @param {Element} settings - what settings does it have\r\n\t * @param {Object} render - plugin get JSON, and should return HTML\r\n\t * @param {Object} save - plugin gets HTML content, returns JSON\r\n\t * @param {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE\r\n\t * @param {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE\r\n\t *\r\n\t * @example\r\n\t * - type : 'header',\r\n\t * - iconClassname : 'ce-icon-header',\r\n\t * - make : headerTool.make,\r\n\t * - appendCallback : headerTool.appendCallback,\r\n\t * - settings : headerTool.makeSettings(),\r\n\t * - render : headerTool.render,\r\n\t * - save : headerTool.save,\r\n\t * - displayInToolbox : true,\r\n\t * - enableLineBreaks : false\r\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.make).then(editor.ui.addTools).then(editor.ui.bindEvents).then(editor.ui.preparePlugins).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/**\r\n\t * Codex Editor Core\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.1.2\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (core) {\n\t\n\t /**\r\n\t * @public\r\n\t *\r\n\t * Editor preparing method\r\n\t * @return Promise\r\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.uploadImagesUrl) {\n\t\n\t editor.settings.uploadImagesUrl = userSettings.uploadImagesUrl;\n\t }\n\t\n\t editor.nodes.textarea = document.getElementById(userSettings.textareaId || editor.settings.textareaId);\n\t\n\t if (_typeof(editor.nodes.textarea) === undefined || editor.nodes.textarea === null) {\n\t\n\t reject(Error(\"Textarea wasn't found by ID: #\" + userSettings.textareaId));\n\t } else {\n\t\n\t resolve();\n\t }\n\t });\n\t };\n\t\n\t /**\r\n\t * Logging method\r\n\t * @param type = ['log', 'info', 'warn']\r\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 /**\r\n\t * @protected\r\n\t *\r\n\t * Helper for insert one element after another\r\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 /**\r\n\t * @const\r\n\t *\r\n\t * Readable DOM-node types map\r\n\t */\n\t core.nodeTypes = {\n\t TAG: 1,\n\t TEXT: 3,\n\t COMMENT: 8\n\t };\n\t\n\t /**\r\n\t * @const\r\n\t * Readable keys map\r\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 /**\r\n\t * @protected\r\n\t *\r\n\t * Check object for DOM node\r\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 /**\r\n\t * Native Ajax\r\n\t */\n\t core.ajax = function (data) {\n\t\n\t if (!data || !data.url) {\n\t\n\t return;\n\t }\n\t\n\t var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),\n\t successFunction = function successFunction() {},\n\t params = '',\n\t obj;\n\t\n\t data.async = true;\n\t data.type = data.type || 'GET';\n\t data.data = data.data || '';\n\t data['content-type'] = data['content-type'] || 'application/json; charset=utf-8';\n\t successFunction = data.success || successFunction;\n\t\n\t if (data.type == 'GET' && data.data) {\n\t\n\t data.url = /\\?/.test(data.url) ? data.url + '&' + data.data : data.url + '?' + data.data;\n\t } else {\n\t\n\t for (obj in data.data) {\n\t\n\t params += obj + '=' + encodeURIComponent(data.data[obj]) + '&';\n\t }\n\t }\n\t\n\t if (data.withCredentials) {\n\t\n\t XMLHTTP.withCredentials = true;\n\t }\n\t\n\t if (data.beforeSend && typeof data.beforeSend == 'function') {\n\t\n\t data.beforeSend.call();\n\t }\n\t\n\t XMLHTTP.open(data.type, data.url, data.async);\n\t XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\n\t\n\t XMLHTTP.onreadystatechange = function () {\n\t\n\t if (XMLHTTP.readyState == 4 && XMLHTTP.status == 200) {\n\t\n\t successFunction(XMLHTTP.responseText);\n\t }\n\t };\n\t\n\t XMLHTTP.send(params);\n\t };\n\t\n\t /**\r\n\t * Appends script to head of document\r\n\t * @return Promise\r\n\t */\n\t core.importScript = function (scriptPath, instanceName) {\n\t\n\t return new Promise(function (resolve, reject) {\n\t\n\t var instancePrefix = 'cdx-script-';\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(instancePrefix + 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 = instancePrefix + 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 return core;\n\t}({});\n\n/***/ },\n/* 2 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor UI module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.1\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (ui) {\n\t\n\t /**\r\n\t * Basic editor classnames\r\n\t */\n\t ui.className = {\n\t\n\t /**\r\n\t * @const {string} BLOCK_CLASSNAME - redactor blocks name\r\n\t */\n\t BLOCK_CLASSNAME: 'ce-block',\n\t\n\t /**\r\n\t * @const {String} wrapper for plugins content\r\n\t */\n\t BLOCK_CONTENT: 'ce-block__content',\n\t\n\t /**\r\n\t * @const {String} BLOCK_STRETCHED - makes block stretched\r\n\t */\n\t BLOCK_STRETCHED: 'ce-block--stretched',\n\t\n\t /**\r\n\t * @const {String} BLOCK_HIGHLIGHTED - adds background\r\n\t */\n\t BLOCK_HIGHLIGHTED: 'ce-block--focused',\n\t\n\t /**\r\n\t * @const {String} - highlights covered blocks\r\n\t */\n\t BLOCK_IN_FEED_MODE: 'ce-block--feed-mode',\n\t\n\t /**\r\n\t * @const {String} - for all default settings\r\n\t */\n\t SETTINGS_ITEM: 'ce-settings__item'\n\t\n\t };\n\t\n\t /**\r\n\t * @protected\r\n\t *\r\n\t * Making main interface\r\n\t */\n\t ui.make = function () {\n\t\n\t var wrapper, toolbar, toolbarContent, redactor, commentsSidebar, notifications, blockButtons, blockSettings, showSettingsButton, showTrashButton, showCommentButton, toolbox, plusButton;\n\t\n\t /** Make editor wrapper */\n\t wrapper = editor.draw.wrapper();\n\t\n\t /** Append editor wrapper after initial textarea */\n\t editor.core.insertAfter(editor.nodes.textarea, wrapper);\n\t\n\t /** Append block with notifications to the document */\n\t notifications = editor.draw.alertsHolder();\n\t editor.nodes.notifications = document.body.appendChild(notifications);\n\t\n\t /** Make toolbar and content-editable redactor */\n\t toolbar = editor.draw.toolbar();\n\t toolbarContent = editor.draw.toolbarContent();\n\t plusButton = editor.draw.plusButton();\n\t showSettingsButton = editor.draw.settingsButton();\n\t showCommentButton = editor.draw.commentButton();\n\t showTrashButton = editor.toolbar.settings.makeRemoveBlockButton();\n\t blockSettings = editor.draw.blockSettings();\n\t blockButtons = editor.draw.blockButtons();\n\t toolbox = editor.draw.toolbox();\n\t redactor = editor.draw.redactor();\n\t commentsSidebar = editor.draw.commentsSidebar();\n\t\n\t /** settings */\n\t var defaultSettings = editor.draw.defaultSettings(),\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 /** Make blocks buttons\r\n\t * This block contains settings button and remove block button\r\n\t */\n\t blockButtons.appendChild(showSettingsButton);\n\t blockButtons.appendChild(showTrashButton);\n\t blockButtons.appendChild(showCommentButton);\n\t blockButtons.appendChild(blockSettings);\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 /** 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 wrapper.appendChild(toolbar);\n\t\n\t wrapper.appendChild(commentsSidebar);\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.toolbar = toolbar;\n\t editor.nodes.plusButton = plusButton;\n\t editor.nodes.toolbox = toolbox;\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 editor.nodes.showCommentButton = showCommentButton;\n\t editor.nodes.commentsSidebar = commentsSidebar;\n\t\n\t editor.nodes.redactor = redactor;\n\t\n\t /** Make container for inline toolbar */\n\t editor.ui.makeInlineToolbar();\n\t\n\t /** fill in default settings */\n\t editor.toolbar.settings.addDefaultSettings();\n\t };\n\t\n\t ui.makeInlineToolbar = function () {\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 /**\r\n\t * @private\r\n\t * Append tools passed in editor.tools\r\n\t */\n\t ui.addTools = function () {\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) {\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 /**\r\n\t * Add inline toolbar tools\r\n\t */\n\t editor.ui.addInlineToolbarTools();\n\t };\n\t\n\t ui.addInlineToolbarTools = function () {\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 underline: {\n\t icon: 'ce-icon-underline',\n\t command: 'underline'\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 /**\r\n\t * Add callbacks to this buttons\r\n\t */\n\t editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command);\n\t }\n\t };\n\t\n\t /**\r\n\t * @private\r\n\t * Bind editor UI events\r\n\t */\n\t ui.bindEvents = function () {\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 document.addEventListener('keydown', editor.callback.globalKeydown, false);\n\t\n\t /** All keydowns on Redactor zone */\n\t editor.nodes.redactor.addEventListener('keydown', editor.callback.redactorKeyDown, false);\n\t\n\t /** All keydowns on Document */\n\t document.addEventListener('keyup', editor.callback.globalKeyup, false);\n\t\n\t /**\r\n\t * Mouse click to radactor\r\n\t */\n\t editor.nodes.redactor.addEventListener('click', editor.callback.redactorClicked, false);\n\t\n\t /**\r\n\t * Clicks to the Plus button\r\n\t */\n\t editor.nodes.plusButton.addEventListener('click', editor.callback.plusButtonClicked, false);\n\t\n\t /**\r\n\t * Clicks to SETTINGS button in toolbar\r\n\t */\n\t editor.nodes.showSettingsButton.addEventListener('click', editor.callback.showSettingsButtonClicked, false);\n\t\n\t /**\r\n\t * Clicks to COMMENT button in toolbar\r\n\t */\n\t editor.nodes.showCommentButton.addEventListener('click', editor.callback.showCommentButtonClicked, false);\n\t\n\t /**\r\n\t * @deprecated ( but now in use for syncronization );\r\n\t * Any redactor changes: keyboard input, mouse cut/paste, drag-n-drop text\r\n\t */\n\t // editor.nodes.redactor.addEventListener('input', editor.callback.redactorInputEvent, 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.nodes.toolbarButtons[button].addEventListener('click', editor.callback.toolbarButtonClicked, false);\n\t }\n\t };\n\t\n\t /**\r\n\t * Initialize plugins before using\r\n\t * Ex. Load scripts or call some internal methods\r\n\t * @return Promise\r\n\t */\n\t ui.preparePlugins = function () {\n\t\n\t return new Promise(function (resolve, reject) {\n\t\n\t var pluginName = void 0,\n\t plugin = void 0;\n\t\n\t for (pluginName in editor.tools) {\n\t\n\t plugin = editor.tools[pluginName];\n\t\n\t if (typeof plugin.prepare != 'function') {\n\t\n\t continue;\n\t }\n\t\n\t plugin.prepare(plugin.config || {}).then(function () {\n\t\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 ui.addBlockHandlers = function (block) {\n\t\n\t if (!block) return;\n\t\n\t /**\r\n\t * Block keydowns\r\n\t */\n\t block.addEventListener('keydown', editor.callback.blockKeydown, false);\n\t\n\t /**\r\n\t * Pasting content from another source\r\n\t * We have two type of sanitization\r\n\t * First - uses deep-first search algorithm to get sub nodes,\r\n\t * sanitizes whole Block_content and replaces cleared nodes\r\n\t * This method is deprecated\r\n\t * Method is used in editor.callback.blockPaste(event)\r\n\t *\r\n\t * Secont - uses Mutation observer.\r\n\t * Observer \"observe\" DOM changes and send changings to callback.\r\n\t * Callback gets changed node, not whole Block_content.\r\n\t * Inserted or changed node, which we've gotten have been cleared and replaced with diry node\r\n\t *\r\n\t * Method is used in editor.callback.blockPasteViaSanitize(event)\r\n\t *\r\n\t * @uses html-janitor\r\n\t * @example editor.callback.blockPasteViaSanitize(event), the second method.\r\n\t *\r\n\t */\n\t block.addEventListener('paste', editor.callback.blockPasteCallback, false);\n\t\n\t block.addEventListener('mouseup', 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 /** Save all inputs in global variable state */\n\t editor.state.inputs = redactor.querySelectorAll('[contenteditable], input');\n\t };\n\t\n\t /**\r\n\t * Adds first initial block on empty redactor\r\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', 'Расскажите свою историю...');\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 button.addEventListener('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/* 3 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t *\r\n\t * Codex.Editor Transport Module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (transport) {\n\t\n\t transport.input = null;\n\t\n\t /**\r\n\t * @property {Object} arguments - keep plugin settings and defined callbacks\r\n\t */\n\t transport.arguments = null;\n\t\n\t transport.prepare = function () {\n\t\n\t var input = document.createElement('INPUT');\n\t\n\t input.type = 'file';\n\t input.addEventListener('change', editor.transport.fileSelected);\n\t\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 this.input = null;\n\t\n\t /** Prepare new one */\n\t this.prepare();\n\t };\n\t\n\t /**\r\n\t * Callback for file selection\r\n\t * @param {Event} event\r\n\t */\n\t transport.fileSelected = function () {\n\t\n\t var input = this,\n\t files = input.files,\n\t formdData = new FormData();\n\t\n\t formdData.append('files', files[0], files[0].name);\n\t\n\t editor.transport.ajax({\n\t data: formdData,\n\t beforeSend: editor.transport.arguments.beforeSend,\n\t success: editor.transport.arguments.success,\n\t error: editor.transport.arguments.error\n\t });\n\t };\n\t\n\t /**\r\n\t * Use plugin callbacks\r\n\t * @protected\r\n\t */\n\t transport.selectAndUpload = function (args) {\n\t\n\t this.arguments = args;\n\t this.input.click();\n\t };\n\t\n\t /**\r\n\t * Ajax requests module\r\n\t * @todo use core.ajax\r\n\t */\n\t transport.ajax = function (params) {\n\t\n\t var xhr = new XMLHttpRequest(),\n\t beforeSend = typeof params.beforeSend == 'function' ? params.beforeSend : function () {},\n\t success = typeof params.success == 'function' ? params.success : function () {},\n\t error = typeof params.error == 'function' ? params.error : function () {};\n\t\n\t beforeSend();\n\t\n\t xhr.open('POST', editor.settings.uploadImagesUrl, true);\n\t\n\t xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\n\t\n\t xhr.onload = function () {\n\t\n\t if (xhr.status === 200) {\n\t\n\t success(xhr.responseText);\n\t } else {\n\t\n\t editor.core.log('request error: %o', xhr);\n\t error();\n\t }\n\t };\n\t\n\t xhr.send(params.data);\n\t this.clearInput();\n\t };\n\t\n\t return transport;\n\t}({});\n\n/***/ },\n/* 4 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Renderer Module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (renderer) {\n\t\n\t /**\r\n\t * Asyncronously parses input JSON to redactor blocks\r\n\t */\n\t renderer.makeBlocksFromData = function () {\n\t\n\t /**\r\n\t * If redactor is empty, add first paragraph to start writing\r\n\t */\n\t if (!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 /**\r\n\t * Parses JSON to blocks\r\n\t * @param {object} data\r\n\t * @return Primise -> nodeList\r\n\t */\n\t renderer.appendBlocks = function (data) {\n\t\n\t var blocks = data.items;\n\t\n\t /**\r\n\t * Sequence of one-by-one blocks appending\r\n\t * Uses to save blocks order after async-handler\r\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 /**\r\n\t * Append node at specified index\r\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 /**\r\n\t * second, compose editor-block from JSON object\r\n\t */\n\t .then(editor.renderer.createBlockFromData)\n\t\n\t /**\r\n\t * now insert block to redactor\r\n\t */\n\t .then(function (blockData) {\n\t\n\t /**\r\n\t * blockData has 'block', 'type' and 'stretched' information\r\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 /**\r\n\t * Asynchronously returns block data from blocksList by index\r\n\t * @return Promise to node\r\n\t */\n\t renderer.getNodeAsync = function (blocksList, index) {\n\t\n\t return Promise.resolve().then(function () {\n\t\n\t return blocksList[index];\n\t });\n\t };\n\t\n\t /**\r\n\t * Creates editor block by JSON-data\r\n\t *\r\n\t * @uses render method of each plugin\r\n\t *\r\n\t * @param {object} blockData looks like\r\n\t * { header : {\r\n\t * text: '',\r\n\t * type: 'H3', ...\r\n\t * }\r\n\t * }\r\n\t * @return {object} with type and Element\r\n\t */\n\t renderer.createBlockFromData = function (blockData) {\n\t\n\t /** New parser */\n\t var pluginName = blockData.type,\n\t cover = blockData.cover;\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 /** New Parser */\n\t var block = editor.tools[pluginName].render(blockData.data);\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 cover: cover\n\t };\n\t };\n\t\n\t return renderer;\n\t}({});\n\n/***/ },\n/* 5 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Saver\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0.2\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (saver) {\n\t\n\t /**\r\n\t * Saves blocks\r\n\t * @private\r\n\t */\n\t saver.saveBlocks = 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 /** Empty jsonOutput state */\n\t editor.state.jsonOutput = [];\n\t\n\t Promise.resolve().then(function () {\n\t\n\t return editor.nodes.redactor.childNodes;\n\t })\n\t /** Making a sequence from separate blocks */\n\t .then(editor.saver.makeQueue).then(function () {\n\t // editor.nodes.textarea.innerHTML = editor.state.html;\n\t }).catch(function (error) {\n\t\n\t editor.core.log(error);\n\t });\n\t };\n\t\n\t saver.makeQueue = function (blocks) {\n\t\n\t var queue = 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.saver.getBlockData(queue, blocks, index);\n\t }\n\t };\n\t\n\t /** Gets every block and makes From Data */\n\t saver.getBlockData = function (queue, blocks, index) {\n\t\n\t queue.then(function () {\n\t\n\t return editor.saver.getNodeAsync(blocks, index);\n\t }).then(editor.saver.makeFormDataFromBlocks);\n\t };\n\t\n\t /**\r\n\t * Asynchronously returns block data from blocksList by index\r\n\t * @return Promise to node\r\n\t */\n\t saver.getNodeAsync = function (blocksList, index) {\n\t\n\t return Promise.resolve().then(function () {\n\t\n\t return blocksList[index];\n\t });\n\t };\n\t\n\t saver.makeFormDataFromBlocks = function (block) {\n\t\n\t var pluginName = block.dataset.tool,\n\t id = block.dataset.id;\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].save != 'function') {\n\t\n\t throw Error('Plugin \\xAB' + pluginName + '\\xBB must have save method');\n\t }\n\t\n\t /** Result saver */\n\t var blockContent = block.childNodes[0],\n\t pluginsContent = blockContent.childNodes[0],\n\t savedData = editor.tools[pluginName].save(pluginsContent),\n\t output;\n\t\n\t output = {\n\t id: id,\n\t type: pluginName,\n\t data: savedData\n\t };\n\t\n\t if (editor.tools[pluginName].validate) {\n\t\n\t var result = editor.tools[pluginName].validate(savedData);\n\t\n\t /**\r\n\t * Do not allow invalid data\r\n\t */\n\t if (!result) return;\n\t }\n\t\n\t /** Marks Blocks that will be in main page */\n\t output.cover = block.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);\n\t\n\t editor.state.jsonOutput.push(output);\n\t };\n\t\n\t saver.saveComments = function () {\n\t\n\t var fields = editor.nodes.commentsSidebar.querySelectorAll('.ce-comments-field');\n\t\n\t for (var i = 0; i < fields.length; i++) {\n\t\n\t var comments = fields[i].querySelectorAll('.ce-comment'),\n\t commentsData = [];\n\t\n\t for (var j = 0; j < comments.length; j++) {\n\t\n\t var text = comments[j].querySelector('.ce-comment__text'),\n\t time = comments[j].querySelector('.ce-comment__time');\n\t\n\t if (!text) continue;\n\t\n\t var comment = {\n\t edited: comments[j].dataset.edited,\n\t text: text.textContent,\n\t time: time.textContent\n\t };\n\t\n\t commentsData.push(comment);\n\t }\n\t\n\t if (!commentsData.length) continue;\n\t\n\t var fieldData = {\n\t blockId: fields[i].dataset.blockId,\n\t comments: commentsData\n\t };\n\t\n\t editor.state.comments.push(fieldData);\n\t }\n\t };\n\t\n\t return saver;\n\t}({});\n\n/***/ },\n/* 6 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Content Module\r\n\t * Works with DOM\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.3.11\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (content) {\n\t\n\t /**\r\n\t * Links to current active block\r\n\t * @type {null | Element}\r\n\t */\n\t content.currentNode = null;\n\t\n\t /**\r\n\t * clicked in redactor area\r\n\t * @type {null | Boolean}\r\n\t */\n\t content.editorAreaHightlighted = null;\n\t\n\t /**\r\n\t * Synchronizes redactor with original textarea\r\n\t */\n\t content.sync = function () {\n\t\n\t editor.core.log('syncing...');\n\t\n\t /**\r\n\t * Save redactor content to editor.state\r\n\t */\n\t editor.state.html = editor.nodes.redactor.innerHTML;\n\t };\n\t\n\t /**\r\n\t * @deprecated\r\n\t */\n\t content.getNodeFocused = function () {\n\t\n\t var selection = window.getSelection(),\n\t focused;\n\t\n\t if (selection.anchorNode === null) {\n\t\n\t return null;\n\t }\n\t\n\t if (selection.anchorNode.nodeType == editor.core.nodeTypes.TAG) {\n\t\n\t focused = selection.anchorNode;\n\t } else {\n\t\n\t focused = selection.focusNode.parentElement;\n\t }\n\t\n\t if (!editor.parser.isFirstLevelBlock(focused)) {\n\t\n\t /** Iterate with parent nodes to find first-level*/\n\t var parent = focused.parentNode;\n\t\n\t while (parent && !editor.parser.isFirstLevelBlock(parent)) {\n\t\n\t parent = parent.parentNode;\n\t }\n\t\n\t focused = parent;\n\t }\n\t\n\t if (focused != editor.nodes.redactor) {\n\t\n\t return focused;\n\t }\n\t\n\t return null;\n\t };\n\t\n\t /**\r\n\t * Appends background to the block\r\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 /**\r\n\t * Clear background\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Finds first-level block\r\n\t * @param {Element} node - selected or clicked in redactors area node\r\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 /**\r\n\t * Trigger this event when working node changed\r\n\t * @param {Element} targetNode - first-level of this node will be current\r\n\t * If targetNode is first-level then we set it as current else we look for parents to find first-level\r\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 this.currentNode = this.getFirstLevelBlock(targetNode);\n\t };\n\t\n\t /**\r\n\t * Replaces one redactor block with another\r\n\t * @protected\r\n\t * @param {Element} targetBlock - block to replace. Mostly currentNode.\r\n\t * @param {Element} newBlock\r\n\t * @param {string} newBlockType - type of new block; we need to store it to data-attribute\r\n\t *\r\n\t * [!] Function does not saves old block content.\r\n\t * You can get it manually and pass with newBlock.innerHTML\r\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 /**\r\n\t * Check is this block was in feed\r\n\t * If true, than set switched block also covered\r\n\t */\n\t if (targetBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE)) {\n\t\n\t newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);\n\t }\n\t\n\t /** Saving id */\n\t newBlock.dataset.id = targetBlock.dataset.id;\n\t\n\t /** Replacing */\n\t editor.nodes.redactor.replaceChild(newBlock, targetBlock);\n\t\n\t /**\r\n\t * Set new node as current\r\n\t */\n\t editor.content.workingNodeChanged(newBlock);\n\t\n\t /**\r\n\t * Add block handlers\r\n\t */\n\t editor.ui.addBlockHandlers(newBlock);\n\t\n\t /**\r\n\t * Save changes\r\n\t */\n\t editor.ui.saveInputs();\n\t };\n\t\n\t /**\r\n\t * @private\r\n\t *\r\n\t * Inserts new block to redactor\r\n\t * Wrapps block into a DIV with BLOCK_CLASSNAME class\r\n\t *\r\n\t * @param blockData {object}\r\n\t * @param blockData.block {Element} element with block content\r\n\t * @param blockData.type {string} block plugin\r\n\t * @param needPlaceCaret {bool} pass true to set caret in new block\r\n\t *\r\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 cover = blockData.cover,\n\t isStretched = blockData.stretched;\n\t\n\t var newBlock = editor.content.composeNewBlock(newBlockContent, blockType, isStretched);\n\t\n\t if (cover === true) {\n\t\n\t newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);\n\t }\n\t\n\t if (workingBlock) {\n\t\n\t editor.core.insertAfter(workingBlock, newBlock);\n\t } else {\n\t\n\t /**\r\n\t * If redactor is empty, append as first child\r\n\t */\n\t editor.nodes.redactor.appendChild(newBlock);\n\t }\n\t\n\t /**\r\n\t * Block handler\r\n\t */\n\t editor.ui.addBlockHandlers(newBlock);\n\t\n\t /**\r\n\t * Set new node as current\r\n\t */\n\t editor.content.workingNodeChanged(newBlock);\n\t\n\t /**\r\n\t * Save changes\r\n\t */\n\t editor.ui.saveInputs();\n\t\n\t if (needPlaceCaret) {\n\t\n\t /**\r\n\t * If we don't know input index then we set default value -1\r\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 /**\r\n\t * Block is inserted, wait for new click that defined focusing on editors area\r\n\t * @type {boolean}\r\n\t */\n\t content.editorAreaHightlighted = false;\n\t };\n\t\n\t /**\r\n\t * Replaces blocks with saving content\r\n\t * @protected\r\n\t * @param {Element} noteToReplace\r\n\t * @param {Element} newNode\r\n\t * @param {Element} blockType\r\n\t */\n\t content.switchBlock = function (blockToReplace, newBlock, tool) {\n\t\n\t var newBlockComposed = editor.content.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 /**\r\n\t * Iterates between child noted and looking for #text node on deepest level\r\n\t * @private\r\n\t * @param {Element} block - node where find\r\n\t * @param {int} postiton - starting postion\r\n\t * Example: childNodex.length to find from the end\r\n\t * or 0 to find from the start\r\n\t * @return {Text} block\r\n\t * @uses DFS\r\n\t */\n\t content.getDeepestTextNodeFromPosition = function (block, position) {\n\t\n\t /**\r\n\t * Clear Block from empty and useless spaces with trim.\r\n\t * Such nodes we should remove\r\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\r\n\t * decrease the quantity of childs.\r\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 /**\r\n\t * @private\r\n\t */\n\t content.composeNewBlock = function (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 newBlock.dataset.id = +new Date();\n\t\n\t return newBlock;\n\t };\n\t\n\t /**\r\n\t * Returns Range object of current selection\r\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 /**\r\n\t * Divides block in two blocks (after and before caret)\r\n\t * @private\r\n\t * @param {Int} inputIndex - target input index\r\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 /**\r\n\t * Append all childs founded before anchorNode\r\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 /**\r\n\t * Append text node which is after caret\r\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 /**\r\n\t * Make new paragraph with text after caret\r\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 /**\r\n\t * Merges two blocks — current and target\r\n\t * If target index is not exist, then previous will be as target\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Callback for HTML Mutations\r\n\t * @param {Array} mutation - Mutation Record\r\n\t */\n\t content.paste = function (mutation) {\n\t\n\t var workingNode = editor.content.currentNode,\n\t tool = workingNode.dataset.tool;\n\t\n\t if (editor.tools[tool].allowedToPaste) {\n\t\n\t editor.content.sanitize.call(this, mutation.target);\n\t } else {\n\t\n\t editor.content.pasteTextContent(mutation.addedNodes);\n\t }\n\t };\n\t\n\t /**\r\n\t * @private\r\n\t *\r\n\t * gets only text/plain content of node\r\n\t * @param {Element} target - HTML node\r\n\t */\n\t content.pasteTextContent = function (nodes) {\n\t\n\t var node = nodes[0],\n\t textNode;\n\t\n\t if (!node) {\n\t\n\t return;\n\t }\n\t\n\t if (node.nodeType == editor.core.nodeTypes.TEXT) {\n\t\n\t textNode = document.createTextNode(node);\n\t } else {\n\t\n\t textNode = document.createTextNode(node.textContent);\n\t }\n\t\n\t if (editor.core.isDomNode(node)) {\n\t\n\t node.parentNode.replaceChild(textNode, node);\n\t }\n\t };\n\t\n\t /**\r\n\t * @private\r\n\t *\r\n\t * Sanitizes HTML content\r\n\t * @param {Element} target - inserted element\r\n\t * @uses Sanitize library html-janitor\r\n\t */\n\t content.sanitize = function (target) {\n\t\n\t if (!target) {\n\t\n\t return;\n\t }\n\t\n\t var node = target[0];\n\t\n\t if (!node) {\n\t\n\t return;\n\t }\n\t\n\t /**\r\n\t * Disconnect Observer\r\n\t * hierarchy of function calls inherits context of observer\r\n\t */\n\t this.disconnect();\n\t\n\t /**\r\n\t * Don't sanitize text node\r\n\t */\n\t if (node.nodeType == editor.core.nodeTypes.TEXT) {\n\t\n\t return;\n\t }\n\t\n\t /**\r\n\t * Clear dirty content\r\n\t */\n\t var cleaner = editor.sanitizer.init(editor.satinizer.Config.BASIC),\n\t clean = cleaner.clean(target.outerHTML);\n\t\n\t var div = editor.draw.node('DIV', [], { innerHTML: clean });\n\t\n\t node.replaceWith(div.childNodes[0]);\n\t };\n\t\n\t /**\r\n\t * Iterates all right siblings and parents, which has right siblings\r\n\t * while it does not reached the first-level block\r\n\t *\r\n\t * @param {Element} node\r\n\t * @return {boolean}\r\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 /**\r\n\t * Проверяем родителей до тех пор, пока не найдем блок первого уровня\r\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 /**\r\n\t * Checks if all element right siblings is empty\r\n\t * @param node\r\n\t */\n\t var allSiblingsEmpty_ = function allSiblingsEmpty_(node) {\n\t\n\t /**\r\n\t * Нужно убедиться, что после пустого соседа ничего нет\r\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 /**\r\n\t * @public\r\n\t *\r\n\t * @param [String] htmlString - html content as string\r\n\t * @return {string} - html content as string\r\n\t */\n\t content.wrapTextWithParagraphs = function (htmlString) {\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 /**\r\n\t * Make HTML Element to Wrap Text\r\n\t * It allows us to work with input data as HTML content\r\n\t */\n\t wrapper.innerHTML = htmlString;\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 /**\r\n\t * If node is first-levet\r\n\t * we add this node to our new wrapper\r\n\t */\n\t if (blockTyped) {\n\t\n\t /**\r\n\t * If we had splitted inline nodes to paragraph before\r\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 return content;\n\t}({});\n\n/***/ },\n/* 7 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor toolbar module\r\n\t *\r\n\t * Contains:\r\n\t * - Inline toolbox\r\n\t * - Toolbox within plus button\r\n\t * - Settings section\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (toolbar) {\n\t\n\t toolbar.settings = __webpack_require__(8);\n\t toolbar.inline = __webpack_require__(9);\n\t toolbar.toolbox = __webpack_require__(10);\n\t\n\t /**\r\n\t * Margin between focused node and toolbar\r\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 /**\r\n\t * @protected\r\n\t */\n\t toolbar.open = function () {\n\t\n\t editor.nodes.toolbar.classList.add('opened');\n\t this.opened = true;\n\t };\n\t\n\t /**\r\n\t * @protected\r\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 /**\r\n\t * Moving toolbar to the specified node\r\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/* 8 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Toolbar settings\r\n\t *\r\n\t * @version 1.0.4\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (settings) {\n\t\n\t settings.opened = false;\n\t\n\t settings.setting = null;\n\t settings.actions = null;\n\t\n\t settings.cover = null;\n\t\n\t /**\r\n\t * Append and open settings\r\n\t */\n\t settings.open = function (toolType) {\n\t\n\t /**\r\n\t * Append settings content\r\n\t * It's stored in tool.settings\r\n\t */\n\t if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings) {\n\t\n\t editor.core.log('Plugin \\xAB' + toolType + '\\xBB has no settings', 'warn');\n\t // editor.nodes.pluginSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`;\n\t } else {\n\t\n\t /**\r\n\t * Draw settings block\r\n\t */\n\t var settingsBlock = editor.tools[toolType].makeSettings();\n\t\n\t editor.nodes.pluginSettings.appendChild(settingsBlock);\n\t }\n\t\n\t /** Open settings block */\n\t editor.nodes.blockSettings.classList.add('opened');\n\t editor.toolbar.settings.addDefaultSettings();\n\t this.opened = true;\n\t };\n\t\n\t /**\r\n\t * Close and clear settings\r\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 /**\r\n\t * @param {string} toolType - plugin type\r\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 /**\r\n\t * This function adds default core settings\r\n\t */\n\t settings.addDefaultSettings = function () {\n\t\n\t /** list of default settings */\n\t var feedModeToggler;\n\t\n\t /** Clear block and append initialized settings */\n\t editor.nodes.defaultSettings.innerHTML = '';\n\t\n\t /** Init all default setting buttons */\n\t feedModeToggler = editor.toolbar.settings.makeFeedModeToggler();\n\t\n\t /**\r\n\t * Fill defaultSettings\r\n\t */\n\t\n\t /**\r\n\t * Button that enables/disables Feed-mode\r\n\t * Feed-mode means that block will be showed in articles-feed like cover\r\n\t */\n\t editor.nodes.defaultSettings.appendChild(feedModeToggler);\n\t };\n\t\n\t /**\r\n\t * Cover setting.\r\n\t * This tune highlights block, so that it may be used for showing target block on main page\r\n\t * Draw different setting when block is marked for main page\r\n\t * If TRUE, then we show button that removes this selection\r\n\t * Also defined setting \"Click\" events will be listened and have separate callbacks\r\n\t *\r\n\t * @return {Element} node/button that we place in default settings block\r\n\t */\n\t settings.makeFeedModeToggler = function () {\n\t\n\t var isFeedModeActivated = editor.toolbar.settings.isFeedModeActivated(),\n\t setting,\n\t data;\n\t\n\t if (!isFeedModeActivated) {\n\t\n\t data = {\n\t innerHTML: 'Вывести в ленте'\n\t };\n\t } else {\n\t\n\t data = {\n\t innerHTML: 'Не выводить в ленте'\n\t };\n\t }\n\t\n\t setting = editor.draw.node('DIV', editor.ui.className.SETTINGS_ITEM, data);\n\t setting.addEventListener('click', editor.toolbar.settings.updateFeedMode, false);\n\t\n\t return setting;\n\t };\n\t\n\t /**\r\n\t * Updates Feed-mode\r\n\t */\n\t settings.updateFeedMode = function () {\n\t\n\t var currentNode = editor.content.currentNode;\n\t\n\t currentNode.classList.toggle(editor.ui.className.BLOCK_IN_FEED_MODE);\n\t\n\t editor.toolbar.settings.close();\n\t };\n\t\n\t settings.isFeedModeActivated = function () {\n\t\n\t var currentBlock = editor.content.currentNode;\n\t\n\t if (currentBlock) {\n\t\n\t return currentBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);\n\t } else {\n\t\n\t return false;\n\t }\n\t };\n\t\n\t /**\r\n\t * Here we will draw buttons and add listeners to components\r\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 settingButton.addEventListener('click', editor.toolbar.settings.removeButtonClicked, false);\n\t\n\t confirmAction.addEventListener('click', editor.toolbar.settings.confirmRemovingRequest, false);\n\t\n\t cancelAction.addEventListener('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 /**\r\n\t * If all blocks are removed\r\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/* 9 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Inline toolbar\r\n\t *\r\n\t * Contains from tools:\r\n\t * Bold, Italic, Underline and Anchor\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (inline) {\n\t\n\t inline.buttonsOpened = null;\n\t inline.actionsOpened = null;\n\t inline.wrappersOffset = null;\n\t\n\t /**\r\n\t * saving selection that need for execCommand for styling\r\n\t *\r\n\t */\n\t inline.storedSelection = null;\n\t\n\t /**\r\n\t * @protected\r\n\t *\r\n\t * Open inline toobar\r\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 /**\r\n\t * tool allowed to open inline toolbar\r\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 /**\r\n\t * @protected\r\n\t *\r\n\t * Closes inline toolbar\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Moving toolbar\r\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 (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 /**\r\n\t * @private\r\n\t *\r\n\t * Tool Clicked\r\n\t */\n\t\n\t inline.toolClicked = function (event, type) {\n\t\n\t /**\r\n\t * For simple tools we use default browser function\r\n\t * For more complicated tools, we should write our own behavior\r\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 /**\r\n\t * highlight buttons\r\n\t * after making some action\r\n\t */\n\t editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\n\t };\n\t\n\t /**\r\n\t * @private\r\n\t *\r\n\t * Saving wrappers offset in DOM\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Calculates offset of DOM element\r\n\t *\r\n\t * @param el\r\n\t * @returns {{top: number, left: number}}\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Calculates position of selected text\r\n\t * @returns {{x: number, y: number}}\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Returns selected text as String\r\n\t * @returns {string}\r\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 /**\r\n\t * Callback for keydowns in inline toolbar \"Insert link...\" input\r\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 /**\r\n\t * Preventing events that will be able to happen\r\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 /**\r\n\t * Changing stored selection. if we want to remove anchor from word\r\n\t * we should remove anchor from whole word, not only selected part.\r\n\t * The solution is than we get the length of current link\r\n\t * Change start position to - end of selection minus length of anchor\r\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 /**\r\n\t * focus to input\r\n\t * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus\r\n\t * Prevents event after showing input and when we need to focus an input which is in unexisted form\r\n\t */\n\t action.focus();\n\t event.preventDefault();\n\t\n\t /** Callback to link action */\n\t action.addEventListener('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 /**\r\n\t * @private\r\n\t *\r\n\t * Sets URL\r\n\t *\r\n\t * @param {String} url - URL\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Saves selection\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Sets to previous selection (Range)\r\n\t *\r\n\t * @param {Element} containerEl - editable element where we restore range\r\n\t * @param {Object} savedSel - range basic information to restore\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Removes all ranges from window selection\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * sets or removes hightlight\r\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 /**\r\n\t *\r\n\t * hightlight for anchors\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Mark button if text is already executed\r\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 /**\r\n\t * @private\r\n\t *\r\n\t * Removes hightlight\r\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/* 10 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor toolbox\r\n\t *\r\n\t * All tools be able to appended here\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (toolbox) {\n\t\n\t toolbox.opened = false;\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 /** 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 /** Makes toolbox disapear */\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\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;\n\t visibleTool = tools[nextToolIndex];\n\t\n\t while (!editor.tools[visibleTool].displayInToolbox) {\n\t\n\t nextToolIndex++;\n\t visibleTool = tools[nextToolIndex];\n\t\n\t if (nextToolIndex == tools.length) {\n\t\n\t nextToolIndex = 0;\n\t visibleTool = tools[nextToolIndex];\n\t }\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 /**\r\n\t * Transforming selected node type into selected toolbar element type\r\n\t * @param {event} event\r\n\t */\n\t toolbox.toolClicked = function (event) {\n\t\n\t /**\r\n\t * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty\r\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 /**\r\n\t * Changing current Node\r\n\t */\n\t editor.content.workingNodeChanged();\n\t\n\t /**\r\n\t * Move toolbar when node is changed\r\n\t */\n\t editor.toolbar.move();\n\t };\n\t\n\t return toolbox;\n\t}({});\n\n/***/ },\n/* 11 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor callbacks module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.3.5\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (callbacks) {\n\t\n\t callbacks.globalKeydown = function (event) {\n\t\n\t switch (event.keyCode) {\n\t case editor.core.keys.ENTER:\n\t editor.callback.enterKeyPressed(event);break;\n\t }\n\t };\n\t\n\t callbacks.redactorKeyDown = function (event) {\n\t\n\t switch (event.keyCode) {\n\t case editor.core.keys.TAB:\n\t editor.callback.tabKeyPressed(event);break;\n\t case editor.core.keys.ENTER:\n\t editor.callback.enterKeyPressedOnRedactorZone(event);break;\n\t case editor.core.keys.ESC:\n\t editor.callback.escapeKeyPressed(event);break;\n\t default:\n\t editor.callback.defaultKeyPressed(event);break;\n\t }\n\t };\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 editor.callback.arrowKeyPressed(event);break;\n\t }\n\t };\n\t\n\t callbacks.tabKeyPressed = function (event) {\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 event.preventDefault();\n\t };\n\t\n\t /**\r\n\t * @param {Event} event\r\n\t */\n\t callbacks.enterKeyPressed = function () {\n\t\n\t if (editor.content.editorAreaHightlighted) {\n\t\n\t /**\r\n\t * it means that we lose input index, saved index before is not correct\r\n\t * therefore we need to set caret when we insert new block\r\n\t */\n\t editor.caret.inputIndex = -1;\n\t\n\t editor.callback.enterPressedOnBlock();\n\t }\n\t };\n\t\n\t /**\r\n\t * ENTER key handler\r\n\t * Makes new paragraph block\r\n\t */\n\t callbacks.enterKeyPressedOnRedactorZone = function (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 /**\r\n\t * When toolbar is opened, select tool instead of making new paragraph\r\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 /**\r\n\t * Stop other listeners callback executions\r\n\t */\n\t event.stopPropagation();\n\t event.stopImmediatePropagation();\n\t\n\t return;\n\t }\n\t\n\t /**\r\n\t * Allow paragraph lineBreaks with shift enter\r\n\t * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation\r\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 /**\r\n\t * Allow making new in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\r\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 /**\r\n\t * Workaround situation when caret at the Text node that has some wrapper Elements\r\n\t * Split block cant handle this.\r\n\t * We need to save default behavior\r\n\t */\n\t isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true';\n\t\n\t /**\r\n\t * Split blocks when input has several nodes and caret placed in textNode\r\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 callbacks.escapeKeyPressed = function (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 /**\r\n\t * @param {Event} event\r\n\t */\n\t callbacks.arrowKeyPressed = function () {\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 /**\r\n\t * @param {Event} event\r\n\t */\n\t callbacks.defaultKeyPressed = function () {\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 callbacks.redactorClicked = function (event) {\n\t\n\t callbacks.detectWhenClickedOnFirstLevelBlockArea();\n\t\n\t editor.content.workingNodeChanged(event.target);\n\t\n\t editor.ui.saveInputs();\n\t\n\t var selectedText = editor.toolbar.inline.getSelectionText(),\n\t firstLevelBlock;\n\t\n\t /**\r\n\t * If selection range took off, then we hide inline toolbar\r\n\t */\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 /**\r\n\t * If inputs in redactor does not exits, then we put input index 0 not -1\r\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 /**\r\n\t * @todo Refactor\r\n\t */\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\n\t /**\r\n\t * Move toolbar to the right position and open\r\n\t */\n\t editor.toolbar.move();\n\t editor.toolbar.open();\n\t } else {\n\t\n\t /**\r\n\t * Move toolbar to the new position and open\r\n\t */\n\t editor.toolbar.move();\n\t editor.toolbar.open();\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 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 /** Mark current block */\n\t editor.content.markBlock();\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 /**\r\n\t * This method allows to define, is caret in contenteditable element or not.\r\n\t * Otherwise, if we get TEXT node from range container, that will means we have input index.\r\n\t * In this case we use default browsers behaviour (if plugin allows that) or overwritten action.\r\n\t * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always\r\n\t * specifies to the first-level block. Other cases we just ignore.\r\n\t */\n\t callbacks.detectWhenClickedOnFirstLevelBlockArea = function () {\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 ? false : true;\n\t }\n\t };\n\t\n\t /**\r\n\t * Toolbar button click handler\r\n\t * @param this - cursor to the button\r\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 /** Show or Hide toolbox when plus button is clicked */\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 /**\r\n\t * Block handlers for KeyDown events\r\n\t */\n\t callbacks.blockKeydown = function (event) {\n\t\n\t var block = this; // event.target 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 editor.callback.blockRightOrDownArrowPressed();\n\t break;\n\t\n\t case editor.core.keys.BACKSPACE:\n\t editor.callback.backspacePressed(block, event);\n\t break;\n\t\n\t case editor.core.keys.UP:\n\t case editor.core.keys.LEFT:\n\t editor.callback.blockLeftOrUpArrowPressed();\n\t break;\n\t\n\t }\n\t };\n\t\n\t /**\r\n\t * RIGHT or DOWN keydowns on block\r\n\t */\n\t callbacks.blockRightOrDownArrowPressed = function () {\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 /**\r\n\t * Founded contentEditable element doesn't have childs\r\n\t * Or maybe New created block\r\n\t */\n\t if (!focusedNode.textContent) {\n\t\n\t editor.caret.setToNextBlock(editableElementIndex);\n\t return;\n\t }\n\t\n\t /**\r\n\t * Do nothing when caret doesn not reaches the end of last child\r\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 /**\r\n\t * LEFT or UP keydowns on block\r\n\t */\n\t callbacks.blockLeftOrUpArrowPressed = function () {\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 /**\r\n\t * LEFT or UP not at the beginning\r\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 /**\r\n\t * Do nothing if caret is not at the beginning of first child\r\n\t */\n\t var caretInFirstChild = false,\n\t caretAtTheBeginning = false;\n\t\n\t var firstChild, deepestTextnode;\n\t\n\t /**\r\n\t * Founded contentEditable element doesn't have childs\r\n\t * Or maybe New created block\r\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 /**\r\n\t * Callback for enter key pressing in first-level block area\r\n\t * @param {Event} event\r\n\t */\n\t callbacks.enterPressedOnBlock = function () {\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 callbacks.backspacePressed = function (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 (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 /**\r\n\t * If all blocks are removed\r\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 /**\r\n\t * @deprecated\r\n\t *\r\n\t * @param event\r\n\t */\n\t callbacks.blockPaste = function (event) {\n\t\n\t var currentInputIndex = editor.caret.getCurrentInputIndex(),\n\t node = editor.state.inputs[currentInputIndex];\n\t\n\t window.setTimeout(function () {\n\t\n\t editor.content.sanitize(node);\n\t\n\t event.preventDefault();\n\t }, 10);\n\t\n\t event.stopImmediatePropagation();\n\t };\n\t\n\t /**\r\n\t * This method is used to observe pasted dirty data.\r\n\t *\r\n\t * Mutation handlers send to separate observers each mutation (added, changed and so on), which will be\r\n\t * passed from handler that sanitizes and replaces data.\r\n\t *\r\n\t * Probably won't be used\r\n\t *\r\n\t * @deprecated\r\n\t *\r\n\t * @param event\r\n\t * @private\r\n\t */\n\t callbacks._blockPasteCallback = function () {\n\t\n\t var currentInputIndex = editor.caret.getCurrentInputIndex();\n\t\n\t /**\r\n\t * create an observer instance\r\n\t */\n\t var observer = new MutationObserver(editor.callback.handleMutationsOnPaste);\n\t\n\t /**\r\n\t * configuration of the observer:\r\n\t */\n\t var config = {\n\t attributes: true,\n\t childList: false,\n\t characterData: false,\n\t subtree: true\n\t };\n\t\n\t // pass in the target node, as well as the observer options\n\t observer.observe(editor.state.inputs[currentInputIndex], config);\n\t };\n\t\n\t /**\r\n\t * This method prevents default behaviour.\r\n\t *\r\n\t * We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.\r\n\t * Firstly, we need to memorize the caret position. We can do that by getting the range of selection.\r\n\t * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node\r\n\t *\r\n\t * @param event\r\n\t */\n\t callbacks.blockPasteCallback = function (event) {\n\t\n\t /** Prevent default behaviour */\n\t event.preventDefault();\n\t\n\t /** get html pasted data - dirty data */\n\t var data = event.clipboardData.getData('text/html') || event.clipboardData.getData('text/plain');\n\t\n\t /** Temporary DIV that is used to work with childs as arrays item */\n\t var div = editor.draw.node('DIV', '', {}),\n\t cleaner = new editor.sanitizer.init(editor.sanitizer.Config.BASIC),\n\t cleanData,\n\t fragment;\n\t\n\t /** Create fragment, that we paste to range after proccesing */\n\t fragment = document.createDocumentFragment();\n\t\n\t cleanData = cleaner.clean(data);\n\t\n\t div.innerHTML = cleanData;\n\t\n\t var node, lastNode;\n\t\n\t /**\r\n\t * and fill in fragment\r\n\t */\n\t while (node = div.firstChild) {\n\t\n\t lastNode = fragment.appendChild(node);\n\t }\n\t\n\t /**\r\n\t * work with selection and range\r\n\t */\n\t var selection, range;\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(fragment);\n\t // document.execCommand('insertParagraph', false, \" \");\n\t\n\t /** Preserve the selection */\n\t if (lastNode) {\n\t\n\t range = range.cloneRange();\n\t range.setStartAfter(lastNode);\n\t range.collapse(true);\n\t selection.removeAllRanges();\n\t selection.addRange(range);\n\t }\n\t };\n\t\n\t /**\r\n\t * Sends all mutations to paste handler\r\n\t */\n\t callbacks.handleMutationsOnPaste = function (mutations) {\n\t\n\t var self = this;\n\t\n\t /**\r\n\t * Calling function with context of this function.\r\n\t * Also, we should sanitize pasted or changed data one time and ignore\r\n\t * changings which makes sanitize method.\r\n\t * For that, we need to send Context, MutationObserver.__proto__ that contains\r\n\t * observer disconnect method.\r\n\t */\n\t mutations.forEach(function (mutation) {\n\t\n\t editor.content.paste.call(self, mutation);\n\t });\n\t };\n\t\n\t /**\r\n\t * Clicks on block settings button\r\n\t */\n\t callbacks.showSettingsButtonClicked = function () {\n\t\n\t /**\r\n\t * Get type of current block\r\n\t * It uses to append settings from tool.settings property.\r\n\t * ...\r\n\t * Type is stored in data-type attribute on block\r\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 callbacks.showCommentButtonClicked = function () {\n\t\n\t var block = editor.content.currentNode;\n\t\n\t editor.comments.add(block);\n\t };\n\t\n\t return callbacks;\n\t}({});\n\n/***/ },\n/* 12 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Draw module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0.\r\n\t */\n\t\n\tmodule.exports = function (draw) {\n\t\n\t /**\r\n\t * Base editor wrapper\r\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 /**\r\n\t * Content-editable holder\r\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 /**\r\n\t * Comments side bar\r\n\t */\n\t draw.commentsSidebar = function () {\n\t\n\t var sidebar = draw.node('DIV', 'ce-comments-sidebar');\n\t\n\t return sidebar;\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 /**\r\n\t * Empty toolbar with toggler\r\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 /**\r\n\t * Inline toolbar\r\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 /**\r\n\t * Wrapper for inline toobar buttons\r\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 /**\r\n\t * For some actions\r\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 /**\r\n\t * Block with notifications\r\n\t */\n\t draw.alertsHolder = function () {\n\t\n\t var block = document.createElement('div');\n\t\n\t block.classList.add('ce_notifications-block');\n\t\n\t return block;\n\t };\n\t\n\t /**\r\n\t * @todo Desc\r\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 /**\r\n\t * Block settings panel\r\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 /**\r\n\t * Settings button in toolbar\r\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 /**\r\n\t * Comment button in toolbar\r\n\t */\n\t draw.commentButton = function () {\n\t\n\t var toggler = document.createElement('span');\n\t\n\t toggler.className = 'ce-toolbar__comment-btn';\n\t\n\t /** Toggler button*/\n\t toggler.innerHTML = '';\n\t\n\t return toggler;\n\t };\n\t\n\t /**\r\n\t * Redactor tools wrapper\r\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 /**\r\n\t * @protected\r\n\t *\r\n\t * Draws tool buttons for toolbox\r\n\t *\r\n\t * @param {String} type\r\n\t * @param {String} classname\r\n\t * @returns {Element}\r\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 /**\r\n\t * @protected\r\n\t *\r\n\t * Draws tools for inline toolbar\r\n\t *\r\n\t * @param {String} type\r\n\t * @param {String} classname\r\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 /**\r\n\t * Redactor block\r\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 /**\r\n\t * Creates Node with passed tagName and className\r\n\t * @param {string} tagName\r\n\t * @param {string} className\r\n\t * @param {object} properties - allow to assign properties\r\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 return draw;\n\t}({});\n\n/***/ },\n/* 13 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Caret Module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (caret) {\n\t\n\t /**\r\n\t * @var {int} InputIndex - editable element in DOM\r\n\t */\n\t caret.inputIndex = null;\n\t\n\t /**\r\n\t * @var {int} offset - caret position in a text node.\r\n\t */\n\t caret.offset = null;\n\t\n\t /**\r\n\t * @var {int} focusedNodeIndex - we get index of child node from first-level block\r\n\t */\n\t caret.focusedNodeIndex = null;\n\t\n\t /**\r\n\t * Creates Document Range and sets caret to the element.\r\n\t * @protected\r\n\t * @uses caret.save — if you need to save caret position\r\n\t * @param {Element} el - Changed Node.\r\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.tagName == 'INPUT') {\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 /**\r\n\t * @protected\r\n\t * Updates index of input and saves it in caret object\r\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.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 /**\r\n\t * Returns current input index (caret object)\r\n\t */\n\t caret.getCurrentInputIndex = function () {\n\t\n\t return caret.inputIndex;\n\t };\n\t\n\t /**\r\n\t * @param {int} index - index of first-level block after that we set caret into next input\r\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 /**\r\n\t * When new Block created or deleted content of input\r\n\t * We should add some text node to set caret\r\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 /**\r\n\t * @param {int} index - index of target input.\r\n\t * Sets caret to input with this index\r\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 /**\r\n\t * When new Block created or deleted content of input\r\n\t * We should add some text node to set caret\r\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 /**\r\n\t * @param {int} index - index of input\r\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 /**\r\n\t * When new Block created or deleted content of input\r\n\t * We should add some text node to set caret\r\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 return caret;\n\t}({});\n\n/***/ },\n/* 14 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Editor Notification Module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.0\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (notifications) {\n\t\n\t /**\r\n\t * Error notificator. Shows block with message\r\n\t * @protected\r\n\t */\n\t notifications.errorThrown = function (errorMsg, event) {\n\t\n\t editor.notifications.send('This action is not available currently', event.type, false);\n\t };\n\t\n\t /**\r\n\t * Appends notification with different types\r\n\t * @param message {string} - Error or alert message\r\n\t * @param type {string} - Type of message notification. Ex: Error, Warning, Danger ...\r\n\t * @param append {boolean} - can be True or False when notification should be inserted after\r\n\t */\n\t notifications.send = function (message, type, append) {\n\t\n\t var notification = editor.draw.block('div');\n\t\n\t notification.textContent = message;\n\t notification.classList.add('ce_notification-item', 'ce_notification-' + type, 'flipInX');\n\t\n\t if (!append) {\n\t\n\t editor.nodes.notifications.innerHTML = '';\n\t }\n\t\n\t editor.nodes.notifications.appendChild(notification);\n\t\n\t window.setTimeout(function () {\n\t\n\t notification.remove();\n\t }, 3000);\n\t };\n\t\n\t return notifications;\n\t}({});\n\n/***/ },\n/* 15 */\n/***/ function(module, exports) {\n\n\t\"use strict\";\n\t\n\t/**\r\n\t * Codex Editor Parser Module\r\n\t *\r\n\t * @author Codex Team\r\n\t * @version 1.1\r\n\t */\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (parser) {\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 /**\r\n\t * Check DOM node for display style: separated block or child-view\r\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/* 16 */\n/***/ function(module, exports, __webpack_require__) {\n\n\t'use strict';\n\t\n\t/**\r\n\t * Codex Sanitizer\r\n\t */\n\t\n\tvar janitor = __webpack_require__(17);\n\t\n\tmodule.exports = function (sanitizer) {\n\t\n\t /**\r\n\t * Basic config\r\n\t */\n\t var Config = {\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 i: {},\n\t b: {},\n\t strong: {},\n\t em: {},\n\t span: {}\n\t }\n\t }\n\t };\n\t\n\t sanitizer.Config = Config;\n\t\n\t sanitizer.init = janitor;\n\t\n\t return sanitizer;\n\t}({});\n\n/***/ },\n/* 17 */\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/* 18 */\n/***/ function(module, exports) {\n\n\t'use strict';\n\t\n\tvar editor = codex.editor;\n\t\n\tmodule.exports = function (comments) {\n\t\n\t var draw = {\n\t\n\t commentsField: function commentsField(blockId) {\n\t\n\t var field = editor.draw.node('DIV', 'ce-comments-field');\n\t\n\t field.dataset.blockId = blockId;\n\t\n\t return field;\n\t },\n\t\n\t input: function input(text) {\n\t\n\t var wrapper = editor.draw.node('DIV', 'ce-comment'),\n\t input = editor.draw.node('DIV', 'ce-comment__input', { 'contentEditable': 'true', 'textContent': text || '' }),\n\t deleteBtn = editor.draw.node('DIV', 'ce-comment__delete', { 'textContent': 'Delete' }),\n\t postBtn = editor.draw.node('DIV', 'ce-comment__post', { 'textContent': text ? 'Save' : 'Comment' });\n\t\n\t postBtn.addEventListener('click', callbacks.commentClicked);\n\t deleteBtn.addEventListener('click', callbacks.deleteClicked);\n\t\n\t wrapper.appendChild(input);\n\t wrapper.appendChild(postBtn);\n\t wrapper.appendChild(deleteBtn);\n\t\n\t wrapper.dataset.edited = text ? 'edited ' : '';\n\t\n\t return wrapper;\n\t },\n\t\n\t comment: function comment(data) {\n\t\n\t if (!data.text) return;\n\t\n\t var wrapper = editor.draw.node('DIV', 'ce-comment'),\n\t text = editor.draw.node('DIV', 'ce-comment__text', { 'textContent': data.text }),\n\t date = new Date().toLocaleDateString('en-US', {\n\t month: 'short',\n\t day: 'numeric',\n\t hour: 'numeric',\n\t minute: 'numeric',\n\t hour12: false\n\t }),\n\t time = editor.draw.node('DIV', 'ce-comment__time', { 'textContent': date }),\n\t deleteBtn = editor.draw.node('DIV', 'ce-comment__delete', { 'textContent': 'Delete' }),\n\t editBtn = editor.draw.node('DIV', 'ce-comment__edit', { 'textContent': 'Edit' });\n\t\n\t editBtn.addEventListener('click', callbacks.editClicked);\n\t deleteBtn.addEventListener('click', callbacks.deleteClicked);\n\t\n\t wrapper.dataset.edited = data.edited;\n\t time.dataset.edited = data.edited;\n\t\n\t wrapper.appendChild(text);\n\t wrapper.appendChild(time);\n\t wrapper.appendChild(editBtn);\n\t wrapper.appendChild(deleteBtn);\n\t\n\t return wrapper;\n\t }\n\t\n\t };\n\t\n\t var callbacks = {\n\t\n\t commentClicked: function commentClicked(e) {\n\t\n\t var field = e.path[2],\n\t wrapper = e.path[1],\n\t input = wrapper.querySelector('.ce-comment__input');\n\t\n\t if (input.textContent.trim() == '') return;\n\t\n\t var comment = draw.comment({\n\t text: input.textContent,\n\t edited: wrapper.dataset.edited\n\t });\n\t\n\t field.replaceChild(comment, wrapper);\n\t },\n\t\n\t editClicked: function editClicked(e) {\n\t\n\t var field = e.path[2],\n\t wrapper = e.path[1],\n\t text = wrapper.querySelector('.ce-comment__text');\n\t\n\t var comment = draw.input(text.textContent);\n\t\n\t field.replaceChild(comment, wrapper);\n\t },\n\t\n\t deleteClicked: function deleteClicked(e) {\n\t\n\t var field = e.path[2],\n\t wrapper = e.path[1];\n\t\n\t field.removeChild(wrapper);\n\t }\n\t\n\t };\n\t\n\t var methods = {\n\t\n\t getCoords: function getCoords(block) {\n\t\n\t var rect = block.getBoundingClientRect();\n\t\n\t return {\n\t x: window.pageXOffset + rect.left,\n\t y: window.pageYOffset + rect.top\n\t };\n\t }\n\t\n\t };\n\t\n\t comments.add = function (block) {\n\t\n\t var blockId = block.dataset.id;\n\t\n\t var field = editor.nodes.commentsSidebar.querySelector('.ce-comments-field[data-block-id=\"' + blockId + '\"]') || draw.commentsField(blockId);\n\t\n\t var comment = draw.input();\n\t\n\t comment.querySelector('.ce-comment__input').focus();\n\t\n\t field.appendChild(comment);\n\t\n\t var blockCoords = methods.getCoords(block);\n\t\n\t field.style.top = blockCoords.y + 'px';\n\t\n\t editor.nodes.commentsSidebar.appendChild(field);\n\t };\n\t\n\t return comments;\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 c48085d2245608162342","/**\r\n *\r\n * Codex Editor\r\n *\r\n * @author Codex Team\r\n */\r\n\r\nmodule.exports = (function (editor) {\r\n\r\n 'use strict';\r\n\r\n editor.version = VERSION;\r\n\r\n var init = function () {\r\n\r\n editor.core = require('./modules/core');\r\n editor.ui = require('./modules/ui');\r\n editor.transport = require('./modules/transport');\r\n editor.renderer = require('./modules/renderer');\r\n editor.saver = require('./modules/saver');\r\n editor.content = require('./modules/content');\r\n editor.toolbar = require('./modules/toolbar/toolbar');\r\n editor.callback = require('./modules/callbacks');\r\n editor.draw = require('./modules/draw');\r\n editor.caret = require('./modules/caret');\r\n editor.notifications = require('./modules/notifications');\r\n editor.parser = require('./modules/parser');\r\n editor.sanitizer = require('./modules/sanitizer');\r\n editor.comments = require('./modules/comments');\r\n\r\n };\r\n\r\n /**\r\n * @public\r\n *\r\n * holds initial settings\r\n */\r\n editor.settings = {\r\n tools : ['paragraph', 'header', 'picture', 'list', 'quote', 'code', 'twitter', 'instagram', 'smile'],\r\n textareaId: 'codex-editor',\r\n uploadImagesUrl: '/editor/transport/',\r\n\r\n // Type of block showing on empty editor\r\n initialBlockPlugin: 'paragraph'\r\n };\r\n\r\n /**\r\n * public\r\n *\r\n * Static nodes\r\n */\r\n editor.nodes = {\r\n textarea : null,\r\n wrapper : null,\r\n toolbar : null,\r\n inlineToolbar : {\r\n wrapper : null,\r\n buttons : null,\r\n actions : null\r\n },\r\n toolbox : null,\r\n notifications : null,\r\n plusButton : null,\r\n showSettingsButton: null,\r\n showTrashButton : null,\r\n showCommentButton : null,\r\n blockSettings : null,\r\n pluginSettings : null,\r\n defaultSettings : null,\r\n toolbarButtons : {}, // { type : DomEl, ... }\r\n redactor : null,\r\n commentsSidebar : null\r\n };\r\n\r\n /**\r\n * @public\r\n *\r\n * Output state\r\n */\r\n editor.state = {\r\n jsonOutput: [],\r\n blocks : [],\r\n inputs : [],\r\n comments : []\r\n };\r\n\r\n /**\r\n * @public\r\n * Editor plugins\r\n */\r\n editor.tools = {};\r\n\r\n /**\r\n * Initialization\r\n * @uses Promise cEditor.core.prepare\r\n * @param {} userSettings are :\r\n * - tools [],\r\n * - textareaId String\r\n * ...\r\n *\r\n * Load user defined tools\r\n * Tools must contain this important objects :\r\n * @param {String} type - this is a type of plugin. It can be used as plugin name\r\n * @param {String} iconClassname - this a icon in toolbar\r\n * @param {Object} make - what should plugin do, when it is clicked\r\n * @param {Object} appendCallback - callback after clicking\r\n * @param {Element} settings - what settings does it have\r\n * @param {Object} render - plugin get JSON, and should return HTML\r\n * @param {Object} save - plugin gets HTML content, returns JSON\r\n * @param {Boolean} displayInToolbox - will be displayed in toolbox. Default value is TRUE\r\n * @param {Boolean} enableLineBreaks - inserts new block or break lines. Default value is FALSE\r\n *\r\n * @example\r\n * - type : 'header',\r\n * - iconClassname : 'ce-icon-header',\r\n * - make : headerTool.make,\r\n * - appendCallback : headerTool.appendCallback,\r\n * - settings : headerTool.makeSettings(),\r\n * - render : headerTool.render,\r\n * - save : headerTool.save,\r\n * - displayInToolbox : true,\r\n * - enableLineBreaks : false\r\n */\r\n editor.start = function (userSettings) {\r\n\r\n init();\r\n\r\n editor.core.prepare(userSettings)\r\n\r\n // If all ok, make UI, bind events and parse initial-content\r\n .then(editor.ui.make)\r\n .then(editor.ui.addTools)\r\n .then(editor.ui.bindEvents)\r\n .then(editor.ui.preparePlugins)\r\n .then(editor.transport.prepare)\r\n .then(editor.renderer.makeBlocksFromData)\r\n .then(editor.ui.saveInputs)\r\n .catch(function (error) {\r\n\r\n editor.core.log('Initialization failed with error: %o', 'warn', error);\r\n\r\n });\r\n\r\n };\r\n\r\n return editor;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./codex.js","/**\r\n * Codex Editor Core\r\n *\r\n * @author Codex Team\r\n * @version 1.1.2\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (core) {\r\n\r\n /**\r\n * @public\r\n *\r\n * Editor preparing method\r\n * @return Promise\r\n */\r\n core.prepare = function (userSettings) {\r\n\r\n return new Promise(function (resolve, reject) {\r\n\r\n if ( userSettings ) {\r\n\r\n editor.settings.tools = userSettings.tools || editor.settings.tools;\r\n\r\n }\r\n\r\n if (userSettings.data) {\r\n\r\n editor.state.blocks = userSettings.data;\r\n\r\n }\r\n\r\n if (userSettings.initialBlockPlugin) {\r\n\r\n editor.settings.initialBlockPlugin = userSettings.initialBlockPlugin;\r\n\r\n }\r\n\r\n if (userSettings.uploadImagesUrl) {\r\n\r\n editor.settings.uploadImagesUrl = userSettings.uploadImagesUrl;\r\n\r\n }\r\n\r\n editor.nodes.textarea = document.getElementById(userSettings.textareaId || editor.settings.textareaId);\r\n\r\n if (typeof editor.nodes.textarea === undefined || editor.nodes.textarea === null) {\r\n\r\n reject(Error(\"Textarea wasn't found by ID: #\" + userSettings.textareaId));\r\n\r\n } else {\r\n\r\n resolve();\r\n\r\n }\r\n\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Logging method\r\n * @param type = ['log', 'info', 'warn']\r\n */\r\n core.log = function (msg, type, arg) {\r\n\r\n type = type || 'log';\r\n\r\n if (!arg) {\r\n\r\n arg = msg || 'undefined';\r\n msg = '[codex-editor]: %o';\r\n\r\n } else {\r\n\r\n msg = '[codex-editor]: ' + msg;\r\n\r\n }\r\n\r\n try{\r\n\r\n if ( 'console' in window && window.console[ type ] ) {\r\n\r\n if ( arg ) window.console[ type ]( msg, arg );\r\n else window.console[ type ]( msg );\r\n\r\n }\r\n\r\n }catch(e) {}\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Helper for insert one element after another\r\n */\r\n core.insertAfter = function (target, element) {\r\n\r\n target.parentNode.insertBefore(element, target.nextSibling);\r\n\r\n };\r\n\r\n /**\r\n * @const\r\n *\r\n * Readable DOM-node types map\r\n */\r\n core.nodeTypes = {\r\n TAG : 1,\r\n TEXT : 3,\r\n COMMENT : 8\r\n };\r\n\r\n /**\r\n * @const\r\n * Readable keys map\r\n */\r\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 };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Check object for DOM node\r\n */\r\n core.isDomNode = function (el) {\r\n\r\n return el && typeof el === 'object' && el.nodeType && el.nodeType == this.nodeTypes.TAG;\r\n\r\n };\r\n\r\n /**\r\n * Native Ajax\r\n */\r\n core.ajax = function (data) {\r\n\r\n if (!data || !data.url) {\r\n\r\n return;\r\n\r\n }\r\n\r\n var XMLHTTP = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'),\r\n successFunction = function () {},\r\n params = '',\r\n obj;\r\n\r\n data.async = true;\r\n data.type = data.type || 'GET';\r\n data.data = data.data || '';\r\n data['content-type'] = data['content-type'] || 'application/json; charset=utf-8';\r\n successFunction = data.success || successFunction ;\r\n\r\n if (data.type == 'GET' && data.data) {\r\n\r\n data.url = /\\?/.test(data.url) ? data.url + '&' + data.data : data.url + '?' + data.data;\r\n\r\n } else {\r\n\r\n for(obj in data.data) {\r\n\r\n params += (obj + '=' + encodeURIComponent(data.data[obj]) + '&');\r\n\r\n }\r\n\r\n }\r\n\r\n if (data.withCredentials) {\r\n\r\n XMLHTTP.withCredentials = true;\r\n\r\n }\r\n\r\n if (data.beforeSend && typeof data.beforeSend == 'function') {\r\n\r\n data.beforeSend.call();\r\n\r\n }\r\n\r\n XMLHTTP.open( data.type, data.url, data.async );\r\n XMLHTTP.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r\n XMLHTTP.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');\r\n\r\n XMLHTTP.onreadystatechange = function () {\r\n\r\n if (XMLHTTP.readyState == 4 && XMLHTTP.status == 200) {\r\n\r\n successFunction(XMLHTTP.responseText);\r\n\r\n }\r\n\r\n };\r\n\r\n XMLHTTP.send(params);\r\n\r\n };\r\n\r\n /**\r\n * Appends script to head of document\r\n * @return Promise\r\n */\r\n core.importScript = function (scriptPath, instanceName) {\r\n\r\n return new Promise(function (resolve, reject) {\r\n\r\n const instancePrefix = 'cdx-script-';\r\n\r\n let script;\r\n\r\n /** Script is already loaded */\r\n if ( !instanceName ) {\r\n\r\n reject('Instance name is missed');\r\n\r\n } else if ( document.getElementById(instancePrefix + instanceName) ) {\r\n\r\n resolve(scriptPath);\r\n\r\n }\r\n\r\n script = document.createElement('SCRIPT');\r\n script.async = true;\r\n script.defer = true;\r\n script.id = instancePrefix + instanceName;\r\n\r\n script.onload = function () {\r\n\r\n resolve(scriptPath);\r\n\r\n };\r\n\r\n script.onerror = function () {\r\n\r\n reject(scriptPath);\r\n\r\n };\r\n\r\n script.src = scriptPath;\r\n document.head.appendChild(script);\r\n\r\n });\r\n\r\n };\r\n\r\n return core;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/core.js","/**\r\n * Codex Editor UI module\r\n *\r\n * @author Codex Team\r\n * @version 1.1\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (ui) {\r\n\r\n /**\r\n * Basic editor classnames\r\n */\r\n ui.className = {\r\n\r\n /**\r\n * @const {string} BLOCK_CLASSNAME - redactor blocks name\r\n */\r\n BLOCK_CLASSNAME : 'ce-block',\r\n\r\n /**\r\n * @const {String} wrapper for plugins content\r\n */\r\n BLOCK_CONTENT : 'ce-block__content',\r\n\r\n /**\r\n * @const {String} BLOCK_STRETCHED - makes block stretched\r\n */\r\n BLOCK_STRETCHED : 'ce-block--stretched',\r\n\r\n /**\r\n * @const {String} BLOCK_HIGHLIGHTED - adds background\r\n */\r\n BLOCK_HIGHLIGHTED : 'ce-block--focused',\r\n\r\n /**\r\n * @const {String} - highlights covered blocks\r\n */\r\n BLOCK_IN_FEED_MODE : 'ce-block--feed-mode',\r\n\r\n /**\r\n * @const {String} - for all default settings\r\n */\r\n SETTINGS_ITEM : 'ce-settings__item'\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Making main interface\r\n */\r\n ui.make = function () {\r\n\r\n var wrapper,\r\n toolbar,\r\n toolbarContent,\r\n redactor,\r\n commentsSidebar,\r\n notifications,\r\n blockButtons,\r\n blockSettings,\r\n showSettingsButton,\r\n showTrashButton,\r\n showCommentButton,\r\n toolbox,\r\n plusButton;\r\n\r\n /** Make editor wrapper */\r\n wrapper = editor.draw.wrapper();\r\n\r\n /** Append editor wrapper after initial textarea */\r\n editor.core.insertAfter(editor.nodes.textarea, wrapper);\r\n\r\n /** Append block with notifications to the document */\r\n notifications = editor.draw.alertsHolder();\r\n editor.nodes.notifications = document.body.appendChild(notifications);\r\n\r\n /** Make toolbar and content-editable redactor */\r\n toolbar = editor.draw.toolbar();\r\n toolbarContent = editor.draw.toolbarContent();\r\n plusButton = editor.draw.plusButton();\r\n showSettingsButton = editor.draw.settingsButton();\r\n showCommentButton = editor.draw.commentButton();\r\n showTrashButton = editor.toolbar.settings.makeRemoveBlockButton();\r\n blockSettings = editor.draw.blockSettings();\r\n blockButtons = editor.draw.blockButtons();\r\n toolbox = editor.draw.toolbox();\r\n redactor = editor.draw.redactor();\r\n commentsSidebar = editor.draw.commentsSidebar();\r\n\r\n /** settings */\r\n var defaultSettings = editor.draw.defaultSettings(),\r\n pluginSettings = editor.draw.pluginsSettings();\r\n\r\n /** Add default and plugins settings */\r\n blockSettings.appendChild(pluginSettings);\r\n blockSettings.appendChild(defaultSettings);\r\n\r\n /** Make blocks buttons\r\n * This block contains settings button and remove block button\r\n */\r\n blockButtons.appendChild(showSettingsButton);\r\n blockButtons.appendChild(showTrashButton);\r\n blockButtons.appendChild(showCommentButton);\r\n blockButtons.appendChild(blockSettings);\r\n\r\n /** Append plus button */\r\n toolbarContent.appendChild(plusButton);\r\n\r\n /** Appending toolbar tools */\r\n toolbarContent.appendChild(toolbox);\r\n\r\n /** Appending first-level block buttons */\r\n toolbar.appendChild(blockButtons);\r\n\r\n /** Append toolbarContent to toolbar */\r\n toolbar.appendChild(toolbarContent);\r\n\r\n wrapper.appendChild(toolbar);\r\n\r\n wrapper.appendChild(commentsSidebar);\r\n wrapper.appendChild(redactor);\r\n\r\n /** Save created ui-elements to static nodes state */\r\n editor.nodes.wrapper = wrapper;\r\n editor.nodes.toolbar = toolbar;\r\n editor.nodes.plusButton = plusButton;\r\n editor.nodes.toolbox = toolbox;\r\n editor.nodes.blockSettings = blockSettings;\r\n editor.nodes.pluginSettings = pluginSettings;\r\n editor.nodes.defaultSettings = defaultSettings;\r\n editor.nodes.showSettingsButton = showSettingsButton;\r\n editor.nodes.showTrashButton = showTrashButton;\r\n editor.nodes.showCommentButton = showCommentButton;\r\n editor.nodes.commentsSidebar = commentsSidebar;\r\n\r\n editor.nodes.redactor = redactor;\r\n\r\n /** Make container for inline toolbar */\r\n editor.ui.makeInlineToolbar();\r\n\r\n /** fill in default settings */\r\n editor.toolbar.settings.addDefaultSettings();\r\n\r\n };\r\n\r\n ui.makeInlineToolbar = function () {\r\n\r\n var container = editor.draw.inlineToolbar();\r\n\r\n /** Append to redactor new inline block */\r\n editor.nodes.inlineToolbar.wrapper = container;\r\n\r\n /** Draw toolbar buttons */\r\n editor.nodes.inlineToolbar.buttons = editor.draw.inlineToolbarButtons();\r\n\r\n /** Buttons action or settings */\r\n editor.nodes.inlineToolbar.actions = editor.draw.inlineToolbarActions();\r\n\r\n /** Append to inline toolbar buttons as part of it */\r\n editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.buttons);\r\n editor.nodes.inlineToolbar.wrapper.appendChild(editor.nodes.inlineToolbar.actions);\r\n\r\n editor.nodes.wrapper.appendChild(editor.nodes.inlineToolbar.wrapper);\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n * Append tools passed in editor.tools\r\n */\r\n ui.addTools = function () {\r\n\r\n var tool,\r\n toolName,\r\n toolButton;\r\n\r\n for ( toolName in editor.settings.tools ) {\r\n\r\n tool = editor.settings.tools[toolName];\r\n\r\n editor.tools[toolName] = tool;\r\n\r\n if (!tool.iconClassname) {\r\n\r\n editor.core.log('Toolbar icon classname missed. Tool %o skipped', 'warn', toolName);\r\n continue;\r\n\r\n }\r\n\r\n if (typeof tool.render != 'function') {\r\n\r\n editor.core.log('render method missed. Tool %o skipped', 'warn', toolName);\r\n continue;\r\n\r\n }\r\n\r\n if (!tool.displayInToolbox) {\r\n\r\n continue;\r\n\r\n } else {\r\n\r\n /** if tools is for toolbox */\r\n toolButton = editor.draw.toolbarButton(toolName, tool.iconClassname);\r\n\r\n editor.nodes.toolbox.appendChild(toolButton);\r\n\r\n editor.nodes.toolbarButtons[toolName] = toolButton;\r\n\r\n }\r\n\r\n }\r\n\r\n /**\r\n * Add inline toolbar tools\r\n */\r\n editor.ui.addInlineToolbarTools();\r\n\r\n\r\n };\r\n\r\n ui.addInlineToolbarTools = function () {\r\n\r\n var tools = {\r\n\r\n bold: {\r\n icon : 'ce-icon-bold',\r\n command : 'bold'\r\n },\r\n\r\n italic: {\r\n icon : 'ce-icon-italic',\r\n command : 'italic'\r\n },\r\n\r\n underline: {\r\n icon : 'ce-icon-underline',\r\n command : 'underline'\r\n },\r\n\r\n link: {\r\n icon : 'ce-icon-link',\r\n command : 'createLink'\r\n }\r\n };\r\n\r\n var toolButton,\r\n tool;\r\n\r\n for(var name in tools) {\r\n\r\n tool = tools[name];\r\n\r\n toolButton = editor.draw.toolbarButtonInline(name, tool.icon);\r\n\r\n editor.nodes.inlineToolbar.buttons.appendChild(toolButton);\r\n /**\r\n * Add callbacks to this buttons\r\n */\r\n editor.ui.setInlineToolbarButtonBehaviour(toolButton, tool.command);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n * Bind editor UI events\r\n */\r\n ui.bindEvents = function () {\r\n\r\n editor.core.log('ui.bindEvents fired', 'info');\r\n\r\n // window.addEventListener('error', function (errorMsg, url, lineNumber) {\r\n // editor.notifications.errorThrown(errorMsg, event);\r\n // }, false );\r\n\r\n /** All keydowns on Document */\r\n document.addEventListener('keydown', editor.callback.globalKeydown, false );\r\n\r\n /** All keydowns on Redactor zone */\r\n editor.nodes.redactor.addEventListener('keydown', editor.callback.redactorKeyDown, false);\r\n\r\n /** All keydowns on Document */\r\n document.addEventListener('keyup', editor.callback.globalKeyup, false );\r\n\r\n /**\r\n * Mouse click to radactor\r\n */\r\n editor.nodes.redactor.addEventListener('click', editor.callback.redactorClicked, false );\r\n\r\n /**\r\n * Clicks to the Plus button\r\n */\r\n editor.nodes.plusButton.addEventListener('click', editor.callback.plusButtonClicked, false);\r\n\r\n /**\r\n * Clicks to SETTINGS button in toolbar\r\n */\r\n editor.nodes.showSettingsButton.addEventListener('click', editor.callback.showSettingsButtonClicked, false );\r\n\r\n /**\r\n * Clicks to COMMENT button in toolbar\r\n */\r\n editor.nodes.showCommentButton.addEventListener('click', editor.callback.showCommentButtonClicked, false );\r\n\r\n /**\r\n * @deprecated ( but now in use for syncronization );\r\n * Any redactor changes: keyboard input, mouse cut/paste, drag-n-drop text\r\n */\r\n // editor.nodes.redactor.addEventListener('input', editor.callback.redactorInputEvent, false );\r\n\r\n /** Bind click listeners on toolbar buttons */\r\n for (var button in editor.nodes.toolbarButtons) {\r\n\r\n editor.nodes.toolbarButtons[button].addEventListener('click', editor.callback.toolbarButtonClicked, false);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Initialize plugins before using\r\n * Ex. Load scripts or call some internal methods\r\n * @return Promise\r\n */\r\n ui.preparePlugins = function () {\r\n\r\n return new Promise(function (resolve, reject) {\r\n\r\n let pluginName,\r\n plugin;\r\n\r\n for ( pluginName in editor.tools ) {\r\n\r\n plugin = editor.tools[pluginName];\r\n\r\n if (typeof plugin.prepare != 'function') {\r\n\r\n continue;\r\n\r\n }\r\n\r\n plugin.prepare(plugin.config || {}).then(function () {\r\n\r\n resolve();\r\n\r\n }).catch(function (error) {\r\n\r\n reject(error);\r\n\r\n });\r\n\r\n }\r\n\r\n });\r\n\r\n };\r\n\r\n ui.addBlockHandlers = function (block) {\r\n\r\n if (!block) return;\r\n\r\n /**\r\n * Block keydowns\r\n */\r\n block.addEventListener('keydown', editor.callback.blockKeydown, false);\r\n\r\n /**\r\n * Pasting content from another source\r\n * We have two type of sanitization\r\n * First - uses deep-first search algorithm to get sub nodes,\r\n * sanitizes whole Block_content and replaces cleared nodes\r\n * This method is deprecated\r\n * Method is used in editor.callback.blockPaste(event)\r\n *\r\n * Secont - uses Mutation observer.\r\n * Observer \"observe\" DOM changes and send changings to callback.\r\n * Callback gets changed node, not whole Block_content.\r\n * Inserted or changed node, which we've gotten have been cleared and replaced with diry node\r\n *\r\n * Method is used in editor.callback.blockPasteViaSanitize(event)\r\n *\r\n * @uses html-janitor\r\n * @example editor.callback.blockPasteViaSanitize(event), the second method.\r\n *\r\n */\r\n block.addEventListener('paste', editor.callback.blockPasteCallback, false);\r\n\r\n block.addEventListener('mouseup', editor.toolbar.inline.show, false);\r\n\r\n };\r\n\r\n /** getting all contenteditable elements */\r\n ui.saveInputs = function () {\r\n\r\n var redactor = editor.nodes.redactor;\r\n\r\n /** Save all inputs in global variable state */\r\n editor.state.inputs = redactor.querySelectorAll('[contenteditable], input');\r\n\r\n };\r\n\r\n /**\r\n * Adds first initial block on empty redactor\r\n */\r\n ui.addInitialBlock = function () {\r\n\r\n var initialBlockType = editor.settings.initialBlockPlugin,\r\n initialBlock;\r\n\r\n if ( !editor.tools[initialBlockType] ) {\r\n\r\n editor.core.log('Plugin %o was not implemented and can\\'t be used as initial block', 'warn', initialBlockType);\r\n return;\r\n\r\n }\r\n\r\n initialBlock = editor.tools[initialBlockType].render();\r\n\r\n initialBlock.setAttribute('data-placeholder', 'Расскажите свою историю...');\r\n\r\n editor.content.insertBlock({\r\n type : initialBlockType,\r\n block : initialBlock\r\n });\r\n\r\n editor.content.workingNodeChanged(initialBlock);\r\n\r\n };\r\n\r\n ui.setInlineToolbarButtonBehaviour = function (button, type) {\r\n\r\n button.addEventListener('mousedown', function (event) {\r\n\r\n editor.toolbar.inline.toolClicked(event, type);\r\n\r\n }, false);\r\n\r\n };\r\n\r\n return ui;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/ui.js","/**\r\n *\r\n * Codex.Editor Transport Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (transport) {\r\n\r\n transport.input = null;\r\n\r\n /**\r\n * @property {Object} arguments - keep plugin settings and defined callbacks\r\n */\r\n transport.arguments = null;\r\n\r\n transport.prepare = function () {\r\n\r\n var input = document.createElement('INPUT');\r\n\r\n input.type = 'file';\r\n input.addEventListener('change', editor.transport.fileSelected);\r\n\r\n editor.transport.input = input;\r\n\r\n };\r\n\r\n /** Clear input when files is uploaded */\r\n transport.clearInput = function () {\r\n\r\n /** Remove old input */\r\n this.input = null;\r\n\r\n /** Prepare new one */\r\n this.prepare();\r\n\r\n };\r\n\r\n /**\r\n * Callback for file selection\r\n * @param {Event} event\r\n */\r\n transport.fileSelected = function () {\r\n\r\n var input = this,\r\n files = input.files,\r\n formdData = new FormData();\r\n\r\n formdData.append('files', files[0], files[0].name);\r\n\r\n editor.transport.ajax({\r\n data : formdData,\r\n beforeSend : editor.transport.arguments.beforeSend,\r\n success : editor.transport.arguments.success,\r\n error : editor.transport.arguments.error\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Use plugin callbacks\r\n * @protected\r\n */\r\n transport.selectAndUpload = function (args) {\r\n\r\n this.arguments = args;\r\n this.input.click();\r\n\r\n };\r\n\r\n /**\r\n * Ajax requests module\r\n * @todo use core.ajax\r\n */\r\n transport.ajax = function (params) {\r\n\r\n var xhr = new XMLHttpRequest(),\r\n beforeSend = typeof params.beforeSend == 'function' ? params.beforeSend : function () {},\r\n success = typeof params.success == 'function' ? params.success : function () {},\r\n error = typeof params.error == 'function' ? params.error : function () {};\r\n\r\n beforeSend();\r\n\r\n xhr.open('POST', editor.settings.uploadImagesUrl, true);\r\n\r\n xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');\r\n\r\n xhr.onload = function () {\r\n\r\n if (xhr.status === 200) {\r\n\r\n success(xhr.responseText);\r\n\r\n } else {\r\n\r\n editor.core.log('request error: %o', xhr);\r\n error();\r\n\r\n }\r\n\r\n };\r\n\r\n xhr.send(params.data);\r\n this.clearInput();\r\n\r\n };\r\n\r\n return transport;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/transport.js","/**\r\n * Codex Editor Renderer Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (renderer) {\r\n\r\n /**\r\n * Asyncronously parses input JSON to redactor blocks\r\n */\r\n renderer.makeBlocksFromData = function () {\r\n\r\n /**\r\n * If redactor is empty, add first paragraph to start writing\r\n */\r\n if (!editor.state.blocks.items.length) {\r\n\r\n editor.ui.addInitialBlock();\r\n return;\r\n\r\n }\r\n\r\n Promise.resolve()\r\n\r\n /** First, get JSON from state */\r\n .then(function () {\r\n\r\n return editor.state.blocks;\r\n\r\n })\r\n\r\n /** Then, start to iterate they */\r\n .then(editor.renderer.appendBlocks)\r\n\r\n /** Write log if something goes wrong */\r\n .catch(function (error) {\r\n\r\n editor.core.log('Error while parsing JSON: %o', 'error', error);\r\n\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Parses JSON to blocks\r\n * @param {object} data\r\n * @return Primise -> nodeList\r\n */\r\n renderer.appendBlocks = function (data) {\r\n\r\n var blocks = data.items;\r\n\r\n /**\r\n * Sequence of one-by-one blocks appending\r\n * Uses to save blocks order after async-handler\r\n */\r\n var nodeSequence = Promise.resolve();\r\n\r\n for (var index = 0; index < blocks.length ; index++ ) {\r\n\r\n /** Add node to sequence at specified index */\r\n editor.renderer.appendNodeAtIndex(nodeSequence, blocks, index);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Append node at specified index\r\n */\r\n renderer.appendNodeAtIndex = function (nodeSequence, blocks, index) {\r\n\r\n /** We need to append node to sequence */\r\n nodeSequence\r\n\r\n /** first, get node async-aware */\r\n .then(function () {\r\n\r\n return editor.renderer.getNodeAsync(blocks, index);\r\n\r\n })\r\n\r\n /**\r\n * second, compose editor-block from JSON object\r\n */\r\n .then(editor.renderer.createBlockFromData)\r\n\r\n /**\r\n * now insert block to redactor\r\n */\r\n .then(function (blockData) {\r\n\r\n /**\r\n * blockData has 'block', 'type' and 'stretched' information\r\n */\r\n editor.content.insertBlock(blockData);\r\n\r\n /** Pass created block to next step */\r\n return blockData.block;\r\n\r\n })\r\n\r\n /** Log if something wrong with node */\r\n .catch(function (error) {\r\n\r\n editor.core.log('Node skipped while parsing because %o', 'error', error);\r\n\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Asynchronously returns block data from blocksList by index\r\n * @return Promise to node\r\n */\r\n renderer.getNodeAsync = function (blocksList, index) {\r\n\r\n return Promise.resolve().then(function () {\r\n\r\n return blocksList[index];\r\n\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Creates editor block by JSON-data\r\n *\r\n * @uses render method of each plugin\r\n *\r\n * @param {object} blockData looks like\r\n * { header : {\r\n * text: '',\r\n * type: 'H3', ...\r\n * }\r\n * }\r\n * @return {object} with type and Element\r\n */\r\n renderer.createBlockFromData = function (blockData) {\r\n\r\n /** New parser */\r\n var pluginName = blockData.type,\r\n cover = blockData.cover;\r\n\r\n /** Get first key of object that stores plugin name */\r\n // for (var pluginName in blockData) break;\r\n\r\n /** Check for plugin existance */\r\n if (!editor.tools[pluginName]) {\r\n\r\n throw Error(`Plugin «${pluginName}» not found`);\r\n\r\n }\r\n\r\n /** Check for plugin having render method */\r\n if (typeof editor.tools[pluginName].render != 'function') {\r\n\r\n throw Error(`Plugin «${pluginName}» must have «render» method`);\r\n\r\n }\r\n\r\n /** New Parser */\r\n var block = editor.tools[pluginName].render(blockData.data);\r\n\r\n /** is first-level block stretched */\r\n var stretched = editor.tools[pluginName].isStretched || false;\r\n\r\n /** Retrun type and block */\r\n return {\r\n type : pluginName,\r\n block : block,\r\n stretched : stretched,\r\n cover : cover\r\n };\r\n\r\n };\r\n\r\n return renderer;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/renderer.js","/**\r\n * Codex Editor Saver\r\n *\r\n * @author Codex Team\r\n * @version 1.0.2\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (saver) {\r\n\r\n /**\r\n * Saves blocks\r\n * @private\r\n */\r\n saver.saveBlocks = function () {\r\n\r\n /** Save html content of redactor to memory */\r\n editor.state.html = editor.nodes.redactor.innerHTML;\r\n\r\n /** Empty jsonOutput state */\r\n editor.state.jsonOutput = [];\r\n\r\n Promise.resolve()\r\n\r\n .then(function () {\r\n\r\n return editor.nodes.redactor.childNodes;\r\n\r\n })\r\n /** Making a sequence from separate blocks */\r\n .then(editor.saver.makeQueue)\r\n\r\n .then(function () {\r\n // editor.nodes.textarea.innerHTML = editor.state.html;\r\n })\r\n\r\n .catch( function (error) {\r\n\r\n editor.core.log(error);\r\n\r\n });\r\n\r\n };\r\n\r\n saver.makeQueue = function (blocks) {\r\n\r\n var queue = Promise.resolve();\r\n\r\n for(var index = 0; index < blocks.length; index++) {\r\n\r\n /** Add node to sequence at specified index */\r\n editor.saver.getBlockData(queue, blocks, index);\r\n\r\n }\r\n\r\n };\r\n\r\n /** Gets every block and makes From Data */\r\n saver.getBlockData = function (queue, blocks, index) {\r\n\r\n queue.then(function () {\r\n\r\n return editor.saver.getNodeAsync(blocks, index);\r\n\r\n })\r\n\r\n .then(editor.saver.makeFormDataFromBlocks);\r\n\r\n };\r\n\r\n\r\n /**\r\n * Asynchronously returns block data from blocksList by index\r\n * @return Promise to node\r\n */\r\n saver.getNodeAsync = function (blocksList, index) {\r\n\r\n return Promise.resolve().then(function () {\r\n\r\n return blocksList[index];\r\n\r\n });\r\n\r\n };\r\n\r\n saver.makeFormDataFromBlocks = function (block) {\r\n\r\n var pluginName = block.dataset.tool,\r\n id = block.dataset.id;\r\n\r\n /** Check for plugin existance */\r\n if (!editor.tools[pluginName]) {\r\n\r\n throw Error(`Plugin «${pluginName}» not found`);\r\n\r\n }\r\n\r\n /** Check for plugin having render method */\r\n if (typeof editor.tools[pluginName].save != 'function') {\r\n\r\n throw Error(`Plugin «${pluginName}» must have save method`);\r\n\r\n }\r\n\r\n /** Result saver */\r\n var blockContent = block.childNodes[0],\r\n pluginsContent = blockContent.childNodes[0],\r\n savedData = editor.tools[pluginName].save(pluginsContent),\r\n output;\r\n\r\n\r\n output = {\r\n id : id,\r\n type: pluginName,\r\n data: savedData\r\n };\r\n\r\n if (editor.tools[pluginName].validate) {\r\n\r\n var result = editor.tools[pluginName].validate(savedData);\r\n\r\n /**\r\n * Do not allow invalid data\r\n */\r\n if (!result)\r\n return;\r\n\r\n }\r\n\r\n /** Marks Blocks that will be in main page */\r\n output.cover = block.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);\r\n\r\n editor.state.jsonOutput.push(output);\r\n\r\n };\r\n\r\n saver.saveComments = function () {\r\n\r\n var fields = editor.nodes.commentsSidebar.querySelectorAll('.ce-comments-field');\r\n\r\n for (var i = 0 ; i < fields.length; i++) {\r\n\r\n var comments = fields[i].querySelectorAll('.ce-comment'),\r\n commentsData = [];\r\n\r\n for (var j = 0; j < comments.length; j++) {\r\n\r\n var text = comments[j].querySelector('.ce-comment__text'),\r\n time = comments[j].querySelector('.ce-comment__time');\r\n\r\n if (!text) continue;\r\n\r\n var comment = {\r\n edited : comments[j].dataset.edited,\r\n text : text.textContent,\r\n time : time.textContent\r\n };\r\n\r\n\r\n commentsData.push(comment);\r\n\r\n }\r\n\r\n if (!commentsData.length) continue;\r\n\r\n var fieldData = {\r\n blockId : fields[i].dataset.blockId,\r\n comments : commentsData\r\n };\r\n\r\n editor.state.comments.push(fieldData);\r\n\r\n }\r\n\r\n };\r\n\r\n return saver;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/saver.js","/**\r\n * Codex Editor Content Module\r\n * Works with DOM\r\n *\r\n * @author Codex Team\r\n * @version 1.3.11\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (content) {\r\n\r\n /**\r\n * Links to current active block\r\n * @type {null | Element}\r\n */\r\n content.currentNode = null;\r\n\r\n /**\r\n * clicked in redactor area\r\n * @type {null | Boolean}\r\n */\r\n content.editorAreaHightlighted = null;\r\n\r\n /**\r\n * Synchronizes redactor with original textarea\r\n */\r\n content.sync = function () {\r\n\r\n editor.core.log('syncing...');\r\n\r\n /**\r\n * Save redactor content to editor.state\r\n */\r\n editor.state.html = editor.nodes.redactor.innerHTML;\r\n\r\n };\r\n\r\n /**\r\n * @deprecated\r\n */\r\n content.getNodeFocused = function () {\r\n\r\n var selection = window.getSelection(),\r\n focused;\r\n\r\n if (selection.anchorNode === null) {\r\n\r\n return null;\r\n\r\n }\r\n\r\n if ( selection.anchorNode.nodeType == editor.core.nodeTypes.TAG ) {\r\n\r\n focused = selection.anchorNode;\r\n\r\n } else {\r\n\r\n focused = selection.focusNode.parentElement;\r\n\r\n }\r\n\r\n if ( !editor.parser.isFirstLevelBlock(focused) ) {\r\n\r\n /** Iterate with parent nodes to find first-level*/\r\n var parent = focused.parentNode;\r\n\r\n while (parent && !editor.parser.isFirstLevelBlock(parent)) {\r\n\r\n parent = parent.parentNode;\r\n\r\n }\r\n\r\n focused = parent;\r\n\r\n }\r\n\r\n if (focused != editor.nodes.redactor) {\r\n\r\n return focused;\r\n\r\n }\r\n\r\n return null;\r\n\r\n };\r\n\r\n /**\r\n * Appends background to the block\r\n */\r\n content.markBlock = function () {\r\n\r\n editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED);\r\n\r\n };\r\n\r\n /**\r\n * Clear background\r\n */\r\n content.clearMark = function () {\r\n\r\n if (editor.content.currentNode) {\r\n\r\n editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Finds first-level block\r\n * @param {Element} node - selected or clicked in redactors area node\r\n */\r\n content.getFirstLevelBlock = function (node) {\r\n\r\n if (!editor.core.isDomNode(node)) {\r\n\r\n node = node.parentNode;\r\n\r\n }\r\n\r\n if (node === editor.nodes.redactor || node === document.body) {\r\n\r\n return null;\r\n\r\n } else {\r\n\r\n while(!node.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\r\n\r\n node = node.parentNode;\r\n\r\n }\r\n\r\n return node;\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Trigger this event when working node changed\r\n * @param {Element} targetNode - first-level of this node will be current\r\n * If targetNode is first-level then we set it as current else we look for parents to find first-level\r\n */\r\n content.workingNodeChanged = function (targetNode) {\r\n\r\n /** Clear background from previous marked block before we change */\r\n editor.content.clearMark();\r\n\r\n if (!targetNode) {\r\n\r\n return;\r\n\r\n }\r\n\r\n this.currentNode = this.getFirstLevelBlock(targetNode);\r\n\r\n };\r\n\r\n /**\r\n * Replaces one redactor block with another\r\n * @protected\r\n * @param {Element} targetBlock - block to replace. Mostly currentNode.\r\n * @param {Element} newBlock\r\n * @param {string} newBlockType - type of new block; we need to store it to data-attribute\r\n *\r\n * [!] Function does not saves old block content.\r\n * You can get it manually and pass with newBlock.innerHTML\r\n */\r\n content.replaceBlock = function (targetBlock, newBlock) {\r\n\r\n if (!targetBlock || !newBlock) {\r\n\r\n editor.core.log('replaceBlock: missed params');\r\n return;\r\n\r\n }\r\n\r\n /** If target-block is not a frist-level block, then we iterate parents to find it */\r\n while(!targetBlock.classList.contains(editor.ui.className.BLOCK_CLASSNAME)) {\r\n\r\n targetBlock = targetBlock.parentNode;\r\n\r\n }\r\n\r\n /**\r\n * Check is this block was in feed\r\n * If true, than set switched block also covered\r\n */\r\n if (targetBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE)) {\r\n\r\n newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);\r\n\r\n }\r\n\r\n /** Saving id */\r\n newBlock.dataset.id = targetBlock.dataset.id;\r\n\r\n /** Replacing */\r\n editor.nodes.redactor.replaceChild(newBlock, targetBlock);\r\n\r\n /**\r\n * Set new node as current\r\n */\r\n editor.content.workingNodeChanged(newBlock);\r\n\r\n /**\r\n * Add block handlers\r\n */\r\n editor.ui.addBlockHandlers(newBlock);\r\n\r\n /**\r\n * Save changes\r\n */\r\n editor.ui.saveInputs();\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Inserts new block to redactor\r\n * Wrapps block into a DIV with BLOCK_CLASSNAME class\r\n *\r\n * @param blockData {object}\r\n * @param blockData.block {Element} element with block content\r\n * @param blockData.type {string} block plugin\r\n * @param needPlaceCaret {bool} pass true to set caret in new block\r\n *\r\n */\r\n content.insertBlock = function ( blockData, needPlaceCaret ) {\r\n\r\n var workingBlock = editor.content.currentNode,\r\n newBlockContent = blockData.block,\r\n blockType = blockData.type,\r\n cover = blockData.cover,\r\n isStretched = blockData.stretched;\r\n\r\n var newBlock = editor.content.composeNewBlock(newBlockContent, blockType, isStretched);\r\n\r\n if (cover === true) {\r\n\r\n newBlock.classList.add(editor.ui.className.BLOCK_IN_FEED_MODE);\r\n\r\n }\r\n\r\n if (workingBlock) {\r\n\r\n editor.core.insertAfter(workingBlock, newBlock);\r\n\r\n } else {\r\n\r\n /**\r\n * If redactor is empty, append as first child\r\n */\r\n editor.nodes.redactor.appendChild(newBlock);\r\n\r\n }\r\n\r\n /**\r\n * Block handler\r\n */\r\n editor.ui.addBlockHandlers(newBlock);\r\n\r\n /**\r\n * Set new node as current\r\n */\r\n editor.content.workingNodeChanged(newBlock);\r\n\r\n /**\r\n * Save changes\r\n */\r\n editor.ui.saveInputs();\r\n\r\n\r\n if ( needPlaceCaret ) {\r\n\r\n /**\r\n * If we don't know input index then we set default value -1\r\n */\r\n var currentInputIndex = editor.caret.getCurrentInputIndex() || -1;\r\n\r\n\r\n if (currentInputIndex == -1) {\r\n\r\n\r\n var editableElement = newBlock.querySelector('[contenteditable]'),\r\n emptyText = document.createTextNode('');\r\n\r\n editableElement.appendChild(emptyText);\r\n editor.caret.set(editableElement, 0, 0);\r\n\r\n editor.toolbar.move();\r\n editor.toolbar.showPlusButton();\r\n\r\n\r\n } else {\r\n\r\n if (currentInputIndex === editor.state.inputs.length - 1)\r\n return;\r\n\r\n /** Timeout for browsers execution */\r\n window.setTimeout(function () {\r\n\r\n /** Setting to the new input */\r\n editor.caret.setToNextBlock(currentInputIndex);\r\n editor.toolbar.move();\r\n editor.toolbar.open();\r\n\r\n }, 10);\r\n\r\n }\r\n\r\n }\r\n\r\n /**\r\n * Block is inserted, wait for new click that defined focusing on editors area\r\n * @type {boolean}\r\n */\r\n content.editorAreaHightlighted = false;\r\n\r\n };\r\n\r\n /**\r\n * Replaces blocks with saving content\r\n * @protected\r\n * @param {Element} noteToReplace\r\n * @param {Element} newNode\r\n * @param {Element} blockType\r\n */\r\n content.switchBlock = function (blockToReplace, newBlock, tool) {\r\n\r\n var newBlockComposed = editor.content.composeNewBlock(newBlock, tool);\r\n\r\n /** Replacing */\r\n editor.content.replaceBlock(blockToReplace, newBlockComposed);\r\n\r\n /** Save new Inputs when block is changed */\r\n editor.ui.saveInputs();\r\n\r\n };\r\n\r\n /**\r\n * Iterates between child noted and looking for #text node on deepest level\r\n * @private\r\n * @param {Element} block - node where find\r\n * @param {int} postiton - starting postion\r\n * Example: childNodex.length to find from the end\r\n * or 0 to find from the start\r\n * @return {Text} block\r\n * @uses DFS\r\n */\r\n content.getDeepestTextNodeFromPosition = function (block, position) {\r\n\r\n /**\r\n * Clear Block from empty and useless spaces with trim.\r\n * Such nodes we should remove\r\n */\r\n var blockChilds = block.childNodes,\r\n index,\r\n node,\r\n text;\r\n\r\n for(index = 0; index < blockChilds.length; index++) {\r\n\r\n node = blockChilds[index];\r\n\r\n if (node.nodeType == editor.core.nodeTypes.TEXT) {\r\n\r\n text = node.textContent.trim();\r\n\r\n /** Text is empty. We should remove this child from node before we start DFS\r\n * decrease the quantity of childs.\r\n */\r\n if (text === '') {\r\n\r\n block.removeChild(node);\r\n position--;\r\n\r\n }\r\n\r\n }\r\n\r\n }\r\n\r\n if (block.childNodes.length === 0) {\r\n\r\n return document.createTextNode('');\r\n\r\n }\r\n\r\n /** Setting default position when we deleted all empty nodes */\r\n if ( position < 0 )\r\n position = 1;\r\n\r\n var lookingFromStart = false;\r\n\r\n /** For looking from START */\r\n if (position === 0) {\r\n\r\n lookingFromStart = true;\r\n position = 1;\r\n\r\n }\r\n\r\n while ( position ) {\r\n\r\n /** initial verticle of node. */\r\n if ( lookingFromStart ) {\r\n\r\n block = block.childNodes[0];\r\n\r\n } else {\r\n\r\n block = block.childNodes[position - 1];\r\n\r\n }\r\n\r\n if ( block.nodeType == editor.core.nodeTypes.TAG ) {\r\n\r\n position = block.childNodes.length;\r\n\r\n } else if (block.nodeType == editor.core.nodeTypes.TEXT ) {\r\n\r\n position = 0;\r\n\r\n }\r\n\r\n }\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n */\r\n content.composeNewBlock = function (block, tool, isStretched) {\r\n\r\n var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}),\r\n blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {});\r\n\r\n blockContent.appendChild(block);\r\n newBlock.appendChild(blockContent);\r\n\r\n if (isStretched) {\r\n\r\n blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED);\r\n\r\n }\r\n\r\n newBlock.dataset.tool = tool;\r\n newBlock.dataset.id = +new Date();\r\n\r\n return newBlock;\r\n\r\n };\r\n\r\n /**\r\n * Returns Range object of current selection\r\n */\r\n content.getRange = function () {\r\n\r\n var selection = window.getSelection().getRangeAt(0);\r\n\r\n return selection;\r\n\r\n };\r\n\r\n /**\r\n * Divides block in two blocks (after and before caret)\r\n * @private\r\n * @param {Int} inputIndex - target input index\r\n */\r\n content.splitBlock = function (inputIndex) {\r\n\r\n var selection = window.getSelection(),\r\n anchorNode = selection.anchorNode,\r\n anchorNodeText = anchorNode.textContent,\r\n caretOffset = selection.anchorOffset,\r\n textBeforeCaret,\r\n textNodeBeforeCaret,\r\n textAfterCaret,\r\n textNodeAfterCaret;\r\n\r\n var currentBlock = editor.content.currentNode.querySelector('[contentEditable]');\r\n\r\n\r\n textBeforeCaret = anchorNodeText.substring(0, caretOffset);\r\n textAfterCaret = anchorNodeText.substring(caretOffset);\r\n\r\n textNodeBeforeCaret = document.createTextNode(textBeforeCaret);\r\n\r\n if (textAfterCaret) {\r\n\r\n textNodeAfterCaret = document.createTextNode(textAfterCaret);\r\n\r\n }\r\n\r\n var previousChilds = [],\r\n nextChilds = [],\r\n reachedCurrent = false;\r\n\r\n if (textNodeAfterCaret) {\r\n\r\n nextChilds.push(textNodeAfterCaret);\r\n\r\n }\r\n\r\n for ( var i = 0, child; !!(child = currentBlock.childNodes[i]); i++) {\r\n\r\n if ( child != anchorNode ) {\r\n\r\n if ( !reachedCurrent ) {\r\n\r\n previousChilds.push(child);\r\n\r\n } else {\r\n\r\n nextChilds.push(child);\r\n\r\n }\r\n\r\n } else {\r\n\r\n reachedCurrent = true;\r\n\r\n }\r\n\r\n }\r\n\r\n /** Clear current input */\r\n editor.state.inputs[inputIndex].innerHTML = '';\r\n\r\n /**\r\n * Append all childs founded before anchorNode\r\n */\r\n var previousChildsLength = previousChilds.length;\r\n\r\n for(i = 0; i < previousChildsLength; i++) {\r\n\r\n editor.state.inputs[inputIndex].appendChild(previousChilds[i]);\r\n\r\n }\r\n\r\n editor.state.inputs[inputIndex].appendChild(textNodeBeforeCaret);\r\n\r\n /**\r\n * Append text node which is after caret\r\n */\r\n var nextChildsLength = nextChilds.length,\r\n newNode = document.createElement('div');\r\n\r\n for(i = 0; i < nextChildsLength; i++) {\r\n\r\n newNode.appendChild(nextChilds[i]);\r\n\r\n }\r\n\r\n newNode = newNode.innerHTML;\r\n\r\n /** This type of block creates when enter is pressed */\r\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\r\n\r\n /**\r\n * Make new paragraph with text after caret\r\n */\r\n editor.content.insertBlock({\r\n type : NEW_BLOCK_TYPE,\r\n block : editor.tools[NEW_BLOCK_TYPE].render({\r\n text : newNode\r\n })\r\n }, true );\r\n\r\n };\r\n\r\n /**\r\n * Merges two blocks — current and target\r\n * If target index is not exist, then previous will be as target\r\n */\r\n content.mergeBlocks = function (currentInputIndex, targetInputIndex) {\r\n\r\n /** If current input index is zero, then prevent method execution */\r\n if (currentInputIndex === 0) {\r\n\r\n return;\r\n\r\n }\r\n\r\n var targetInput,\r\n currentInputContent = editor.state.inputs[currentInputIndex].innerHTML;\r\n\r\n if (!targetInputIndex) {\r\n\r\n targetInput = editor.state.inputs[currentInputIndex - 1];\r\n\r\n } else {\r\n\r\n targetInput = editor.state.inputs[targetInputIndex];\r\n\r\n }\r\n\r\n targetInput.innerHTML += currentInputContent;\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Callback for HTML Mutations\r\n * @param {Array} mutation - Mutation Record\r\n */\r\n content.paste = function (mutation) {\r\n\r\n var workingNode = editor.content.currentNode,\r\n tool = workingNode.dataset.tool;\r\n\r\n if (editor.tools[tool].allowedToPaste) {\r\n\r\n editor.content.sanitize.call(this, mutation.target);\r\n\r\n } else {\r\n\r\n editor.content.pasteTextContent(mutation.addedNodes);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * gets only text/plain content of node\r\n * @param {Element} target - HTML node\r\n */\r\n content.pasteTextContent = function (nodes) {\r\n\r\n var node = nodes[0],\r\n textNode;\r\n\r\n if (!node) {\r\n\r\n return;\r\n\r\n }\r\n\r\n if (node.nodeType == editor.core.nodeTypes.TEXT) {\r\n\r\n textNode = document.createTextNode(node);\r\n\r\n } else {\r\n\r\n textNode = document.createTextNode(node.textContent);\r\n\r\n }\r\n\r\n if (editor.core.isDomNode(node)) {\r\n\r\n node.parentNode.replaceChild(textNode, node);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Sanitizes HTML content\r\n * @param {Element} target - inserted element\r\n * @uses Sanitize library html-janitor\r\n */\r\n content.sanitize = function (target) {\r\n\r\n if (!target) {\r\n\r\n return;\r\n\r\n }\r\n\r\n var node = target[0];\r\n\r\n if (!node) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * Disconnect Observer\r\n * hierarchy of function calls inherits context of observer\r\n */\r\n this.disconnect();\r\n\r\n /**\r\n * Don't sanitize text node\r\n */\r\n if (node.nodeType == editor.core.nodeTypes.TEXT) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * Clear dirty content\r\n */\r\n var cleaner = editor.sanitizer.init(editor.satinizer.Config.BASIC),\r\n clean = cleaner.clean(target.outerHTML);\r\n\r\n var div = editor.draw.node('DIV', [], { innerHTML: clean });\r\n\r\n node.replaceWith(div.childNodes[0]);\r\n\r\n\r\n };\r\n\r\n /**\r\n * Iterates all right siblings and parents, which has right siblings\r\n * while it does not reached the first-level block\r\n *\r\n * @param {Element} node\r\n * @return {boolean}\r\n */\r\n content.isLastNode = function (node) {\r\n\r\n // console.log('погнали перебор родителей');\r\n\r\n var allChecked = false;\r\n\r\n while ( !allChecked ) {\r\n\r\n // console.log('Смотрим на %o', node);\r\n // console.log('Проверим, пустые ли соседи справа');\r\n\r\n if ( !allSiblingsEmpty_(node) ) {\r\n\r\n // console.log('Есть непустые соседи. Узел не последний. Выходим.');\r\n return false;\r\n\r\n }\r\n\r\n node = node.parentNode;\r\n\r\n /**\r\n * Проверяем родителей до тех пор, пока не найдем блок первого уровня\r\n */\r\n if ( node.classList.contains(editor.ui.className.BLOCK_CONTENT) ) {\r\n\r\n allChecked = true;\r\n\r\n }\r\n\r\n }\r\n\r\n return true;\r\n\r\n };\r\n\r\n /**\r\n * Checks if all element right siblings is empty\r\n * @param node\r\n */\r\n var allSiblingsEmpty_ = function (node) {\r\n\r\n /**\r\n * Нужно убедиться, что после пустого соседа ничего нет\r\n */\r\n var sibling = node.nextSibling;\r\n\r\n while ( sibling ) {\r\n\r\n if (sibling.textContent.length) {\r\n\r\n return false;\r\n\r\n }\r\n\r\n sibling = sibling.nextSibling;\r\n\r\n }\r\n\r\n return true;\r\n\r\n };\r\n\r\n /**\r\n * @public\r\n *\r\n * @param [String] htmlString - html content as string\r\n * @return {string} - html content as string\r\n */\r\n content.wrapTextWithParagraphs = function (htmlString) {\r\n\r\n var wrapper = document.createElement('DIV'),\r\n newWrapper = document.createElement('DIV'),\r\n i,\r\n paragraph,\r\n firstLevelBlocks = ['DIV', 'P'],\r\n blockTyped,\r\n node;\r\n\r\n /**\r\n * Make HTML Element to Wrap Text\r\n * It allows us to work with input data as HTML content\r\n */\r\n wrapper.innerHTML = htmlString;\r\n paragraph = document.createElement('P');\r\n\r\n for (i = 0; i < wrapper.childNodes.length; i++) {\r\n\r\n node = wrapper.childNodes[i];\r\n\r\n blockTyped = firstLevelBlocks.indexOf(node.tagName) != -1;\r\n\r\n /**\r\n * If node is first-levet\r\n * we add this node to our new wrapper\r\n */\r\n if ( blockTyped ) {\r\n\r\n /**\r\n * If we had splitted inline nodes to paragraph before\r\n */\r\n if ( paragraph.childNodes.length ) {\r\n\r\n newWrapper.appendChild(paragraph.cloneNode(true));\r\n\r\n /** empty paragraph */\r\n paragraph = null;\r\n paragraph = document.createElement('P');\r\n\r\n }\r\n\r\n newWrapper.appendChild(node.cloneNode(true));\r\n\r\n } else {\r\n\r\n /** Collect all inline nodes to one as paragraph */\r\n paragraph.appendChild(node.cloneNode(true));\r\n\r\n /** if node is last we should append this node to paragraph and paragraph to new wrapper */\r\n if ( i == wrapper.childNodes.length - 1 ) {\r\n\r\n newWrapper.appendChild(paragraph.cloneNode(true));\r\n\r\n }\r\n\r\n }\r\n\r\n }\r\n\r\n return newWrapper.innerHTML;\r\n\r\n };\r\n\r\n return content;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/content.js","/**\r\n * Codex Editor toolbar module\r\n *\r\n * Contains:\r\n * - Inline toolbox\r\n * - Toolbox within plus button\r\n * - Settings section\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (toolbar) {\r\n\r\n toolbar.settings = require('./settings');\r\n toolbar.inline = require('./inline');\r\n toolbar.toolbox = require('./toolbox');\r\n\r\n /**\r\n * Margin between focused node and toolbar\r\n */\r\n toolbar.defaultToolbarHeight = 49;\r\n\r\n toolbar.defaultOffset = 34;\r\n\r\n toolbar.opened = false;\r\n\r\n toolbar.current = null;\r\n\r\n /**\r\n * @protected\r\n */\r\n toolbar.open = function () {\r\n\r\n editor.nodes.toolbar.classList.add('opened');\r\n this.opened = true;\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n */\r\n toolbar.close = function () {\r\n\r\n editor.nodes.toolbar.classList.remove('opened');\r\n\r\n toolbar.opened = false;\r\n toolbar.current = null;\r\n\r\n for (var button in editor.nodes.toolbarButtons) {\r\n\r\n editor.nodes.toolbarButtons[button].classList.remove('selected');\r\n\r\n }\r\n\r\n /** Close toolbox when toolbar is not displayed */\r\n editor.toolbar.toolbox.close();\r\n editor.toolbar.settings.close();\r\n\r\n };\r\n\r\n toolbar.toggle = function () {\r\n\r\n if ( !this.opened ) {\r\n\r\n this.open();\r\n\r\n } else {\r\n\r\n this.close();\r\n\r\n }\r\n\r\n };\r\n\r\n toolbar.hidePlusButton = function () {\r\n\r\n editor.nodes.plusButton.classList.add('hide');\r\n\r\n };\r\n\r\n toolbar.showPlusButton = function () {\r\n\r\n editor.nodes.plusButton.classList.remove('hide');\r\n\r\n };\r\n\r\n /**\r\n * Moving toolbar to the specified node\r\n */\r\n toolbar.move = function () {\r\n\r\n /** Close Toolbox when we move toolbar */\r\n editor.toolbar.toolbox.close();\r\n\r\n if (!editor.content.currentNode) {\r\n\r\n return;\r\n\r\n }\r\n\r\n var newYCoordinate = editor.content.currentNode.offsetTop - (editor.toolbar.defaultToolbarHeight / 2) + editor.toolbar.defaultOffset;\r\n\r\n editor.nodes.toolbar.style.transform = `translate3D(0, ${Math.floor(newYCoordinate)}px, 0)`;\r\n\r\n /** Close trash actions */\r\n editor.toolbar.settings.hideRemoveActions();\r\n\r\n };\r\n\r\n return toolbar;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/toolbar.js","/**\r\n * Toolbar settings\r\n *\r\n * @version 1.0.4\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (settings) {\r\n\r\n settings.opened = false;\r\n\r\n settings.setting = null;\r\n settings.actions = null;\r\n\r\n settings.cover = null;\r\n\r\n /**\r\n * Append and open settings\r\n */\r\n settings.open = function (toolType) {\r\n\r\n /**\r\n * Append settings content\r\n * It's stored in tool.settings\r\n */\r\n if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings ) {\r\n\r\n editor.core.log(`Plugin «${toolType}» has no settings`, 'warn');\r\n // editor.nodes.pluginSettings.innerHTML = `Плагин «${toolType}» не имеет настроек`;\r\n\r\n } else {\r\n\r\n /**\r\n * Draw settings block\r\n */\r\n var settingsBlock = editor.tools[toolType].makeSettings();\r\n\r\n editor.nodes.pluginSettings.appendChild(settingsBlock);\r\n\r\n }\r\n\r\n /** Open settings block */\r\n editor.nodes.blockSettings.classList.add('opened');\r\n editor.toolbar.settings.addDefaultSettings();\r\n this.opened = true;\r\n\r\n };\r\n\r\n /**\r\n * Close and clear settings\r\n */\r\n settings.close = function () {\r\n\r\n editor.nodes.blockSettings.classList.remove('opened');\r\n editor.nodes.pluginSettings.innerHTML = '';\r\n\r\n this.opened = false;\r\n\r\n };\r\n\r\n /**\r\n * @param {string} toolType - plugin type\r\n */\r\n settings.toggle = function ( toolType ) {\r\n\r\n if ( !this.opened ) {\r\n\r\n this.open(toolType);\r\n\r\n } else {\r\n\r\n this.close();\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * This function adds default core settings\r\n */\r\n settings.addDefaultSettings = function () {\r\n\r\n /** list of default settings */\r\n var feedModeToggler;\r\n\r\n /** Clear block and append initialized settings */\r\n editor.nodes.defaultSettings.innerHTML = '';\r\n\r\n\r\n /** Init all default setting buttons */\r\n feedModeToggler = editor.toolbar.settings.makeFeedModeToggler();\r\n\r\n /**\r\n * Fill defaultSettings\r\n */\r\n\r\n /**\r\n * Button that enables/disables Feed-mode\r\n * Feed-mode means that block will be showed in articles-feed like cover\r\n */\r\n editor.nodes.defaultSettings.appendChild(feedModeToggler);\r\n\r\n };\r\n\r\n /**\r\n * Cover setting.\r\n * This tune highlights block, so that it may be used for showing target block on main page\r\n * Draw different setting when block is marked for main page\r\n * If TRUE, then we show button that removes this selection\r\n * Also defined setting \"Click\" events will be listened and have separate callbacks\r\n *\r\n * @return {Element} node/button that we place in default settings block\r\n */\r\n settings.makeFeedModeToggler = function () {\r\n\r\n var isFeedModeActivated = editor.toolbar.settings.isFeedModeActivated(),\r\n setting,\r\n data;\r\n\r\n if (!isFeedModeActivated) {\r\n\r\n data = {\r\n innerHTML : 'Вывести в ленте'\r\n };\r\n\r\n } else {\r\n\r\n data = {\r\n innerHTML : 'Не выводить в ленте'\r\n };\r\n\r\n }\r\n\r\n setting = editor.draw.node('DIV', editor.ui.className.SETTINGS_ITEM, data);\r\n setting.addEventListener('click', editor.toolbar.settings.updateFeedMode, false);\r\n\r\n return setting;\r\n\r\n };\r\n\r\n /**\r\n * Updates Feed-mode\r\n */\r\n settings.updateFeedMode = function () {\r\n\r\n var currentNode = editor.content.currentNode;\r\n\r\n currentNode.classList.toggle(editor.ui.className.BLOCK_IN_FEED_MODE);\r\n\r\n editor.toolbar.settings.close();\r\n\r\n };\r\n\r\n settings.isFeedModeActivated = function () {\r\n\r\n var currentBlock = editor.content.currentNode;\r\n\r\n if (currentBlock) {\r\n\r\n return currentBlock.classList.contains(editor.ui.className.BLOCK_IN_FEED_MODE);\r\n\r\n } else {\r\n\r\n return false;\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Here we will draw buttons and add listeners to components\r\n */\r\n settings.makeRemoveBlockButton = function () {\r\n\r\n var removeBlockWrapper = editor.draw.node('SPAN', 'ce-toolbar__remove-btn', {}),\r\n settingButton = editor.draw.node('SPAN', 'ce-toolbar__remove-setting', { innerHTML : '' }),\r\n actionWrapper = editor.draw.node('DIV', 'ce-toolbar__remove-confirmation', {}),\r\n confirmAction = editor.draw.node('DIV', 'ce-toolbar__remove-confirm', { textContent : 'Удалить блок' }),\r\n cancelAction = editor.draw.node('DIV', 'ce-toolbar__remove-cancel', { textContent : 'Отмена' });\r\n\r\n settingButton.addEventListener('click', editor.toolbar.settings.removeButtonClicked, false);\r\n\r\n confirmAction.addEventListener('click', editor.toolbar.settings.confirmRemovingRequest, false);\r\n\r\n cancelAction.addEventListener('click', editor.toolbar.settings.cancelRemovingRequest, false);\r\n\r\n actionWrapper.appendChild(confirmAction);\r\n actionWrapper.appendChild(cancelAction);\r\n\r\n removeBlockWrapper.appendChild(settingButton);\r\n removeBlockWrapper.appendChild(actionWrapper);\r\n\r\n /** Save setting */\r\n editor.toolbar.settings.setting = settingButton;\r\n editor.toolbar.settings.actions = actionWrapper;\r\n\r\n return removeBlockWrapper;\r\n\r\n };\r\n\r\n settings.removeButtonClicked = function () {\r\n\r\n var action = editor.toolbar.settings.actions;\r\n\r\n if (action.classList.contains('opened')) {\r\n\r\n editor.toolbar.settings.hideRemoveActions();\r\n\r\n } else {\r\n\r\n editor.toolbar.settings.showRemoveActions();\r\n\r\n }\r\n\r\n editor.toolbar.toolbox.close();\r\n editor.toolbar.settings.close();\r\n\r\n };\r\n\r\n settings.cancelRemovingRequest = function () {\r\n\r\n editor.toolbar.settings.actions.classList.remove('opened');\r\n\r\n };\r\n\r\n settings.confirmRemovingRequest = function () {\r\n\r\n var currentBlock = editor.content.currentNode,\r\n firstLevelBlocksCount;\r\n\r\n currentBlock.remove();\r\n\r\n firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\r\n\r\n /**\r\n * If all blocks are removed\r\n */\r\n if (firstLevelBlocksCount === 0) {\r\n\r\n /** update currentNode variable */\r\n editor.content.currentNode = null;\r\n\r\n /** Inserting new empty initial block */\r\n editor.ui.addInitialBlock();\r\n\r\n }\r\n\r\n editor.ui.saveInputs();\r\n\r\n editor.toolbar.close();\r\n\r\n };\r\n\r\n settings.showRemoveActions = function () {\r\n\r\n editor.toolbar.settings.actions.classList.add('opened');\r\n\r\n };\r\n\r\n settings.hideRemoveActions = function () {\r\n\r\n editor.toolbar.settings.actions.classList.remove('opened');\r\n\r\n };\r\n\r\n return settings;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/settings.js","/**\r\n * Inline toolbar\r\n *\r\n * Contains from tools:\r\n * Bold, Italic, Underline and Anchor\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (inline) {\r\n\r\n inline.buttonsOpened = null;\r\n inline.actionsOpened = null;\r\n inline.wrappersOffset = null;\r\n\r\n /**\r\n * saving selection that need for execCommand for styling\r\n *\r\n */\r\n inline.storedSelection = null;\r\n\r\n /**\r\n * @protected\r\n *\r\n * Open inline toobar\r\n */\r\n inline.show = function () {\r\n\r\n var currentNode = editor.content.currentNode,\r\n tool = currentNode.dataset.tool,\r\n plugin;\r\n\r\n /**\r\n * tool allowed to open inline toolbar\r\n */\r\n plugin = editor.tools[tool];\r\n\r\n if (!plugin.showInlineToolbar)\r\n return;\r\n\r\n var selectedText = inline.getSelectionText(),\r\n toolbar = editor.nodes.inlineToolbar.wrapper;\r\n\r\n if (selectedText.length > 0) {\r\n\r\n /** Move toolbar and open */\r\n editor.toolbar.inline.move();\r\n\r\n /** Open inline toolbar */\r\n toolbar.classList.add('opened');\r\n\r\n /** show buttons of inline toolbar */\r\n editor.toolbar.inline.showButtons();\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Closes inline toolbar\r\n */\r\n inline.close = function () {\r\n\r\n var toolbar = editor.nodes.inlineToolbar.wrapper;\r\n\r\n toolbar.classList.remove('opened');\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Moving toolbar\r\n */\r\n inline.move = function () {\r\n\r\n if (!this.wrappersOffset) {\r\n\r\n this.wrappersOffset = this.getWrappersOffset();\r\n\r\n }\r\n\r\n var coords = this.getSelectionCoords(),\r\n defaultOffset = 0,\r\n toolbar = editor.nodes.inlineToolbar.wrapper,\r\n newCoordinateX,\r\n newCoordinateY;\r\n\r\n if (toolbar.offsetHeight === 0) {\r\n\r\n defaultOffset = 40;\r\n\r\n }\r\n\r\n newCoordinateX = coords.x - this.wrappersOffset.left;\r\n newCoordinateY = coords.y + window.scrollY - this.wrappersOffset.top - defaultOffset - toolbar.offsetHeight;\r\n\r\n toolbar.style.transform = `translate3D(${Math.floor(newCoordinateX)}px, ${Math.floor(newCoordinateY)}px, 0)`;\r\n\r\n /** Close everything */\r\n editor.toolbar.inline.closeButtons();\r\n editor.toolbar.inline.closeAction();\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Tool Clicked\r\n */\r\n\r\n inline.toolClicked = function (event, type) {\r\n\r\n /**\r\n * For simple tools we use default browser function\r\n * For more complicated tools, we should write our own behavior\r\n */\r\n switch (type) {\r\n case 'createLink' : editor.toolbar.inline.createLinkAction(event, type); break;\r\n default : editor.toolbar.inline.defaultToolAction(type); break;\r\n }\r\n\r\n /**\r\n * highlight buttons\r\n * after making some action\r\n */\r\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Saving wrappers offset in DOM\r\n */\r\n inline.getWrappersOffset = function () {\r\n\r\n var wrapper = editor.nodes.wrapper,\r\n offset = this.getOffset(wrapper);\r\n\r\n this.wrappersOffset = offset;\r\n return offset;\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Calculates offset of DOM element\r\n *\r\n * @param el\r\n * @returns {{top: number, left: number}}\r\n */\r\n inline.getOffset = function ( el ) {\r\n\r\n var _x = 0;\r\n var _y = 0;\r\n\r\n while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {\r\n\r\n _x += (el.offsetLeft + el.clientLeft);\r\n _y += (el.offsetTop + el.clientTop);\r\n el = el.offsetParent;\r\n\r\n }\r\n return { top: _y, left: _x };\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Calculates position of selected text\r\n * @returns {{x: number, y: number}}\r\n */\r\n inline.getSelectionCoords = function () {\r\n\r\n var sel = document.selection, range;\r\n var x = 0, y = 0;\r\n\r\n if (sel) {\r\n\r\n if (sel.type != 'Control') {\r\n\r\n range = sel.createRange();\r\n range.collapse(true);\r\n x = range.boundingLeft;\r\n y = range.boundingTop;\r\n\r\n }\r\n\r\n } else if (window.getSelection) {\r\n\r\n sel = window.getSelection();\r\n\r\n if (sel.rangeCount) {\r\n\r\n range = sel.getRangeAt(0).cloneRange();\r\n if (range.getClientRects) {\r\n\r\n range.collapse(true);\r\n var rect = range.getClientRects()[0];\r\n\r\n if (!rect) {\r\n\r\n return;\r\n\r\n }\r\n\r\n x = rect.left;\r\n y = rect.top;\r\n\r\n }\r\n\r\n }\r\n\r\n }\r\n return { x: x, y: y };\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Returns selected text as String\r\n * @returns {string}\r\n */\r\n inline.getSelectionText = function () {\r\n\r\n var selectedText = '';\r\n\r\n // all modern browsers and IE9+\r\n if (window.getSelection) {\r\n\r\n selectedText = window.getSelection().toString();\r\n\r\n }\r\n\r\n return selectedText;\r\n\r\n };\r\n\r\n /** Opens buttons block */\r\n inline.showButtons = function () {\r\n\r\n var buttons = editor.nodes.inlineToolbar.buttons;\r\n\r\n buttons.classList.add('opened');\r\n\r\n editor.toolbar.inline.buttonsOpened = true;\r\n\r\n /** highlight buttons */\r\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(editor.toolbar.inline.hightlight);\r\n\r\n };\r\n\r\n /** Makes buttons disappear */\r\n inline.closeButtons = function () {\r\n\r\n var buttons = editor.nodes.inlineToolbar.buttons;\r\n\r\n buttons.classList.remove('opened');\r\n\r\n editor.toolbar.inline.buttonsOpened = false;\r\n\r\n };\r\n\r\n /** Open buttons defined action if exist */\r\n inline.showActions = function () {\r\n\r\n var action = editor.nodes.inlineToolbar.actions;\r\n\r\n action.classList.add('opened');\r\n\r\n editor.toolbar.inline.actionsOpened = true;\r\n\r\n };\r\n\r\n /** Close actions block */\r\n inline.closeAction = function () {\r\n\r\n var action = editor.nodes.inlineToolbar.actions;\r\n\r\n action.innerHTML = '';\r\n action.classList.remove('opened');\r\n editor.toolbar.inline.actionsOpened = false;\r\n\r\n };\r\n\r\n\r\n /**\r\n * Callback for keydowns in inline toolbar \"Insert link...\" input\r\n */\r\n let inlineToolbarAnchorInputKeydown_ = function (event) {\r\n\r\n if (event.keyCode != editor.core.keys.ENTER) {\r\n\r\n return;\r\n\r\n }\r\n\r\n let editable = editor.content.currentNode,\r\n storedSelection = editor.toolbar.inline.storedSelection;\r\n\r\n editor.toolbar.inline.restoreSelection(editable, storedSelection);\r\n editor.toolbar.inline.setAnchor(this.value);\r\n\r\n /**\r\n * Preventing events that will be able to happen\r\n */\r\n event.preventDefault();\r\n event.stopImmediatePropagation();\r\n\r\n editor.toolbar.inline.clearRange();\r\n\r\n };\r\n\r\n /** Action for link creation or for setting anchor */\r\n inline.createLinkAction = function (event) {\r\n\r\n var isActive = this.isLinkActive();\r\n\r\n var editable = editor.content.currentNode,\r\n storedSelection = editor.toolbar.inline.saveSelection(editable);\r\n\r\n /** Save globally selection */\r\n editor.toolbar.inline.storedSelection = storedSelection;\r\n\r\n if (isActive) {\r\n\r\n\r\n /**\r\n * Changing stored selection. if we want to remove anchor from word\r\n * we should remove anchor from whole word, not only selected part.\r\n * The solution is than we get the length of current link\r\n * Change start position to - end of selection minus length of anchor\r\n */\r\n editor.toolbar.inline.restoreSelection(editable, storedSelection);\r\n\r\n editor.toolbar.inline.defaultToolAction('unlink');\r\n\r\n } else {\r\n\r\n /** Create input and close buttons */\r\n var action = editor.draw.inputForLink();\r\n\r\n editor.nodes.inlineToolbar.actions.appendChild(action);\r\n\r\n editor.toolbar.inline.closeButtons();\r\n editor.toolbar.inline.showActions();\r\n\r\n /**\r\n * focus to input\r\n * Solution: https://developer.mozilla.org/ru/docs/Web/API/HTMLElement/focus\r\n * Prevents event after showing input and when we need to focus an input which is in unexisted form\r\n */\r\n action.focus();\r\n event.preventDefault();\r\n\r\n /** Callback to link action */\r\n action.addEventListener('keydown', inlineToolbarAnchorInputKeydown_, false);\r\n\r\n }\r\n\r\n };\r\n\r\n inline.isLinkActive = function () {\r\n\r\n var isActive = false;\r\n\r\n editor.nodes.inlineToolbar.buttons.childNodes.forEach(function (tool) {\r\n\r\n var dataType = tool.dataset.type;\r\n\r\n if (dataType == 'link' && tool.classList.contains('hightlighted')) {\r\n\r\n isActive = true;\r\n\r\n }\r\n\r\n });\r\n\r\n return isActive;\r\n\r\n };\r\n\r\n /** default action behavior of tool */\r\n inline.defaultToolAction = function (type) {\r\n\r\n document.execCommand(type, false, null);\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Sets URL\r\n *\r\n * @param {String} url - URL\r\n */\r\n inline.setAnchor = function (url) {\r\n\r\n document.execCommand('createLink', false, url);\r\n\r\n /** Close after URL inserting */\r\n editor.toolbar.inline.closeAction();\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Saves selection\r\n */\r\n inline.saveSelection = function (containerEl) {\r\n\r\n var range = window.getSelection().getRangeAt(0),\r\n preSelectionRange = range.cloneRange(),\r\n start;\r\n\r\n preSelectionRange.selectNodeContents(containerEl);\r\n preSelectionRange.setEnd(range.startContainer, range.startOffset);\r\n\r\n start = preSelectionRange.toString().length;\r\n\r\n return {\r\n start: start,\r\n end: start + range.toString().length\r\n };\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Sets to previous selection (Range)\r\n *\r\n * @param {Element} containerEl - editable element where we restore range\r\n * @param {Object} savedSel - range basic information to restore\r\n */\r\n inline.restoreSelection = function (containerEl, savedSel) {\r\n\r\n var range = document.createRange(),\r\n charIndex = 0;\r\n\r\n range.setStart(containerEl, 0);\r\n range.collapse(true);\r\n\r\n var nodeStack = [ containerEl ],\r\n node,\r\n foundStart = false,\r\n stop = false,\r\n nextCharIndex;\r\n\r\n while (!stop && (node = nodeStack.pop())) {\r\n\r\n if (node.nodeType == 3) {\r\n\r\n nextCharIndex = charIndex + node.length;\r\n\r\n if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) {\r\n\r\n range.setStart(node, savedSel.start - charIndex);\r\n foundStart = true;\r\n\r\n }\r\n if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) {\r\n\r\n range.setEnd(node, savedSel.end - charIndex);\r\n stop = true;\r\n\r\n }\r\n charIndex = nextCharIndex;\r\n\r\n } else {\r\n\r\n var i = node.childNodes.length;\r\n\r\n while (i--) {\r\n\r\n nodeStack.push(node.childNodes[i]);\r\n\r\n }\r\n\r\n }\r\n\r\n }\r\n\r\n var sel = window.getSelection();\r\n\r\n sel.removeAllRanges();\r\n sel.addRange(range);\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Removes all ranges from window selection\r\n */\r\n inline.clearRange = function () {\r\n\r\n var selection = window.getSelection();\r\n\r\n selection.removeAllRanges();\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * sets or removes hightlight\r\n */\r\n inline.hightlight = function (tool) {\r\n\r\n var dataType = tool.dataset.type;\r\n\r\n if (document.queryCommandState(dataType)) {\r\n\r\n editor.toolbar.inline.setButtonHighlighted(tool);\r\n\r\n } else {\r\n\r\n editor.toolbar.inline.removeButtonsHighLight(tool);\r\n\r\n }\r\n\r\n /**\r\n *\r\n * hightlight for anchors\r\n */\r\n var selection = window.getSelection(),\r\n tag = selection.anchorNode.parentNode;\r\n\r\n if (tag.tagName == 'A' && dataType == 'link') {\r\n\r\n editor.toolbar.inline.setButtonHighlighted(tool);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Mark button if text is already executed\r\n */\r\n inline.setButtonHighlighted = function (button) {\r\n\r\n button.classList.add('hightlighted');\r\n\r\n /** At link tool we also change icon */\r\n if (button.dataset.type == 'link') {\r\n\r\n var icon = button.childNodes[0];\r\n\r\n icon.classList.remove('ce-icon-link');\r\n icon.classList.add('ce-icon-unlink');\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * @private\r\n *\r\n * Removes hightlight\r\n */\r\n inline.removeButtonsHighLight = function (button) {\r\n\r\n button.classList.remove('hightlighted');\r\n\r\n /** At link tool we also change icon */\r\n if (button.dataset.type == 'link') {\r\n\r\n var icon = button.childNodes[0];\r\n\r\n icon.classList.remove('ce-icon-unlink');\r\n icon.classList.add('ce-icon-link');\r\n\r\n }\r\n\r\n };\r\n\r\n\r\n return inline;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/inline.js","/**\r\n * Codex Editor toolbox\r\n *\r\n * All tools be able to appended here\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\n\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (toolbox) {\r\n\r\n toolbox.opened = false;\r\n\r\n /** Shows toolbox */\r\n toolbox.open = function () {\r\n\r\n /** Close setting if toolbox is opened */\r\n if (editor.toolbar.settings.opened) {\r\n\r\n editor.toolbar.settings.close();\r\n\r\n }\r\n\r\n /** display toolbox */\r\n editor.nodes.toolbox.classList.add('opened');\r\n\r\n /** Animate plus button */\r\n editor.nodes.plusButton.classList.add('clicked');\r\n\r\n /** toolbox state */\r\n editor.toolbar.toolbox.opened = true;\r\n\r\n };\r\n\r\n /** Closes toolbox */\r\n toolbox.close = function () {\r\n\r\n /** Makes toolbox disapear */\r\n editor.nodes.toolbox.classList.remove('opened');\r\n\r\n /** Rotate plus button */\r\n editor.nodes.plusButton.classList.remove('clicked');\r\n\r\n /** toolbox state */\r\n editor.toolbar.toolbox.opened = false;\r\n\r\n };\r\n\r\n toolbox.leaf = function () {\r\n\r\n let currentTool = editor.toolbar.current,\r\n tools = Object.keys(editor.tools),\r\n barButtons = editor.nodes.toolbarButtons,\r\n nextToolIndex = 0,\r\n toolToSelect,\r\n visibleTool,\r\n tool;\r\n\r\n if ( !currentTool ) {\r\n\r\n /** Get first tool from object*/\r\n for(tool in editor.tools) {\r\n\r\n if (editor.tools[tool].displayInToolbox) {\r\n\r\n break;\r\n\r\n }\r\n\r\n nextToolIndex ++;\r\n\r\n }\r\n\r\n } else {\r\n\r\n nextToolIndex = tools.indexOf(currentTool) + 1;\r\n visibleTool = tools[nextToolIndex];\r\n\r\n while (!editor.tools[visibleTool].displayInToolbox) {\r\n\r\n nextToolIndex++;\r\n visibleTool = tools[nextToolIndex];\r\n\r\n if ( nextToolIndex == tools.length ) {\r\n\r\n nextToolIndex = 0;\r\n visibleTool = tools[nextToolIndex];\r\n\r\n }\r\n\r\n }\r\n\r\n }\r\n\r\n toolToSelect = tools[nextToolIndex];\r\n\r\n for ( var button in barButtons ) {\r\n\r\n barButtons[button].classList.remove('selected');\r\n\r\n }\r\n\r\n barButtons[toolToSelect].classList.add('selected');\r\n editor.toolbar.current = toolToSelect;\r\n\r\n };\r\n\r\n /**\r\n * Transforming selected node type into selected toolbar element type\r\n * @param {event} event\r\n */\r\n toolbox.toolClicked = function (event) {\r\n\r\n /**\r\n * UNREPLACEBLE_TOOLS this types of tools are forbidden to replace even they are empty\r\n */\r\n var UNREPLACEBLE_TOOLS = ['image', 'link', 'list', 'instagram', 'twitter', 'embed'],\r\n tool = editor.tools[editor.toolbar.current],\r\n workingNode = editor.content.currentNode,\r\n currentInputIndex = editor.caret.inputIndex,\r\n newBlockContent,\r\n appendCallback,\r\n blockData;\r\n\r\n /** Make block from plugin */\r\n newBlockContent = tool.render();\r\n\r\n /** information about block */\r\n blockData = {\r\n block : newBlockContent,\r\n type : tool.type,\r\n stretched : false\r\n };\r\n\r\n if (\r\n workingNode &&\r\n UNREPLACEBLE_TOOLS.indexOf(workingNode.dataset.tool) === -1 &&\r\n workingNode.textContent.trim() === ''\r\n ) {\r\n\r\n /** Replace current block */\r\n editor.content.switchBlock(workingNode, newBlockContent, tool.type);\r\n\r\n } else {\r\n\r\n /** Insert new Block from plugin */\r\n editor.content.insertBlock(blockData);\r\n\r\n /** increase input index */\r\n currentInputIndex++;\r\n\r\n }\r\n\r\n /** Fire tool append callback */\r\n appendCallback = tool.appendCallback;\r\n\r\n if (appendCallback && typeof appendCallback == 'function') {\r\n\r\n appendCallback.call(event);\r\n\r\n }\r\n\r\n window.setTimeout(function () {\r\n\r\n /** Set caret to current block */\r\n editor.caret.setToBlock(currentInputIndex);\r\n\r\n }, 10);\r\n\r\n\r\n /**\r\n * Changing current Node\r\n */\r\n editor.content.workingNodeChanged();\r\n\r\n /**\r\n * Move toolbar when node is changed\r\n */\r\n editor.toolbar.move();\r\n\r\n };\r\n\r\n return toolbox;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/toolbar/toolbox.js","/**\r\n * Codex Editor callbacks module\r\n *\r\n * @author Codex Team\r\n * @version 1.3.5\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (callbacks) {\r\n\r\n callbacks.globalKeydown = function (event) {\r\n\r\n switch (event.keyCode) {\r\n case editor.core.keys.ENTER : editor.callback.enterKeyPressed(event); break;\r\n }\r\n\r\n };\r\n\r\n callbacks.redactorKeyDown = function (event) {\r\n\r\n switch (event.keyCode) {\r\n case editor.core.keys.TAB : editor.callback.tabKeyPressed(event); break;\r\n case editor.core.keys.ENTER : editor.callback.enterKeyPressedOnRedactorZone(event); break;\r\n case editor.core.keys.ESC : editor.callback.escapeKeyPressed(event); break;\r\n default : editor.callback.defaultKeyPressed(event); break;\r\n }\r\n\r\n };\r\n\r\n callbacks.globalKeyup = function (event) {\r\n\r\n switch (event.keyCode) {\r\n case editor.core.keys.UP :\r\n case editor.core.keys.LEFT :\r\n case editor.core.keys.RIGHT :\r\n case editor.core.keys.DOWN : editor.callback.arrowKeyPressed(event); break;\r\n }\r\n\r\n };\r\n\r\n callbacks.tabKeyPressed = function (event) {\r\n\r\n if ( !editor.toolbar.opened ) {\r\n\r\n editor.toolbar.open();\r\n\r\n }\r\n\r\n if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) {\r\n\r\n editor.toolbar.toolbox.open();\r\n\r\n } else {\r\n\r\n editor.toolbar.toolbox.leaf();\r\n\r\n }\r\n\r\n event.preventDefault();\r\n\r\n };\r\n\r\n /**\r\n * @param {Event} event\r\n */\r\n callbacks.enterKeyPressed = function () {\r\n\r\n if (editor.content.editorAreaHightlighted) {\r\n\r\n /**\r\n * it means that we lose input index, saved index before is not correct\r\n * therefore we need to set caret when we insert new block\r\n */\r\n editor.caret.inputIndex = -1;\r\n\r\n editor.callback.enterPressedOnBlock();\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * ENTER key handler\r\n * Makes new paragraph block\r\n */\r\n callbacks.enterKeyPressedOnRedactorZone = function (event) {\r\n\r\n if (event.target.contentEditable == 'true') {\r\n\r\n /** Update input index */\r\n editor.caret.saveCurrentInputIndex();\r\n\r\n }\r\n\r\n var currentInputIndex = editor.caret.getCurrentInputIndex() || 0,\r\n workingNode = editor.content.currentNode,\r\n tool = workingNode.dataset.tool,\r\n isEnterPressedOnToolbar = editor.toolbar.opened &&\r\n editor.toolbar.current &&\r\n event.target == editor.state.inputs[currentInputIndex];\r\n\r\n /** The list of tools which needs the default browser behaviour */\r\n var enableLineBreaks = editor.tools[tool].enableLineBreaks;\r\n\r\n /** This type of block creates when enter is pressed */\r\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\r\n\r\n /**\r\n * When toolbar is opened, select tool instead of making new paragraph\r\n */\r\n if ( isEnterPressedOnToolbar ) {\r\n\r\n event.preventDefault();\r\n\r\n editor.toolbar.toolbox.toolClicked(event);\r\n\r\n editor.toolbar.close();\r\n\r\n /**\r\n * Stop other listeners callback executions\r\n */\r\n event.stopPropagation();\r\n event.stopImmediatePropagation();\r\n\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * Allow paragraph lineBreaks with shift enter\r\n * Or if shiftkey pressed and enter and enabledLineBreaks, the let new block creation\r\n */\r\n if ( event.shiftKey || enableLineBreaks ) {\r\n\r\n event.stopPropagation();\r\n event.stopImmediatePropagation();\r\n return;\r\n\r\n }\r\n\r\n var currentSelection = window.getSelection(),\r\n currentSelectedNode = currentSelection.anchorNode,\r\n caretAtTheEndOfText = editor.caret.position.atTheEnd(),\r\n isTextNodeHasParentBetweenContenteditable = false;\r\n\r\n /**\r\n * Allow making new in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\r\n */\r\n if ( event.shiftKey && !enableLineBreaks ) {\r\n\r\n editor.callback.enterPressedOnBlock(editor.content.currentBlock, event);\r\n event.preventDefault();\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * Workaround situation when caret at the Text node that has some wrapper Elements\r\n * Split block cant handle this.\r\n * We need to save default behavior\r\n */\r\n isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true';\r\n\r\n /**\r\n * Split blocks when input has several nodes and caret placed in textNode\r\n */\r\n if (\r\n currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT &&\r\n !isTextNodeHasParentBetweenContenteditable &&\r\n !caretAtTheEndOfText\r\n ) {\r\n\r\n event.preventDefault();\r\n\r\n editor.core.log('Splitting Text node...');\r\n\r\n editor.content.splitBlock(currentInputIndex);\r\n\r\n /** Show plus button when next input after split is empty*/\r\n if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) {\r\n\r\n editor.toolbar.showPlusButton();\r\n\r\n }\r\n\r\n } else {\r\n\r\n var islastNode = editor.content.isLastNode(currentSelectedNode);\r\n\r\n if ( islastNode && caretAtTheEndOfText ) {\r\n\r\n event.preventDefault();\r\n event.stopPropagation();\r\n event.stopImmediatePropagation();\r\n\r\n editor.core.log('ENTER clicked in last textNode. Create new BLOCK');\r\n\r\n editor.content.insertBlock({\r\n type: NEW_BLOCK_TYPE,\r\n block: editor.tools[NEW_BLOCK_TYPE].render()\r\n }, true);\r\n\r\n editor.toolbar.move();\r\n editor.toolbar.open();\r\n\r\n /** Show plus button with empty block */\r\n editor.toolbar.showPlusButton();\r\n\r\n }\r\n\r\n }\r\n\r\n /** get all inputs after new appending block */\r\n editor.ui.saveInputs();\r\n\r\n };\r\n\r\n callbacks.escapeKeyPressed = function (event) {\r\n\r\n /** Close all toolbar */\r\n editor.toolbar.close();\r\n\r\n /** Close toolbox */\r\n editor.toolbar.toolbox.close();\r\n\r\n event.preventDefault();\r\n\r\n };\r\n\r\n /**\r\n * @param {Event} event\r\n */\r\n callbacks.arrowKeyPressed = function () {\r\n\r\n editor.content.workingNodeChanged();\r\n\r\n /* Closing toolbar */\r\n editor.toolbar.close();\r\n editor.toolbar.move();\r\n\r\n };\r\n\r\n /**\r\n * @param {Event} event\r\n */\r\n callbacks.defaultKeyPressed = function () {\r\n\r\n editor.toolbar.close();\r\n\r\n if (!editor.toolbar.inline.actionsOpened) {\r\n\r\n editor.toolbar.inline.close();\r\n editor.content.clearMark();\r\n\r\n }\r\n\r\n };\r\n\r\n callbacks.redactorClicked = function (event) {\r\n\r\n callbacks.detectWhenClickedOnFirstLevelBlockArea();\r\n\r\n editor.content.workingNodeChanged(event.target);\r\n\r\n editor.ui.saveInputs();\r\n\r\n var selectedText = editor.toolbar.inline.getSelectionText(),\r\n firstLevelBlock;\r\n\r\n /**\r\n * If selection range took off, then we hide inline toolbar\r\n */\r\n if (selectedText.length === 0) {\r\n\r\n editor.toolbar.inline.close();\r\n\r\n }\r\n\r\n /** Update current input index in memory when caret focused into existed input */\r\n if (event.target.contentEditable == 'true') {\r\n\r\n editor.caret.saveCurrentInputIndex();\r\n\r\n }\r\n\r\n if (editor.content.currentNode === null) {\r\n\r\n /**\r\n * If inputs in redactor does not exits, then we put input index 0 not -1\r\n */\r\n var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0;\r\n\r\n /** If we have any inputs */\r\n if (editor.state.inputs.length) {\r\n\r\n /**\r\n * @todo Refactor\r\n */\r\n\r\n /** getting firstlevel parent of input */\r\n firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);\r\n\r\n }\r\n\r\n /** If input is empty, then we set caret to the last input */\r\n if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) {\r\n\r\n editor.caret.setToBlock(indexOfLastInput);\r\n\r\n } else {\r\n\r\n /** Create new input when caret clicked in redactors area */\r\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\r\n\r\n editor.content.insertBlock({\r\n type : NEW_BLOCK_TYPE,\r\n block : editor.tools[NEW_BLOCK_TYPE].render()\r\n });\r\n\r\n /** If there is no inputs except inserted */\r\n if (editor.state.inputs.length === 1) {\r\n\r\n editor.caret.setToBlock(indexOfLastInput);\r\n\r\n } else {\r\n\r\n /** Set caret to this appended input */\r\n editor.caret.setToNextBlock(indexOfLastInput);\r\n\r\n }\r\n\r\n }\r\n\r\n /**\r\n * Move toolbar to the right position and open\r\n */\r\n editor.toolbar.move();\r\n editor.toolbar.open();\r\n\r\n } else {\r\n\r\n /**\r\n * Move toolbar to the new position and open\r\n */\r\n editor.toolbar.move();\r\n editor.toolbar.open();\r\n\r\n /** Close all panels */\r\n editor.toolbar.settings.close();\r\n editor.toolbar.toolbox.close();\r\n\r\n }\r\n\r\n\r\n var inputIsEmpty = !editor.content.currentNode.textContent.trim(),\r\n currentNodeType = editor.content.currentNode.dataset.tool,\r\n isInitialType = currentNodeType == editor.settings.initialBlockPlugin;\r\n\r\n\r\n /** Hide plus buttons */\r\n editor.toolbar.hidePlusButton();\r\n\r\n /** Mark current block */\r\n editor.content.markBlock();\r\n\r\n\r\n if ( isInitialType && inputIsEmpty ) {\r\n\r\n /** Show plus button */\r\n editor.toolbar.showPlusButton();\r\n\r\n }\r\n\r\n\r\n };\r\n\r\n /**\r\n * This method allows to define, is caret in contenteditable element or not.\r\n * Otherwise, if we get TEXT node from range container, that will means we have input index.\r\n * In this case we use default browsers behaviour (if plugin allows that) or overwritten action.\r\n * Therefore, to be sure that we've clicked first-level block area, we should have currentNode, which always\r\n * specifies to the first-level block. Other cases we just ignore.\r\n */\r\n callbacks.detectWhenClickedOnFirstLevelBlockArea = function () {\r\n\r\n var selection = window.getSelection(),\r\n anchorNode = selection.anchorNode,\r\n flag = false;\r\n\r\n if (selection.rangeCount === 0) {\r\n\r\n editor.content.editorAreaHightlighted = true;\r\n\r\n } else {\r\n\r\n if (!editor.core.isDomNode(anchorNode)) {\r\n\r\n anchorNode = anchorNode.parentNode;\r\n\r\n }\r\n\r\n /** Already founded, without loop */\r\n if (anchorNode.contentEditable == 'true') {\r\n\r\n flag = true;\r\n\r\n }\r\n\r\n while (anchorNode.contentEditable != 'true') {\r\n\r\n anchorNode = anchorNode.parentNode;\r\n\r\n if (anchorNode.contentEditable == 'true') {\r\n\r\n flag = true;\r\n\r\n }\r\n\r\n if (anchorNode == document.body) {\r\n\r\n break;\r\n\r\n }\r\n\r\n }\r\n\r\n /** If editable element founded, flag is \"TRUE\", Therefore we return \"FALSE\" */\r\n editor.content.editorAreaHightlighted = flag ? false : true;\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Toolbar button click handler\r\n * @param this - cursor to the button\r\n */\r\n callbacks.toolbarButtonClicked = function (event) {\r\n\r\n var button = this;\r\n\r\n editor.toolbar.current = button.dataset.type;\r\n\r\n editor.toolbar.toolbox.toolClicked(event);\r\n editor.toolbar.close();\r\n\r\n };\r\n\r\n /** Show or Hide toolbox when plus button is clicked */\r\n callbacks.plusButtonClicked = function () {\r\n\r\n if (!editor.nodes.toolbox.classList.contains('opened')) {\r\n\r\n editor.toolbar.toolbox.open();\r\n\r\n } else {\r\n\r\n editor.toolbar.toolbox.close();\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Block handlers for KeyDown events\r\n */\r\n callbacks.blockKeydown = function (event) {\r\n\r\n let block = this; // event.target input\r\n\r\n switch (event.keyCode) {\r\n\r\n case editor.core.keys.DOWN:\r\n case editor.core.keys.RIGHT:\r\n editor.callback.blockRightOrDownArrowPressed();\r\n break;\r\n\r\n case editor.core.keys.BACKSPACE:\r\n editor.callback.backspacePressed(block, event);\r\n break;\r\n\r\n case editor.core.keys.UP:\r\n case editor.core.keys.LEFT:\r\n editor.callback.blockLeftOrUpArrowPressed();\r\n break;\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * RIGHT or DOWN keydowns on block\r\n */\r\n callbacks.blockRightOrDownArrowPressed = function () {\r\n\r\n var selection = window.getSelection(),\r\n inputs = editor.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n /** Check for caret existance */\r\n if (!focusedNode) {\r\n\r\n return false;\r\n\r\n }\r\n\r\n /** Looking for closest (parent) contentEditable element of focused node */\r\n while (focusedNode.contentEditable != 'true') {\r\n\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n\r\n editableElementIndex ++;\r\n\r\n }\r\n\r\n /**\r\n * Founded contentEditable element doesn't have childs\r\n * Or maybe New created block\r\n */\r\n if (!focusedNode.textContent) {\r\n\r\n editor.caret.setToNextBlock(editableElementIndex);\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * Do nothing when caret doesn not reaches the end of last child\r\n */\r\n var caretInLastChild = false,\r\n caretAtTheEndOfText = false;\r\n\r\n var lastChild,\r\n deepestTextnode;\r\n\r\n lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ];\r\n\r\n if (editor.core.isDomNode(lastChild)) {\r\n\r\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);\r\n\r\n } else {\r\n\r\n deepestTextnode = lastChild;\r\n\r\n }\r\n\r\n caretInLastChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;\r\n\r\n if ( !caretInLastChild || !caretAtTheEndOfText ) {\r\n\r\n editor.core.log('arrow [down|right] : caret does not reached the end');\r\n return false;\r\n\r\n }\r\n\r\n editor.caret.setToNextBlock(editableElementIndex);\r\n\r\n };\r\n\r\n /**\r\n * LEFT or UP keydowns on block\r\n */\r\n callbacks.blockLeftOrUpArrowPressed = function () {\r\n\r\n var selection = window.getSelection(),\r\n inputs = editor.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n /** Check for caret existance */\r\n if (!focusedNode) {\r\n\r\n return false;\r\n\r\n }\r\n\r\n /**\r\n * LEFT or UP not at the beginning\r\n */\r\n if ( selection.anchorOffset !== 0) {\r\n\r\n return false;\r\n\r\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\r\n\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n\r\n editableElementIndex ++;\r\n\r\n }\r\n\r\n /**\r\n * Do nothing if caret is not at the beginning of first child\r\n */\r\n var caretInFirstChild = false,\r\n caretAtTheBeginning = false;\r\n\r\n var firstChild,\r\n deepestTextnode;\r\n\r\n /**\r\n * Founded contentEditable element doesn't have childs\r\n * Or maybe New created block\r\n */\r\n if (!focusedNode.textContent) {\r\n\r\n editor.caret.setToPreviousBlock(editableElementIndex);\r\n return;\r\n\r\n }\r\n\r\n firstChild = focusedNode.childNodes[0];\r\n\r\n if (editor.core.isDomNode(firstChild)) {\r\n\r\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);\r\n\r\n } else {\r\n\r\n deepestTextnode = firstChild;\r\n\r\n }\r\n\r\n caretInFirstChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheBeginning = selection.anchorOffset === 0;\r\n\r\n if ( caretInFirstChild && caretAtTheBeginning ) {\r\n\r\n editor.caret.setToPreviousBlock(editableElementIndex);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Callback for enter key pressing in first-level block area\r\n * @param {Event} event\r\n */\r\n callbacks.enterPressedOnBlock = function () {\r\n\r\n var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;\r\n\r\n editor.content.insertBlock({\r\n type : NEW_BLOCK_TYPE,\r\n block : editor.tools[NEW_BLOCK_TYPE].render()\r\n }, true );\r\n\r\n editor.toolbar.move();\r\n editor.toolbar.open();\r\n\r\n };\r\n\r\n callbacks.backspacePressed = function (block, event) {\r\n\r\n var currentInputIndex = editor.caret.getCurrentInputIndex(),\r\n range,\r\n selectionLength,\r\n firstLevelBlocksCount;\r\n\r\n if (block.textContent.trim()) {\r\n\r\n range = editor.content.getRange();\r\n selectionLength = range.endOffset - range.startOffset;\r\n\r\n if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) {\r\n\r\n editor.content.mergeBlocks(currentInputIndex);\r\n\r\n } else {\r\n\r\n return;\r\n\r\n }\r\n\r\n }\r\n\r\n if (!selectionLength) {\r\n\r\n block.remove();\r\n\r\n }\r\n\r\n\r\n firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;\r\n\r\n /**\r\n * If all blocks are removed\r\n */\r\n if (firstLevelBlocksCount === 0) {\r\n\r\n /** update currentNode variable */\r\n editor.content.currentNode = null;\r\n\r\n /** Inserting new empty initial block */\r\n editor.ui.addInitialBlock();\r\n\r\n /** Updating inputs state after deleting last block */\r\n editor.ui.saveInputs();\r\n\r\n /** Set to current appended block */\r\n window.setTimeout(function () {\r\n\r\n editor.caret.setToPreviousBlock(1);\r\n\r\n }, 10);\r\n\r\n } else {\r\n\r\n if (editor.caret.inputIndex !== 0) {\r\n\r\n /** Target block is not first */\r\n editor.caret.setToPreviousBlock(editor.caret.inputIndex);\r\n\r\n } else {\r\n\r\n /** If we try to delete first block */\r\n editor.caret.setToNextBlock(editor.caret.inputIndex);\r\n\r\n }\r\n\r\n }\r\n\r\n editor.toolbar.move();\r\n\r\n if (!editor.toolbar.opened) {\r\n\r\n editor.toolbar.open();\r\n\r\n }\r\n\r\n /** Updating inputs state */\r\n editor.ui.saveInputs();\r\n\r\n /** Prevent default browser behaviour */\r\n event.preventDefault();\r\n\r\n };\r\n\r\n /**\r\n * @deprecated\r\n *\r\n * @param event\r\n */\r\n callbacks.blockPaste = function (event) {\r\n\r\n var currentInputIndex = editor.caret.getCurrentInputIndex(),\r\n node = editor.state.inputs[currentInputIndex];\r\n\r\n window.setTimeout(function () {\r\n\r\n editor.content.sanitize(node);\r\n\r\n event.preventDefault();\r\n\r\n }, 10);\r\n\r\n event.stopImmediatePropagation();\r\n\r\n };\r\n\r\n /**\r\n * This method is used to observe pasted dirty data.\r\n *\r\n * Mutation handlers send to separate observers each mutation (added, changed and so on), which will be\r\n * passed from handler that sanitizes and replaces data.\r\n *\r\n * Probably won't be used\r\n *\r\n * @deprecated\r\n *\r\n * @param event\r\n * @private\r\n */\r\n callbacks._blockPasteCallback = function () {\r\n\r\n var currentInputIndex = editor.caret.getCurrentInputIndex();\r\n\r\n /**\r\n * create an observer instance\r\n */\r\n var observer = new MutationObserver(editor.callback.handleMutationsOnPaste);\r\n\r\n /**\r\n * configuration of the observer:\r\n */\r\n var config = {\r\n attributes: true,\r\n childList: false,\r\n characterData: false,\r\n subtree : true\r\n };\r\n\r\n // pass in the target node, as well as the observer options\r\n observer.observe(editor.state.inputs[currentInputIndex], config);\r\n\r\n };\r\n\r\n /**\r\n * This method prevents default behaviour.\r\n *\r\n * We get from clipboard pasted data, sanitize, make a fragment that contains of this sanitized nodes.\r\n * Firstly, we need to memorize the caret position. We can do that by getting the range of selection.\r\n * After all, we insert clear fragment into caret placed position. Then, we should move the caret to the last node\r\n *\r\n * @param event\r\n */\r\n callbacks.blockPasteCallback = function (event) {\r\n\r\n /** Prevent default behaviour */\r\n event.preventDefault();\r\n\r\n /** get html pasted data - dirty data */\r\n var data = event.clipboardData.getData('text/html') || event.clipboardData.getData('text/plain');\r\n\r\n /** Temporary DIV that is used to work with childs as arrays item */\r\n var div = editor.draw.node('DIV', '', {}),\r\n cleaner = new editor.sanitizer.init(editor.sanitizer.Config.BASIC),\r\n cleanData,\r\n fragment;\r\n\r\n /** Create fragment, that we paste to range after proccesing */\r\n fragment = document.createDocumentFragment();\r\n\r\n cleanData = cleaner.clean(data);\r\n\r\n div.innerHTML = cleanData;\r\n\r\n var node, lastNode;\r\n\r\n /**\r\n * and fill in fragment\r\n */\r\n while (( node = div.firstChild) ) {\r\n\r\n lastNode = fragment.appendChild(node);\r\n\r\n }\r\n\r\n /**\r\n * work with selection and range\r\n */\r\n var selection, range;\r\n\r\n selection = window.getSelection();\r\n\r\n range = selection.getRangeAt(0);\r\n range.deleteContents();\r\n\r\n range.insertNode(fragment);\r\n // document.execCommand('insertParagraph', false, \" \");\r\n\r\n /** Preserve the selection */\r\n if (lastNode) {\r\n\r\n range = range.cloneRange();\r\n range.setStartAfter(lastNode);\r\n range.collapse(true);\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n\r\n }\r\n\r\n };\r\n\r\n /**\r\n * Sends all mutations to paste handler\r\n */\r\n callbacks.handleMutationsOnPaste = function (mutations) {\r\n\r\n var self = this;\r\n\r\n /**\r\n * Calling function with context of this function.\r\n * Also, we should sanitize pasted or changed data one time and ignore\r\n * changings which makes sanitize method.\r\n * For that, we need to send Context, MutationObserver.__proto__ that contains\r\n * observer disconnect method.\r\n */\r\n mutations.forEach(function (mutation) {\r\n\r\n editor.content.paste.call(self, mutation);\r\n\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Clicks on block settings button\r\n */\r\n callbacks.showSettingsButtonClicked = function () {\r\n\r\n /**\r\n * Get type of current block\r\n * It uses to append settings from tool.settings property.\r\n * ...\r\n * Type is stored in data-type attribute on block\r\n */\r\n var currentToolType = editor.content.currentNode.dataset.tool;\r\n\r\n editor.toolbar.settings.toggle(currentToolType);\r\n\r\n /** Close toolbox when settings button is active */\r\n editor.toolbar.toolbox.close();\r\n editor.toolbar.settings.hideRemoveActions();\r\n\r\n };\r\n\r\n callbacks.showCommentButtonClicked = function () {\r\n\r\n var block = editor.content.currentNode;\r\n\r\n editor.comments.add(block);\r\n\r\n };\r\n\r\n return callbacks;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/callbacks.js","/**\r\n * Codex Editor Draw module\r\n *\r\n * @author Codex Team\r\n * @version 1.0.\r\n */\r\n\r\nmodule.exports = (function (draw) {\r\n\r\n /**\r\n * Base editor wrapper\r\n */\r\n draw.wrapper = function () {\r\n\r\n var wrapper = document.createElement('div');\r\n\r\n wrapper.className += 'codex-editor';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n /**\r\n * Content-editable holder\r\n */\r\n draw.redactor = function () {\r\n\r\n var redactor = document.createElement('div');\r\n\r\n redactor.className += 'ce-redactor';\r\n\r\n return redactor;\r\n\r\n };\r\n\r\n /**\r\n * Comments side bar\r\n */\r\n draw.commentsSidebar = function () {\r\n\r\n var sidebar = draw.node('DIV', 'ce-comments-sidebar');\r\n\r\n return sidebar;\r\n\r\n };\r\n\r\n draw.ceBlock = function () {\r\n\r\n var block = document.createElement('DIV');\r\n\r\n block.className += 'ce_block';\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * Empty toolbar with toggler\r\n */\r\n draw.toolbar = function () {\r\n\r\n var bar = document.createElement('div');\r\n\r\n bar.className += 'ce-toolbar';\r\n\r\n return bar;\r\n\r\n };\r\n\r\n draw.toolbarContent = function () {\r\n\r\n var wrapper = document.createElement('DIV');\r\n\r\n wrapper.classList.add('ce-toolbar__content');\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n /**\r\n * Inline toolbar\r\n */\r\n draw.inlineToolbar = function () {\r\n\r\n var bar = document.createElement('DIV');\r\n\r\n bar.className += 'ce-toolbar-inline';\r\n\r\n return bar;\r\n\r\n };\r\n\r\n /**\r\n * Wrapper for inline toobar buttons\r\n */\r\n draw.inlineToolbarButtons = function () {\r\n\r\n var wrapper = document.createElement('DIV');\r\n\r\n wrapper.className += 'ce-toolbar-inline__buttons';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n /**\r\n * For some actions\r\n */\r\n draw.inlineToolbarActions = function () {\r\n\r\n var wrapper = document.createElement('DIV');\r\n\r\n wrapper.className += 'ce-toolbar-inline__actions';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n draw.inputForLink = function () {\r\n\r\n var input = document.createElement('INPUT');\r\n\r\n input.type = 'input';\r\n input.className += 'inputForLink';\r\n input.placeholder = 'Вставьте ссылку ...';\r\n input.setAttribute('form', 'defaultForm');\r\n\r\n input.setAttribute('autofocus', 'autofocus');\r\n\r\n return input;\r\n\r\n };\r\n\r\n /**\r\n * Block with notifications\r\n */\r\n draw.alertsHolder = function () {\r\n\r\n var block = document.createElement('div');\r\n\r\n block.classList.add('ce_notifications-block');\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * @todo Desc\r\n */\r\n draw.blockButtons = function () {\r\n\r\n var block = document.createElement('div');\r\n\r\n block.className += 'ce-toolbar__actions';\r\n\r\n return block;\r\n\r\n };\r\n\r\n /**\r\n * Block settings panel\r\n */\r\n draw.blockSettings = function () {\r\n\r\n var settings = document.createElement('div');\r\n\r\n settings.className += 'ce-settings';\r\n\r\n return settings;\r\n\r\n };\r\n\r\n draw.defaultSettings = function () {\r\n\r\n var div = document.createElement('div');\r\n\r\n div.classList.add('ce-settings_default');\r\n\r\n return div;\r\n\r\n };\r\n\r\n draw.pluginsSettings = function () {\r\n\r\n var div = document.createElement('div');\r\n\r\n div.classList.add('ce-settings_plugin');\r\n\r\n return div;\r\n\r\n };\r\n\r\n draw.plusButton = function () {\r\n\r\n var button = document.createElement('span');\r\n\r\n button.className = 'ce-toolbar__plus';\r\n // button.innerHTML = '';\r\n\r\n return button;\r\n\r\n };\r\n\r\n /**\r\n * Settings button in toolbar\r\n */\r\n draw.settingsButton = function () {\r\n\r\n var toggler = document.createElement('span');\r\n\r\n toggler.className = 'ce-toolbar__settings-btn';\r\n\r\n /** Toggler button*/\r\n toggler.innerHTML = '';\r\n\r\n return toggler;\r\n\r\n };\r\n\r\n /**\r\n * Comment button in toolbar\r\n */\r\n draw.commentButton = function () {\r\n\r\n var toggler = document.createElement('span');\r\n\r\n toggler.className = 'ce-toolbar__comment-btn';\r\n\r\n /** Toggler button*/\r\n toggler.innerHTML = '';\r\n\r\n return toggler;\r\n\r\n };\r\n\r\n /**\r\n * Redactor tools wrapper\r\n */\r\n\r\n draw.toolbox = function () {\r\n\r\n var wrapper = document.createElement('div');\r\n\r\n wrapper.className = 'ce-toolbar__tools';\r\n\r\n return wrapper;\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Draws tool buttons for toolbox\r\n *\r\n * @param {String} type\r\n * @param {String} classname\r\n * @returns {Element}\r\n */\r\n draw.toolbarButton = function (type, classname) {\r\n\r\n var button = document.createElement('li'),\r\n toolIcon = document.createElement('i'),\r\n toolTitle = document.createElement('span');\r\n\r\n button.dataset.type = type;\r\n button.setAttribute('title', type);\r\n\r\n toolIcon.classList.add(classname);\r\n toolTitle.classList.add('ce_toolbar_tools--title');\r\n\r\n\r\n button.appendChild(toolIcon);\r\n button.appendChild(toolTitle);\r\n\r\n return button;\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n *\r\n * Draws tools for inline toolbar\r\n *\r\n * @param {String} type\r\n * @param {String} classname\r\n */\r\n draw.toolbarButtonInline = function (type, classname) {\r\n\r\n var button = document.createElement('BUTTON'),\r\n toolIcon = document.createElement('I');\r\n\r\n button.type = 'button';\r\n button.dataset.type = type;\r\n toolIcon.classList.add(classname);\r\n\r\n button.appendChild(toolIcon);\r\n\r\n return button;\r\n\r\n };\r\n\r\n /**\r\n * Redactor block\r\n */\r\n draw.block = function (tagName, content) {\r\n\r\n var node = document.createElement(tagName);\r\n\r\n node.innerHTML = content || '';\r\n\r\n return node;\r\n\r\n };\r\n\r\n /**\r\n * Creates Node with passed tagName and className\r\n * @param {string} tagName\r\n * @param {string} className\r\n * @param {object} properties - allow to assign properties\r\n */\r\n draw.node = function ( tagName, className, properties ) {\r\n\r\n var el = document.createElement( tagName );\r\n\r\n if ( className ) el.className = className;\r\n\r\n if ( properties ) {\r\n\r\n for (var name in properties) {\r\n\r\n el[name] = properties[name];\r\n\r\n }\r\n\r\n }\r\n\r\n return el;\r\n\r\n };\r\n\r\n return draw;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/draw.js","/**\r\n * Codex Editor Caret Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (caret) {\r\n\r\n /**\r\n * @var {int} InputIndex - editable element in DOM\r\n */\r\n caret.inputIndex = null;\r\n\r\n /**\r\n * @var {int} offset - caret position in a text node.\r\n */\r\n caret.offset = null;\r\n\r\n /**\r\n * @var {int} focusedNodeIndex - we get index of child node from first-level block\r\n */\r\n caret.focusedNodeIndex = null;\r\n\r\n /**\r\n * Creates Document Range and sets caret to the element.\r\n * @protected\r\n * @uses caret.save — if you need to save caret position\r\n * @param {Element} el - Changed Node.\r\n */\r\n caret.set = function ( el, index, offset) {\r\n\r\n offset = offset || caret.offset || 0;\r\n index = index || caret.focusedNodeIndex || 0;\r\n\r\n var childs = el.childNodes,\r\n nodeToSet;\r\n\r\n if ( childs.length === 0 ) {\r\n\r\n nodeToSet = el;\r\n\r\n } else {\r\n\r\n nodeToSet = childs[index];\r\n\r\n }\r\n\r\n /** If Element is INPUT */\r\n if (el.tagName == 'INPUT') {\r\n\r\n el.focus();\r\n return;\r\n\r\n }\r\n\r\n if (editor.core.isDomNode(nodeToSet)) {\r\n\r\n nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);\r\n\r\n }\r\n\r\n var range = document.createRange(),\r\n selection = window.getSelection();\r\n\r\n window.setTimeout(function () {\r\n\r\n range.setStart(nodeToSet, offset);\r\n range.setEnd(nodeToSet, offset);\r\n\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n\r\n editor.caret.saveCurrentInputIndex();\r\n\r\n }, 20);\r\n\r\n };\r\n\r\n /**\r\n * @protected\r\n * Updates index of input and saves it in caret object\r\n */\r\n caret.saveCurrentInputIndex = function () {\r\n\r\n /** Index of Input that we paste sanitized content */\r\n var selection = window.getSelection(),\r\n inputs = editor.state.inputs,\r\n focusedNode = selection.anchorNode,\r\n focusedNodeHolder;\r\n\r\n if (!focusedNode) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\r\n\r\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\r\n\r\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\r\n\r\n editableElementIndex ++;\r\n\r\n }\r\n\r\n caret.inputIndex = editableElementIndex;\r\n\r\n };\r\n\r\n /**\r\n * Returns current input index (caret object)\r\n */\r\n caret.getCurrentInputIndex = function () {\r\n\r\n return caret.inputIndex;\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of first-level block after that we set caret into next input\r\n */\r\n caret.setToNextBlock = function (index) {\r\n\r\n var inputs = editor.state.inputs,\r\n nextInput = inputs[index + 1];\r\n\r\n if (!nextInput) {\r\n\r\n editor.core.log('We are reached the end');\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!nextInput.childNodes.length) {\r\n\r\n var emptyTextElement = document.createTextNode('');\r\n\r\n nextInput.appendChild(emptyTextElement);\r\n\r\n }\r\n\r\n editor.caret.inputIndex = index + 1;\r\n editor.caret.set(nextInput, 0, 0);\r\n editor.content.workingNodeChanged(nextInput);\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of target input.\r\n * Sets caret to input with this index\r\n */\r\n caret.setToBlock = function (index) {\r\n\r\n var inputs = editor.state.inputs,\r\n targetInput = inputs[index];\r\n\r\n if ( !targetInput ) {\r\n\r\n return;\r\n\r\n }\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!targetInput.childNodes.length) {\r\n\r\n var emptyTextElement = document.createTextNode('');\r\n\r\n targetInput.appendChild(emptyTextElement);\r\n\r\n }\r\n\r\n editor.caret.inputIndex = index;\r\n editor.caret.set(targetInput, 0, 0);\r\n editor.content.workingNodeChanged(targetInput);\r\n\r\n };\r\n\r\n /**\r\n * @param {int} index - index of input\r\n */\r\n caret.setToPreviousBlock = function (index) {\r\n\r\n index = index || 0;\r\n\r\n var inputs = editor.state.inputs,\r\n previousInput = inputs[index - 1],\r\n lastChildNode,\r\n lengthOfLastChildNode,\r\n emptyTextElement;\r\n\r\n\r\n if (!previousInput) {\r\n\r\n editor.core.log('We are reached first node');\r\n return;\r\n\r\n }\r\n\r\n lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length);\r\n lengthOfLastChildNode = lastChildNode.length;\r\n\r\n /**\r\n * When new Block created or deleted content of input\r\n * We should add some text node to set caret\r\n */\r\n if (!previousInput.childNodes.length) {\r\n\r\n emptyTextElement = document.createTextNode('');\r\n previousInput.appendChild(emptyTextElement);\r\n\r\n }\r\n editor.caret.inputIndex = index - 1;\r\n editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode);\r\n editor.content.workingNodeChanged(inputs[index - 1]);\r\n\r\n };\r\n\r\n caret.position = {\r\n\r\n atStart : function () {\r\n\r\n var selection = window.getSelection(),\r\n anchorOffset = selection.anchorOffset,\r\n anchorNode = selection.anchorNode,\r\n firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode),\r\n pluginsRender = firstLevelBlock.childNodes[0];\r\n\r\n if (!editor.core.isDomNode(anchorNode)) {\r\n\r\n anchorNode = anchorNode.parentNode;\r\n\r\n }\r\n\r\n var isFirstNode = anchorNode === pluginsRender.childNodes[0],\r\n isOffsetZero = anchorOffset === 0;\r\n\r\n return isFirstNode && isOffsetZero;\r\n\r\n },\r\n\r\n atTheEnd : function () {\r\n\r\n var selection = window.getSelection(),\r\n anchorOffset = selection.anchorOffset,\r\n anchorNode = selection.anchorNode;\r\n\r\n /** Caret is at the end of input */\r\n return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;\r\n\r\n }\r\n };\r\n\r\n return caret;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/caret.js","/**\r\n * Codex Editor Notification Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (notifications) {\r\n\r\n /**\r\n * Error notificator. Shows block with message\r\n * @protected\r\n */\r\n notifications.errorThrown = function (errorMsg, event) {\r\n\r\n editor.notifications.send('This action is not available currently', event.type, false);\r\n\r\n };\r\n\r\n /**\r\n * Appends notification with different types\r\n * @param message {string} - Error or alert message\r\n * @param type {string} - Type of message notification. Ex: Error, Warning, Danger ...\r\n * @param append {boolean} - can be True or False when notification should be inserted after\r\n */\r\n notifications.send = function (message, type, append) {\r\n\r\n var notification = editor.draw.block('div');\r\n\r\n notification.textContent = message;\r\n notification.classList.add('ce_notification-item', 'ce_notification-' + type, 'flipInX');\r\n\r\n if (!append) {\r\n\r\n editor.nodes.notifications.innerHTML = '';\r\n\r\n }\r\n\r\n editor.nodes.notifications.appendChild(notification);\r\n\r\n window.setTimeout(function () {\r\n\r\n notification.remove();\r\n\r\n }, 3000);\r\n\r\n };\r\n\r\n return notifications;\r\n\r\n})({});\n\n\n// WEBPACK FOOTER //\n// ./modules/notifications.js","/**\r\n * Codex Editor Parser Module\r\n *\r\n * @author Codex Team\r\n * @version 1.1\r\n */\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = (function (parser) {\r\n\r\n /** inserting text */\r\n parser.insertPastedContent = function (blockType, tag) {\r\n\r\n editor.content.insertBlock({\r\n type : blockType.type,\r\n block : blockType.render({\r\n text : tag.innerHTML\r\n })\r\n });\r\n\r\n };\r\n\r\n /**\r\n * Check DOM node for display style: separated block or child-view\r\n */\r\n parser.isFirstLevelBlock = function (node) {\r\n\r\n return node.nodeType == editor.core.nodeTypes.TAG &&\r\n node.classList.contains(editor.ui.className.BLOCK_CLASSNAME);\r\n\r\n };\r\n\r\n return parser;\r\n\r\n})({});\r\n\n\n\n// WEBPACK FOOTER //\n// ./modules/parser.js","/**\r\n * Codex Sanitizer\r\n */\r\n\r\nvar janitor = require('html-janitor');\r\n\r\nmodule.exports = (function (sanitizer) {\r\n\r\n /**\r\n * Basic config\r\n */\r\n var Config = {\r\n\r\n BASIC : {\r\n\r\n tags: {\r\n p: {},\r\n a: {\r\n href: true,\r\n target: '_blank',\r\n rel: 'nofollow'\r\n },\r\n i: {},\r\n b: {},\r\n strong: {},\r\n em: {},\r\n span: {}\r\n }\r\n }\r\n };\r\n\r\n sanitizer.Config = Config;\r\n\r\n sanitizer.init = janitor;\r\n\r\n return sanitizer;\r\n\r\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 = 17\n// module chunks = 0","\r\nlet editor = codex.editor;\r\n\r\nmodule.exports = function (comments) {\r\n\r\n var draw = {\r\n\r\n commentsField: function (blockId) {\r\n\r\n var field = editor.draw.node('DIV', 'ce-comments-field');\r\n\r\n field.dataset.blockId = blockId;\r\n\r\n return field;\r\n\r\n },\r\n\r\n input: function (text) {\r\n\r\n var wrapper = editor.draw.node('DIV', 'ce-comment'),\r\n input = editor.draw.node('DIV', 'ce-comment__input', {'contentEditable': 'true', 'textContent':text||''}),\r\n deleteBtn = editor.draw.node('DIV', 'ce-comment__delete', {'textContent': 'Delete'}),\r\n postBtn = editor.draw.node('DIV', 'ce-comment__post', {'textContent': text?'Save':'Comment'});\r\n\r\n postBtn.addEventListener('click', callbacks.commentClicked);\r\n deleteBtn.addEventListener('click', callbacks.deleteClicked);\r\n\r\n wrapper.appendChild(input);\r\n wrapper.appendChild(postBtn);\r\n wrapper.appendChild(deleteBtn);\r\n\r\n wrapper.dataset.edited = text?'edited ':'';\r\n\r\n return wrapper;\r\n\r\n },\r\n\r\n comment: function (data) {\r\n\r\n if (!data.text) return;\r\n\r\n var wrapper = editor.draw.node('DIV', 'ce-comment'),\r\n text = editor.draw.node('DIV', 'ce-comment__text', {'textContent': data.text}),\r\n date = new Date().toLocaleDateString('en-US', {\r\n month: 'short',\r\n day: 'numeric',\r\n hour: 'numeric',\r\n minute: 'numeric',\r\n hour12: false\r\n }),\r\n time = editor.draw.node('DIV', 'ce-comment__time', {'textContent': date}),\r\n deleteBtn = editor.draw.node('DIV', 'ce-comment__delete', {'textContent': 'Delete'}),\r\n editBtn = editor.draw.node('DIV', 'ce-comment__edit', {'textContent': 'Edit'});\r\n\r\n editBtn.addEventListener('click', callbacks.editClicked);\r\n deleteBtn.addEventListener('click', callbacks.deleteClicked);\r\n\r\n wrapper.dataset.edited = data.edited;\r\n time.dataset.edited = data.edited;\r\n\r\n wrapper.appendChild(text);\r\n wrapper.appendChild(time);\r\n wrapper.appendChild(editBtn);\r\n wrapper.appendChild(deleteBtn);\r\n\r\n return wrapper;\r\n\r\n }\r\n\r\n };\r\n\r\n var callbacks = {\r\n\r\n commentClicked: function (e) {\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1],\r\n input = wrapper.querySelector('.ce-comment__input');\r\n\r\n if (input.textContent.trim() == '') return;\r\n\r\n var comment = draw.comment({\r\n text: input.textContent,\r\n edited: wrapper.dataset.edited\r\n });\r\n\r\n field.replaceChild(comment, wrapper);\r\n\r\n },\r\n\r\n editClicked: function (e) {\r\n\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1],\r\n text = wrapper.querySelector('.ce-comment__text');\r\n\r\n var comment = draw.input(text.textContent);\r\n\r\n field.replaceChild(comment, wrapper);\r\n\r\n },\r\n\r\n deleteClicked: function (e) {\r\n\r\n var field = e.path[2],\r\n wrapper = e.path[1];\r\n\r\n field.removeChild(wrapper);\r\n\r\n }\r\n\r\n };\r\n\r\n var methods = {\r\n\r\n getCoords: function (block) {\r\n\r\n var rect = block.getBoundingClientRect();\r\n\r\n return {\r\n x: window.pageXOffset + rect.left,\r\n y: window.pageYOffset + rect.top\r\n };\r\n\r\n }\r\n\r\n };\r\n\r\n comments.add = function (block) {\r\n\r\n var blockId = block.dataset.id;\r\n\r\n var field = editor.nodes.commentsSidebar.querySelector('.ce-comments-field[data-block-id=\"'+blockId+'\"]') ||\r\n draw.commentsField(blockId);\r\n\r\n var comment = draw.input();\r\n\r\n comment.querySelector('.ce-comment__input').focus();\r\n\r\n field.appendChild(comment);\r\n\r\n var blockCoords = methods.getCoords(block);\r\n\r\n field.style.top = blockCoords.y + 'px';\r\n\r\n editor.nodes.commentsSidebar.appendChild(field);\r\n\r\n };\r\n\r\n\r\n return comments;\r\n\r\n}({});\n\n\n// WEBPACK FOOTER //\n// ./modules/comments.js"],"sourceRoot":""}
\ No newline at end of file
diff --git a/modules/callbacks.js b/modules/callbacks.js
index 063f08cb..cbeeacd6 100644
--- a/modules/callbacks.js
+++ b/modules/callbacks.js
@@ -927,13 +927,13 @@ module.exports = (function (callbacks) {
};
- callbacks.showCommentButtonClicked = function() {
+ callbacks.showCommentButtonClicked = function () {
- var block = codex.content.currentNode;
+ var block = editor.content.currentNode;
- codex.comments.add(block);
+ editor.comments.add(block);
- }
+ };
return callbacks;
diff --git a/modules/comments.js b/modules/comments.js
index a84457e1..6f46c525 100644
--- a/modules/comments.js
+++ b/modules/comments.js
@@ -1,11 +1,11 @@
let editor = codex.editor;
-var comments = function(comments) {
+module.exports = function (comments) {
var draw = {
- commentsField: function(blockId) {
+ commentsField: function (blockId) {
var field = editor.draw.node('DIV', 'ce-comments-field');
@@ -15,7 +15,7 @@ var comments = function(comments) {
},
- input: function(text) {
+ input: function (text) {
var wrapper = editor.draw.node('DIV', 'ce-comment'),
input = editor.draw.node('DIV', 'ce-comment__input', {'contentEditable': 'true', 'textContent':text||''}),
@@ -35,19 +35,19 @@ var comments = function(comments) {
},
- comment: function(data) {
+ comment: function (data) {
if (!data.text) return;
var wrapper = editor.draw.node('DIV', 'ce-comment'),
text = editor.draw.node('DIV', 'ce-comment__text', {'textContent': data.text}),
- date = new Date().toLocaleDateString('en-US',{
- month: 'short',
- day: 'numeric',
- hour: 'numeric',
- minute: 'numeric',
- hour12: false
- }),
+ date = new Date().toLocaleDateString('en-US', {
+ month: 'short',
+ day: 'numeric',
+ hour: 'numeric',
+ minute: 'numeric',
+ hour12: false
+ }),
time = editor.draw.node('DIV', 'ce-comment__time', {'textContent': date}),
deleteBtn = editor.draw.node('DIV', 'ce-comment__delete', {'textContent': 'Delete'}),
editBtn = editor.draw.node('DIV', 'ce-comment__edit', {'textContent': 'Edit'});
@@ -71,7 +71,7 @@ var comments = function(comments) {
var callbacks = {
- commentClicked: function(e) {
+ commentClicked: function (e) {
var field = e.path[2],
wrapper = e.path[1],
@@ -80,15 +80,15 @@ var comments = function(comments) {
if (input.textContent.trim() == '') return;
var comment = draw.comment({
- text: input.textContent,
- edited: wrapper.dataset.edited
+ text: input.textContent,
+ edited: wrapper.dataset.edited
});
field.replaceChild(comment, wrapper);
},
- editClicked: function(e) {
+ editClicked: function (e) {
var field = e.path[2],
@@ -101,7 +101,7 @@ var comments = function(comments) {
},
- deleteClicked: function(e) {
+ deleteClicked: function (e) {
var field = e.path[2],
wrapper = e.path[1];
@@ -114,20 +114,20 @@ var comments = function(comments) {
var methods = {
- getCoords: function(block) {
+ getCoords: function (block) {
var rect = block.getBoundingClientRect();
return {
- x: pageXOffset + rect.left,
- y: pageYOffset + rect.top
- }
+ x: window.pageXOffset + rect.left,
+ y: window.pageYOffset + rect.top
+ };
}
- }
+ };
- comments.add = function(block) {
+ comments.add = function (block) {
var blockId = block.dataset.id;
@@ -151,6 +151,4 @@ var comments = function(comments) {
return comments;
-}({});
-
-module.exports = comments;
\ No newline at end of file
+}({});
\ No newline at end of file
diff --git a/modules/draw.js b/modules/draw.js
index eec91ae5..9d5d64d3 100644
--- a/modules/draw.js
+++ b/modules/draw.js
@@ -36,7 +36,7 @@ module.exports = (function (draw) {
/**
* Comments side bar
*/
- draw.commentsSidebar = function() {
+ draw.commentsSidebar = function () {
var sidebar = draw.node('DIV', 'ce-comments-sidebar');
@@ -44,7 +44,7 @@ module.exports = (function (draw) {
};
- draw.ceBlock = function() {
+ draw.ceBlock = function () {
var block = document.createElement('DIV');
@@ -230,6 +230,7 @@ module.exports = (function (draw) {
toggler.innerHTML = '';
return toggler;
+
};
/**
diff --git a/modules/saver.js b/modules/saver.js
index 0819206d..74162891 100644
--- a/modules/saver.js
+++ b/modules/saver.js
@@ -135,9 +135,9 @@ module.exports = (function (saver) {
};
- saver.saveComments = function() {
+ saver.saveComments = function () {
- var fields = codex.nodes.commentsSidebar.querySelectorAll('.ce-comments-field');
+ var fields = editor.nodes.commentsSidebar.querySelectorAll('.ce-comments-field');
for (var i = 0 ; i < fields.length; i++) {
@@ -152,23 +152,24 @@ module.exports = (function (saver) {
if (!text) continue;
var comment = {
- edited : comments[j].dataset.edited,
- text : text.textContent,
- time : time.textContent
+ edited : comments[j].dataset.edited,
+ text : text.textContent,
+ time : time.textContent
};
commentsData.push(comment);
+
}
if (!commentsData.length) continue;
var fieldData = {
- blockId : fields[i].dataset.blockId,
- comments : commentsData
+ blockId : fields[i].dataset.blockId,
+ comments : commentsData
};
- codex.state.comments.push(fieldData);
+ editor.state.comments.push(fieldData);
}
diff --git a/modules/ui.js b/modules/ui.js
index a262d805..35fb315e 100644
--- a/modules/ui.js
+++ b/modules/ui.js
@@ -82,13 +82,13 @@ module.exports = (function (ui) {
toolbarContent = editor.draw.toolbarContent();
plusButton = editor.draw.plusButton();
showSettingsButton = editor.draw.settingsButton();
- showCommentButton = codex.draw.commentButton();
+ showCommentButton = editor.draw.commentButton();
showTrashButton = editor.toolbar.settings.makeRemoveBlockButton();
blockSettings = editor.draw.blockSettings();
blockButtons = editor.draw.blockButtons();
toolbox = editor.draw.toolbox();
redactor = editor.draw.redactor();
- commentsSidebar = codex.draw.commentsSidebar();
+ commentsSidebar = editor.draw.commentsSidebar();
/** settings */
var defaultSettings = editor.draw.defaultSettings(),
diff --git a/whatwg-fetch.js b/whatwg-fetch.js
index 0d13206b..03d47791 100644
--- a/whatwg-fetch.js
+++ b/whatwg-fetch.js
@@ -1,2 +1,2 @@
-var codex=codex||{};codex.editor=function(t){function e(o){if(r[o])return r[o].exports;var n=r[o]={exports:{},id:o,loaded:!1};return t[o].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var r={};return e.m=t,e.c=r,e.p="",e(0)}([function(t,e){!function(t){"use strict";function e(t){if("string"!=typeof t&&(t=String(t)),/[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(t))throw new TypeError("Invalid character in header field name");return t.toLowerCase()}function r(t){return"string"!=typeof t&&(t=String(t)),t}function o(t){var e={next:function(){var e=t.shift();return{done:void 0===e,value:e}}};return m.iterable&&(e[Symbol.iterator]=function(){return e}),e}function n(t){this.map={},t instanceof n?t.forEach(function(t,e){this.append(e,t)},this):t&&Object.getOwnPropertyNames(t).forEach(function(e){this.append(e,t[e])},this)}function i(t){return t.bodyUsed?Promise.reject(new TypeError("Already read")):void(t.bodyUsed=!0)}function s(t){return new Promise(function(e,r){t.onload=function(){e(t.result)},t.onerror=function(){r(t.error)}})}function a(t){var e=new FileReader,r=s(e);return e.readAsArrayBuffer(t),r}function u(t){var e=new FileReader,r=s(e);return e.readAsText(t),r}function f(t){for(var e=new Uint8Array(t),r=new Array(e.length),o=0;o