diff --git a/build/codex-editor.js b/build/codex-editor.js index 8e56ae99..c3350f77 100644 --- a/build/codex-editor.js +++ b/build/codex-editor.js @@ -103,7 +103,7 @@ return /******/ (function(modules) { // webpackBootstrap /*! no static exports found */ /***/ (function(module, exports) { -module.exports = "\n" +module.exports = "\n" /***/ }), @@ -483,7 +483,7 @@ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Cons */ // eslint-disable-next-line var modules = ["api-blocks.ts","api-events.ts","api-listener.ts","api-sanitizer.ts","api-saver.ts","api-selection.ts","api-toolbar.ts","api.ts","block-events.ts","blockManager.js","caret.js","events.js","listeners.js","renderer.js","sanitizer.js","saver.js","toolbar-blockSettings.js","toolbar-inline.ts","toolbar-toolbox.js","toolbar.js","tools.js","ui.js"].map(function (module) { - return __webpack_require__("./src/components/modules sync [^_](api-blocks.ts|api-events.ts|api-listener.ts|api-sanitizer.ts|api-saver.ts|api-selection.ts|api-toolbar.ts|api.ts|block-events.ts|blockManager.js|caret.js|events.js|listeners.js|renderer.js|sanitizer.js|saver.js|toolbar-blockSettings.js|toolbar-inline.ts|toolbar-toolbox.js|toolbar.js|tools.js|ui.js)$")("./" + module); + return __webpack_require__("./src/components/modules sync recursive ^\\.\\/.*$")("./" + module); }); /** @@ -2420,35 +2420,83 @@ module.exports = exports['default']; /***/ }), -/***/ "./src/components/modules sync [^_](api-blocks.ts|api-events.ts|api-listener.ts|api-sanitizer.ts|api-saver.ts|api-selection.ts|api-toolbar.ts|api.ts|block-events.ts|blockManager.js|caret.js|events.js|listeners.js|renderer.js|sanitizer.js|saver.js|toolbar-blockSettings.js|toolbar-inline.ts|toolbar-toolbox.js|toolbar.js|tools.js|ui.js)$": -/*!********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************!*\ - !*** ./src/components/modules sync nonrecursive [^_](api-blocks.ts|api-events.ts|api-listener.ts|api-sanitizer.ts|api-saver.ts|api-selection.ts|api-toolbar.ts|api.ts|block-events.ts|blockManager.js|caret.js|events.js|listeners.js|renderer.js|sanitizer.js|saver.js|toolbar-blockSettings.js|toolbar-inline.ts|toolbar-toolbox.js|toolbar.js|tools.js|ui.js)$ ***! - \********************************************************************************************************************************************************************************************************************************************************************************************************************************************************************/ +/***/ "./src/components/modules sync recursive ^\\.\\/.*$": +/*!**********************************************!*\ + !*** ./src/components/modules sync ^\.\/.*$ ***! + \**********************************************/ /*! no static exports found */ /***/ (function(module, exports, __webpack_require__) { var map = { + "./_anchors": "./src/components/modules/_anchors.js", + "./_anchors.js": "./src/components/modules/_anchors.js", + "./_callbacks": "./src/components/modules/_callbacks.js", + "./_callbacks.js": "./src/components/modules/_callbacks.js", + "./_caret": "./src/components/modules/_caret.js", + "./_caret.js": "./src/components/modules/_caret.js", + "./_content": "./src/components/modules/_content.js", + "./_content.js": "./src/components/modules/_content.js", + "./_destroyer": "./src/components/modules/_destroyer.js", + "./_destroyer.js": "./src/components/modules/_destroyer.js", + "./_notifications": "./src/components/modules/_notifications.js", + "./_notifications.js": "./src/components/modules/_notifications.js", + "./_parser": "./src/components/modules/_parser.js", + "./_parser.js": "./src/components/modules/_parser.js", + "./_paste": "./src/components/modules/_paste.js", + "./_paste.js": "./src/components/modules/_paste.js", + "./_transport": "./src/components/modules/_transport.js", + "./_transport.js": "./src/components/modules/_transport.js", + "./api": "./src/components/modules/api.ts", + "./api-blocks": "./src/components/modules/api-blocks.ts", "./api-blocks.ts": "./src/components/modules/api-blocks.ts", + "./api-events": "./src/components/modules/api-events.ts", "./api-events.ts": "./src/components/modules/api-events.ts", + "./api-listener": "./src/components/modules/api-listener.ts", "./api-listener.ts": "./src/components/modules/api-listener.ts", + "./api-sanitizer": "./src/components/modules/api-sanitizer.ts", "./api-sanitizer.ts": "./src/components/modules/api-sanitizer.ts", + "./api-saver": "./src/components/modules/api-saver.ts", "./api-saver.ts": "./src/components/modules/api-saver.ts", + "./api-selection": "./src/components/modules/api-selection.ts", "./api-selection.ts": "./src/components/modules/api-selection.ts", + "./api-toolbar": "./src/components/modules/api-toolbar.ts", "./api-toolbar.ts": "./src/components/modules/api-toolbar.ts", "./api.ts": "./src/components/modules/api.ts", + "./block-events": "./src/components/modules/block-events.ts", "./block-events.ts": "./src/components/modules/block-events.ts", + "./blockManager": "./src/components/modules/blockManager.js", "./blockManager.js": "./src/components/modules/blockManager.js", + "./caret": "./src/components/modules/caret.js", "./caret.js": "./src/components/modules/caret.js", + "./events": "./src/components/modules/events.js", "./events.js": "./src/components/modules/events.js", + "./listeners": "./src/components/modules/listeners.js", "./listeners.js": "./src/components/modules/listeners.js", + "./renderer": "./src/components/modules/renderer.js", "./renderer.js": "./src/components/modules/renderer.js", + "./sanitizer": "./src/components/modules/sanitizer.js", "./sanitizer.js": "./src/components/modules/sanitizer.js", + "./saver": "./src/components/modules/saver.js", "./saver.js": "./src/components/modules/saver.js", + "./toolbar": "./src/components/modules/toolbar.js", + "./toolbar-blockSettings": "./src/components/modules/toolbar-blockSettings.js", "./toolbar-blockSettings.js": "./src/components/modules/toolbar-blockSettings.js", + "./toolbar-inline": "./src/components/modules/toolbar-inline.ts", "./toolbar-inline.ts": "./src/components/modules/toolbar-inline.ts", + "./toolbar-toolbox": "./src/components/modules/toolbar-toolbox.js", "./toolbar-toolbox.js": "./src/components/modules/toolbar-toolbox.js", "./toolbar.js": "./src/components/modules/toolbar.js", + "./toolbar/inline": "./src/components/modules/toolbar/inline.js", + "./toolbar/inline.js": "./src/components/modules/toolbar/inline.js", + "./toolbar/settings": "./src/components/modules/toolbar/settings.js", + "./toolbar/settings.js": "./src/components/modules/toolbar/settings.js", + "./toolbar/toolbar": "./src/components/modules/toolbar/toolbar.js", + "./toolbar/toolbar.js": "./src/components/modules/toolbar/toolbar.js", + "./toolbar/toolbox": "./src/components/modules/toolbar/toolbox.js", + "./toolbar/toolbox.js": "./src/components/modules/toolbar/toolbox.js", + "./tools": "./src/components/modules/tools.js", "./tools.js": "./src/components/modules/tools.js", + "./ui": "./src/components/modules/ui.js", "./ui.js": "./src/components/modules/ui.js" }; @@ -2471,7 +2519,2765 @@ webpackContext.keys = function webpackContextKeys() { }; webpackContext.resolve = webpackContextResolve; module.exports = webpackContext; -webpackContext.id = "./src/components/modules sync [^_](api-blocks.ts|api-events.ts|api-listener.ts|api-sanitizer.ts|api-saver.ts|api-selection.ts|api-toolbar.ts|api.ts|block-events.ts|blockManager.js|caret.js|events.js|listeners.js|renderer.js|sanitizer.js|saver.js|toolbar-blockSettings.js|toolbar-inline.ts|toolbar-toolbox.js|toolbar.js|tools.js|ui.js)$"; +webpackContext.id = "./src/components/modules sync recursive ^\\.\\/.*$"; + +/***/ }), + +/***/ "./src/components/modules/_anchors.js": +/*!********************************************!*\ + !*** ./src/components/modules/_anchors.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor Anchors module + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = function (anchors) { + var editor = codex.editor; + + anchors.input = null; + anchors.currentNode = null; + + anchors.settingsOpened = function (currentBlock) { + anchors.currentNode = currentBlock; + anchors.input.value = anchors.currentNode.dataset.anchor || ''; + }; + + anchors.anchorChanged = function (e) { + var newAnchor = e.target.value = anchors.rusToTranslit(e.target.value); + + anchors.currentNode.dataset.anchor = newAnchor; + + if (newAnchor.trim() !== '') { + anchors.currentNode.classList.add(editor.ui.className.BLOCK_WITH_ANCHOR); + } else { + anchors.currentNode.classList.remove(editor.ui.className.BLOCK_WITH_ANCHOR); + } + }; + + anchors.keyDownOnAnchorInput = function (e) { + if (e.keyCode == editor.core.keys.ENTER) { + e.preventDefault(); + e.stopPropagation(); + + e.target.blur(); + editor.toolbar.settings.close(); + } + }; + + anchors.keyUpOnAnchorInput = function (e) { + if (e.keyCode >= editor.core.keys.LEFT && e.keyCode <= editor.core.keys.DOWN) { + e.stopPropagation(); + } + }; + + anchors.rusToTranslit = function (string) { + var ru = ['А', 'Б', 'В', 'Г', 'Д', 'Е', 'Ё', 'Ж', 'З', 'И', 'Й', 'К', 'Л', 'М', 'Н', 'О', 'П', 'Р', 'С', 'Т', 'У', 'Ф', 'Х', 'Ц', 'Ч', 'Ш', 'Щ', 'Ь', 'Ы', 'Ь', 'Э', 'Ю', 'Я'], + en = ['A', 'B', 'V', 'G', 'D', 'E', 'E', 'Zh', 'Z', 'I', 'Y', 'K', 'L', 'M', 'N', 'O', 'P', 'R', 'S', 'T', 'U', 'F', 'H', 'C', 'Ch', 'Sh', 'Sch', '', 'Y', '', 'E', 'Yu', 'Ya']; + + for (var i = 0; i < ru.length; i++) { + string = string.split(ru[i]).join(en[i]); + string = string.split(ru[i].toLowerCase()).join(en[i].toLowerCase()); + } + + string = string.replace(/[^0-9a-zA-Z_]+/g, '-'); + + return string; + }; + + return anchors; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_callbacks.js": +/*!**********************************************!*\ + !*** ./src/components/modules/_callbacks.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * @module Codex Editor Callbacks module + * @description Module works with editor added Elements + * + * @author Codex Team + * @version 1.4.0 + */ + +module.exports = function (callbacks) { + var editor = codex.editor; + + /** + * used by UI module + * @description Routes all keydowns on document + * @param {Object} event + */ + callbacks.globalKeydown = function (event) { + switch (event.keyCode) { + case editor.core.keys.ENTER: + enterKeyPressed_(event);break; + } + }; + + /** + * used by UI module + * @description Routes all keydowns on redactors area + * @param {Object} event + */ + callbacks.redactorKeyDown = function (event) { + switch (event.keyCode) { + case editor.core.keys.TAB: + tabKeyPressedOnRedactorsZone_(event);break; + case editor.core.keys.ENTER: + enterKeyPressedOnRedactorsZone_(event);break; + case editor.core.keys.ESC: + escapeKeyPressedOnRedactorsZone_(event);break; + default: + defaultKeyPressedOnRedactorsZone_(event);break; + } + }; + + /** + * used by UI module + * @description Routes all keyup events + * @param {Object} event + */ + callbacks.globalKeyup = function (event) { + switch (event.keyCode) { + case editor.core.keys.UP: + case editor.core.keys.LEFT: + case editor.core.keys.RIGHT: + case editor.core.keys.DOWN: + arrowKeyPressed_(event);break; + } + }; + + /** + * @param {Object} event + * @private + * + * Handles behaviour when tab pressed + * @description if Content is empty show toolbox (if it is closed) or leaf tools + * uses Toolbars toolbox module to handle the situation + */ + var tabKeyPressedOnRedactorsZone_ = function tabKeyPressedOnRedactorsZone_(event) { + /** + * Wait for solution. Would like to know the behaviour + * @todo Add spaces + */ + event.preventDefault(); + + if (!editor.core.isBlockEmpty(editor.content.currentNode)) { + return; + } + + if (!editor.toolbar.opened) { + editor.toolbar.open(); + } + + if (editor.toolbar.opened && !editor.toolbar.toolbox.opened) { + editor.toolbar.toolbox.open(); + } else { + editor.toolbar.toolbox.leaf(); + } + }; + + /** + * Handles global EnterKey Press + * @see enterPressedOnBlock_ + * @param {Object} event + */ + var enterKeyPressed_ = function enterKeyPressed_() { + if (editor.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 + */ + editor.caret.inputIndex = -1; + + enterPressedOnBlock_(); + } + }; + + /** + * Callback for enter key pressing in first-level block area + * + * @param {Event} event + * @private + * + * @description Inserts new block with initial type from settings + */ + var enterPressedOnBlock_ = function enterPressedOnBlock_() { + var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; + + editor.content.insertBlock({ + type: NEW_BLOCK_TYPE, + block: editor.tools[NEW_BLOCK_TYPE].render() + }, true); + + editor.toolbar.move(); + editor.toolbar.open(); + }; + + /** + * ENTER key handler + * + * @param {Object} event + * @private + * + * @description Makes new block with initial type from settings + */ + var enterKeyPressedOnRedactorsZone_ = function enterKeyPressedOnRedactorsZone_(event) { + if (event.target.contentEditable == 'true') { + /** Update input index */ + editor.caret.saveCurrentInputIndex(); + } + + var currentInputIndex = editor.caret.getCurrentInputIndex() || 0, + workingNode = editor.content.currentNode, + tool = workingNode.dataset.tool, + isEnterPressedOnToolbar = editor.toolbar.opened && editor.toolbar.current && event.target == editor.state.inputs[currentInputIndex]; + + /** The list of tools which needs the default browser behaviour */ + var enableLineBreaks = editor.tools[tool].enableLineBreaks; + + /** This type of block creates when enter is pressed */ + var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; + + /** + * When toolbar is opened, select tool instead of making new paragraph + */ + if (isEnterPressedOnToolbar) { + event.preventDefault(); + + editor.toolbar.toolbox.toolClicked(event); + + editor.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 currentSelection = window.getSelection(), + currentSelectedNode = currentSelection.anchorNode, + caretAtTheEndOfText = editor.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) { + editor.callback.enterPressedOnBlock(editor.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 == editor.core.nodeTypes.TEXT && !isTextNodeHasParentBetweenContenteditable && !caretAtTheEndOfText) { + event.preventDefault(); + + editor.core.log('Splitting Text node...'); + + editor.content.splitBlock(currentInputIndex); + + /** Show plus button when next input after split is empty*/ + if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) { + editor.toolbar.showPlusButton(); + } + } else { + var islastNode = editor.content.isLastNode(currentSelectedNode); + + if (islastNode && caretAtTheEndOfText) { + event.preventDefault(); + event.stopPropagation(); + event.stopImmediatePropagation(); + + editor.core.log('ENTER clicked in last textNode. Create new BLOCK'); + + editor.content.insertBlock({ + type: NEW_BLOCK_TYPE, + block: editor.tools[NEW_BLOCK_TYPE].render() + }, true); + + editor.toolbar.move(); + editor.toolbar.open(); + + /** Show plus button with empty block */ + editor.toolbar.showPlusButton(); + } + } + + /** get all inputs after new appending block */ + editor.ui.saveInputs(); + }; + + /** + * Escape behaviour + * @param event + * @private + * + * @description Closes toolbox and toolbar. Prevents default behaviour + */ + var escapeKeyPressedOnRedactorsZone_ = function escapeKeyPressedOnRedactorsZone_(event) { + /** Close all toolbar */ + editor.toolbar.close(); + + /** Close toolbox */ + editor.toolbar.toolbox.close(); + + event.preventDefault(); + }; + + /** + * @param {Event} event + * @private + * + * closes and moves toolbar + */ + var arrowKeyPressed_ = function arrowKeyPressed_(event) { + editor.content.workingNodeChanged(); + + /* Closing toolbar */ + editor.toolbar.close(); + editor.toolbar.move(); + }; + + /** + * @private + * @param {Event} event + * + * @description Closes all opened bars from toolbar. + * If block is mark, clears highlightning + */ + var defaultKeyPressedOnRedactorsZone_ = function defaultKeyPressedOnRedactorsZone_() { + editor.toolbar.close(); + + if (!editor.toolbar.inline.actionsOpened) { + editor.toolbar.inline.close(); + editor.content.clearMark(); + } + }; + + /** + * Handler when clicked on redactors area + * + * @protected + * @param event + * + * @description Detects clicked area. If it is first-level block area, marks as detected and + * on next enter press will be inserted new block + * Otherwise, save carets position (input index) and put caret to the editable zone. + * + * @see detectWhenClickedOnFirstLevelBlockArea_ + * + */ + callbacks.redactorClicked = function (event) { + detectWhenClickedOnFirstLevelBlockArea_(); + + editor.content.workingNodeChanged(event.target); + editor.ui.saveInputs(); + + var selectedText = editor.toolbar.inline.getSelectionText(), + firstLevelBlock; + + /** If selection range took off, then we hide inline toolbar */ + if (selectedText.length === 0) { + editor.toolbar.inline.close(); + } + + /** Update current input index in memory when caret focused into existed input */ + if (event.target.contentEditable == 'true') { + editor.caret.saveCurrentInputIndex(); + } + + if (editor.content.currentNode === null) { + /** + * If inputs in redactor does not exits, then we put input index 0 not -1 + */ + var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0; + + /** If we have any inputs */ + if (editor.state.inputs.length) { + /** getting firstlevel parent of input */ + firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]); + } + + /** If input is empty, then we set caret to the last input */ + if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) { + editor.caret.setToBlock(indexOfLastInput); + } else { + /** Create new input when caret clicked in redactors area */ + var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin; + + editor.content.insertBlock({ + type: NEW_BLOCK_TYPE, + block: editor.tools[NEW_BLOCK_TYPE].render() + }); + + /** If there is no inputs except inserted */ + if (editor.state.inputs.length === 1) { + editor.caret.setToBlock(indexOfLastInput); + } else { + /** Set caret to this appended input */ + editor.caret.setToNextBlock(indexOfLastInput); + } + } + } else { + /** Close all panels */ + editor.toolbar.settings.close(); + editor.toolbar.toolbox.close(); + } + + /** + * Move toolbar and open + */ + editor.toolbar.move(); + editor.toolbar.open(); + + var inputIsEmpty = !editor.content.currentNode.textContent.trim(), + currentNodeType = editor.content.currentNode.dataset.tool, + isInitialType = currentNodeType == editor.settings.initialBlockPlugin; + + /** Hide plus buttons */ + editor.toolbar.hidePlusButton(); + + if (!inputIsEmpty) { + /** Mark current block */ + editor.content.markBlock(); + } + + if (isInitialType && inputIsEmpty) { + /** Show plus button */ + editor.toolbar.showPlusButton(); + } + }; + + /** + * This method allows to define, is caret in contenteditable element or not. + * + * @private + * + * @description 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. + */ + var detectWhenClickedOnFirstLevelBlockArea_ = function detectWhenClickedOnFirstLevelBlockArea_() { + var selection = window.getSelection(), + anchorNode = selection.anchorNode, + flag = false; + + if (selection.rangeCount === 0) { + editor.content.editorAreaHightlighted = true; + } else { + if (!editor.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" */ + editor.content.editorAreaHightlighted = !flag; + } + }; + + /** + * Toolbar button click handler + * + * @param {Object} event - cursor to the button + * @protected + * + * @description gets current tool and calls render method + */ + callbacks.toolbarButtonClicked = function (event) { + var button = this; + + editor.toolbar.current = button.dataset.type; + + editor.toolbar.toolbox.toolClicked(event); + editor.toolbar.close(); + }; + + /** + * Show or Hide toolbox when plus button is clicked + */ + callbacks.plusButtonClicked = function () { + if (!editor.nodes.toolbox.classList.contains('opened')) { + editor.toolbar.toolbox.open(); + } else { + editor.toolbar.toolbox.close(); + } + }; + + /** + * Block handlers for KeyDown events + * + * @protected + * @param {Object} event + * + * Handles keydowns on block + * @see blockRightOrDownArrowPressed_ + * @see backspacePressed_ + * @see blockLeftOrUpArrowPressed_ + */ + callbacks.blockKeydown = function (event) { + var block = event.target; // event.target is input + + switch (event.keyCode) { + case editor.core.keys.DOWN: + case editor.core.keys.RIGHT: + blockRightOrDownArrowPressed_(event); + break; + + case editor.core.keys.BACKSPACE: + backspacePressed_(block, event); + break; + + case editor.core.keys.UP: + case editor.core.keys.LEFT: + blockLeftOrUpArrowPressed_(event); + break; + } + }; + + /** + * RIGHT or DOWN keydowns on block + * + * @param {Object} event + * @private + * + * @description watches the selection and gets closest editable element. + * Uses method getDeepestTextNodeFromPosition to get the last node of next block + * Sets caret if it is contenteditable + */ + var blockRightOrDownArrowPressed_ = function blockRightOrDownArrowPressed_(event) { + var selection = window.getSelection(), + inputs = editor.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) { + editor.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 (editor.core.isDomNode(lastChild)) { + deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length); + } else { + deepestTextnode = lastChild; + } + + caretInLastChild = selection.anchorNode == deepestTextnode; + caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset; + + if (!caretInLastChild || !caretAtTheEndOfText) { + editor.core.log('arrow [down|right] : caret does not reached the end'); + return false; + } + + editor.caret.setToNextBlock(editableElementIndex); + }; + + /** + * LEFT or UP keydowns on block + * + * @param {Object} event + * @private + * + * watches the selection and gets closest editable element. + * Uses method getDeepestTextNodeFromPosition to get the last node of previous block + * Sets caret if it is contenteditable + * + */ + var blockLeftOrUpArrowPressed_ = function blockLeftOrUpArrowPressed_(event) { + var selection = window.getSelection(), + inputs = editor.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) { + editor.caret.setToPreviousBlock(editableElementIndex); + return; + } + + firstChild = focusedNode.childNodes[0]; + + if (editor.core.isDomNode(firstChild)) { + deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0); + } else { + deepestTextnode = firstChild; + } + + caretInFirstChild = selection.anchorNode == deepestTextnode; + caretAtTheBeginning = selection.anchorOffset === 0; + + if (caretInFirstChild && caretAtTheBeginning) { + editor.caret.setToPreviousBlock(editableElementIndex); + } + }; + + /** + * Handles backspace keydown + * + * @param {Element} block + * @param {Object} event + * @private + * + * @description if block is empty, delete the block and set caret to the previous block + * If block is not empty, try to merge two blocks - current and previous + * But it we try'n to remove first block, then we should set caret to the next block, not previous. + * If we removed the last block, create new one + */ + var backspacePressed_ = function backspacePressed_(block, event) { + var currentInputIndex = editor.caret.getCurrentInputIndex(), + range, + selectionLength, + firstLevelBlocksCount; + + if (editor.core.isNativeInput(event.target)) { + /** If input value is empty - remove block */ + if (event.target.value.trim() == '') { + block.remove(); + } else { + return; + } + } + + if (block.textContent.trim()) { + range = editor.content.getRange(); + selectionLength = range.endOffset - range.startOffset; + + if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) { + editor.content.mergeBlocks(currentInputIndex); + } else { + return; + } + } + + if (!selectionLength) { + block.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(); + + /** Updating inputs state after deleting last block */ + editor.ui.saveInputs(); + + /** Set to current appended block */ + window.setTimeout(function () { + editor.caret.setToPreviousBlock(1); + }, 10); + } else { + if (editor.caret.inputIndex !== 0) { + /** Target block is not first */ + editor.caret.setToPreviousBlock(editor.caret.inputIndex); + } else { + /** If we try to delete first block */ + editor.caret.setToNextBlock(editor.caret.inputIndex); + } + } + + editor.toolbar.move(); + + if (!editor.toolbar.opened) { + editor.toolbar.open(); + } + + /** Updating inputs state */ + editor.ui.saveInputs(); + + /** Prevent default browser behaviour */ + event.preventDefault(); + }; + + /** + * used by UI module + * Clicks on block settings button + * + * @param {Object} event + * @protected + * @description Opens toolbar settings + */ + callbacks.showSettingsButtonClicked = function (event) { + /** + * 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 = editor.content.currentNode.dataset.tool; + + editor.toolbar.settings.toggle(currentToolType); + + /** Close toolbox when settings button is active */ + editor.toolbar.toolbox.close(); + editor.toolbar.settings.hideRemoveActions(); + }; + + return callbacks; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_caret.js": +/*!******************************************!*\ + !*** ./src/components/modules/_caret.js ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor Caret Module + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = function (caret) { + var editor = codex.editor; + + /** + * @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.contentEditable != 'true') { + 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; + } + }; + + /** + * Inserts node at the caret location + * @param {HTMLElement|DocumentFragment} node + */ + caret.insertNode = function (node) { + var selection, + range, + lastNode = node; + + if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) { + lastNode = node.lastChild; + } + + selection = window.getSelection(); + + range = selection.getRangeAt(0); + range.deleteContents(); + + range.insertNode(node); + + range.setStartAfter(lastNode); + range.collapse(true); + + selection.removeAllRanges(); + selection.addRange(range); + }; + + return caret; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_content.js": +/*!********************************************!*\ + !*** ./src/components/modules/_content.js ***! + \********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /** + * Codex Editor Content Module + * Works with DOM + * + * @class Content + * @classdesc Class works provides COdex Editor appearance logic + * + * @author Codex Team + * @version 2.0.0 + */ + +var _dom = __webpack_require__(/*! ../dom */ "./src/components/dom.js"); + +var _dom2 = _interopRequireDefault(_dom); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +module.exports = function () { + _createClass(Content, null, [{ + key: 'name', + + /** + * Module key name + * @returns {string} + */ + get: function get() { + return 'Content'; + } + + /** + * @constructor + * + * @param {EditorConfig} config + */ + + }]); + + function Content(config) { + _classCallCheck(this, Content); + + this.config = config; + this.Editor = null; + + this.CSS = { + block: 'ce-block', + content: 'ce-block__content', + stretched: 'ce-block--stretched', + highlighted: 'ce-block--highlighted' + }; + + this._currentNode = null; + this._currentIndex = 0; + } + + /** + * Editor modules setter + * @param {object} Editor + */ + + + _createClass(Content, [{ + key: 'composeBlock_', + + + /** + * @private + * @param pluginHTML + * @param {Boolean} isStretched - make stretched block or not + * + * @description adds necessary information to wrap new created block by first-level holder + */ + value: function composeBlock_(pluginHTML) { + var isStretched = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; + + var block = _dom2.default.make('DIV', this.CSS.block), + blockContent = _dom2.default.make('DIV', this.CSS.content); + + blockContent.appendChild(pluginHTML); + block.appendChild(blockContent); + + if (isStretched) { + blockContent.classList.add(this.CSS.stretched); + } + + block.dataset.toolId = this._currentIndex++; + + return block; + } + }, { + key: 'getFirstLevelBlock', + + + /** + * Finds first-level block + * @description looks for first-level block. + * gets parent while node is not first-level + * + * @param {Element} node - selected or clicked in redactors area node + * @protected + * + */ + value: function getFirstLevelBlock(node) { + if (!_dom2.default.isElement(node)) { + node = node.parentNode; + } + + if (node === this.Editor.ui.nodes.redactor || node === document.body) { + return null; + } else { + while (node.classList && !node.classList.contains(this.CSS.block)) { + node = node.parentNode; + } + + return node; + } + } + }, { + key: 'insertBlock', + + + /** + * Insert new block to working area + * + * @param {HTMLElement} tool + * + * @returns {Number} tool index + * + */ + value: function insertBlock(tool) { + var newBlock = this.composeBlock_(tool); + + if (this.currentNode) { + this.currentNode.insertAdjacentElement('afterend', newBlock); + } else { + /** + * If redactor is empty, append as first child + */ + this.Editor.ui.nodes.redactor.appendChild(newBlock); + } + + /** + * Set new node as current + */ + this.currentNode = newBlock; + + return newBlock.dataset.toolId; + } + }, { + key: 'state', + set: function set(Editor) { + this.Editor = Editor; + } + + /** + * Get current working node + * + * @returns {null|HTMLElement} + */ + + }, { + key: 'currentNode', + get: function get() { + return this._currentNode; + } + + /** + * Set working node. Working node should be first level block, so we find it before set one to _currentNode property + * + * @param {HTMLElement} node + */ + , + set: function set(node) { + var firstLevelBlock = this.getFirstLevelBlock(node); + + this._currentNode = firstLevelBlock; + } + }]); + + return Content; +}(); + +// module.exports = (function (content) { +// +// let editor = codex.editor; +// +// /** +// * Links to current active block +// * @type {null | Element} +// */ +// content.currentNode = null; +// +// /** +// * clicked in redactor area +// * @type {null | Boolean} +// */ +// content.editorAreaHightlighted = null; +// +// /** +// * @deprecated +// * Synchronizes redactor with original textarea +// */ +// content.sync = function () { +// +// editor.core.log('syncing...'); +// +// /** +// * Save redactor content to editor.state +// */ +// editor.state.html = editor.nodes.redactor.innerHTML; +// +// }; +// +// /** +// * Appends background to the block +// * +// * @description add CSS class to highlight visually first-level block area +// */ +// content.markBlock = function () { +// +// editor.content.currentNode.classList.add(editor.ui.className.BLOCK_HIGHLIGHTED); +// +// }; +// +// /** +// * Clear background +// * +// * @description clears styles that highlights block +// */ +// content.clearMark = function () { +// +// if (editor.content.currentNode) { +// +// editor.content.currentNode.classList.remove(editor.ui.className.BLOCK_HIGHLIGHTED); +// +// } +// +// }; +// +// /** +// * Finds first-level block +// * +// * @param {Element} node - selected or clicked in redactors area node +// * @protected +// * +// * @description looks for first-level block. +// * gets parent while node is not first-level +// */ +// content.getFirstLevelBlock = function (node) { +// +// if (!editor.core.isDomNode(node)) { +// +// node = node.parentNode; +// +// } +// +// if (node === editor.nodes.redactor || node === document.body) { +// +// return null; +// +// } else { +// +// while(!node.classList.contains(editor.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 +// * @protected +// * +// * @description 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 */ +// editor.content.clearMark(); +// +// if (!targetNode) { +// +// return; +// +// } +// +// content.currentNode = content.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 (targetBlock, newBlock) { +// +// if (!targetBlock || !newBlock) { +// +// editor.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(editor.ui.className.BLOCK_CLASSNAME)) { +// +// targetBlock = targetBlock.parentNode; +// +// } +// +// /** Replacing */ +// editor.nodes.redactor.replaceChild(newBlock, targetBlock); +// +// /** +// * Set new node as current +// */ +// editor.content.workingNodeChanged(newBlock); +// +// /** +// * Add block handlers +// */ +// editor.ui.addBlockHandlers(newBlock); +// +// /** +// * Save changes +// */ +// editor.ui.saveInputs(); +// +// }; +// +// /** +// * @protected +// * +// * 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 = editor.content.currentNode, +// newBlockContent = blockData.block, +// blockType = blockData.type, +// isStretched = blockData.stretched; +// +// var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched); +// +// if (workingBlock) { +// +// editor.core.insertAfter(workingBlock, newBlock); +// +// } else { +// +// /** +// * If redactor is empty, append as first child +// */ +// editor.nodes.redactor.appendChild(newBlock); +// +// } +// +// /** +// * Block handler +// */ +// editor.ui.addBlockHandlers(newBlock); +// +// /** +// * Set new node as current +// */ +// editor.content.workingNodeChanged(newBlock); +// +// /** +// * Save changes +// */ +// editor.ui.saveInputs(); +// +// +// if ( needPlaceCaret ) { +// +// /** +// * If we don't know input index then we set default value -1 +// */ +// var currentInputIndex = editor.caret.getCurrentInputIndex() || -1; +// +// +// if (currentInputIndex == -1) { +// +// +// var editableElement = newBlock.querySelector('[contenteditable]'), +// emptyText = document.createTextNode(''); +// +// editableElement.appendChild(emptyText); +// editor.caret.set(editableElement, 0, 0); +// +// editor.toolbar.move(); +// editor.toolbar.showPlusButton(); +// +// +// } else { +// +// if (currentInputIndex === editor.state.inputs.length - 1) +// return; +// +// /** Timeout for browsers execution */ +// window.setTimeout(function () { +// +// /** Setting to the new input */ +// editor.caret.setToNextBlock(currentInputIndex); +// editor.toolbar.move(); +// editor.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) { +// +// tool = tool || editor.content.currentNode.dataset.tool; +// var newBlockComposed = composeNewBlock_(newBlock, tool); +// +// /** Replacing */ +// editor.content.replaceBlock(blockToReplace, newBlockComposed); +// +// /** Save new Inputs when block is changed */ +// editor.ui.saveInputs(); +// +// }; +// +// /** +// * Iterates between child noted and looking for #text node on deepest level +// * @protected +// * +// * @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 == editor.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 lookingFromStart = false; +// +// /** For looking from START */ +// if (position === 0) { +// +// lookingFromStart = true; +// position = 1; +// +// } +// +// while ( position ) { +// +// /** initial verticle of node. */ +// if ( lookingFromStart ) { +// +// block = block.childNodes[0]; +// +// } else { +// +// block = block.childNodes[position - 1]; +// +// } +// +// if ( block.nodeType == editor.core.nodeTypes.TAG ) { +// +// position = block.childNodes.length; +// +// } else if (block.nodeType == editor.core.nodeTypes.TEXT ) { +// +// position = 0; +// +// } +// +// } +// +// return block; +// +// }; +// +// /** +// * @private +// * @param {Element} block - current plugins render +// * @param {String} tool - plugins name +// * @param {Boolean} isStretched - make stretched block or not +// * +// * @description adds necessary information to wrap new created block by first-level holder +// */ +// var composeNewBlock_ = function (block, tool, isStretched) { +// +// var newBlock = editor.draw.node('DIV', editor.ui.className.BLOCK_CLASSNAME, {}), +// blockContent = editor.draw.node('DIV', editor.ui.className.BLOCK_CONTENT, {}); +// +// blockContent.appendChild(block); +// newBlock.appendChild(blockContent); +// +// if (isStretched) { +// +// blockContent.classList.add(editor.ui.className.BLOCK_STRETCHED); +// +// } +// +// newBlock.dataset.tool = tool; +// return newBlock; +// +// }; +// +// /** +// * Returns Range object of current selection +// * @protected +// */ +// content.getRange = function () { +// +// var selection = window.getSelection().getRangeAt(0); +// +// return selection; +// +// }; +// +// /** +// * Divides block in two blocks (after and before caret) +// * +// * @protected +// * @param {int} inputIndex - target input index +// * +// * @description splits current input content to the separate blocks +// * When enter is pressed among the words, that text will be splited. +// */ +// content.splitBlock = function (inputIndex) { +// +// var selection = window.getSelection(), +// anchorNode = selection.anchorNode, +// anchorNodeText = anchorNode.textContent, +// caretOffset = selection.anchorOffset, +// textBeforeCaret, +// textNodeBeforeCaret, +// textAfterCaret, +// textNodeAfterCaret; +// +// var currentBlock = editor.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 */ +// editor.state.inputs[inputIndex].innerHTML = ''; +// +// /** +// * Append all childs founded before anchorNode +// */ +// var previousChildsLength = previousChilds.length; +// +// for(i = 0; i < previousChildsLength; i++) { +// +// editor.state.inputs[inputIndex].appendChild(previousChilds[i]); +// +// } +// +// editor.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 = editor.settings.initialBlockPlugin; +// +// /** +// * Make new paragraph with text after caret +// */ +// editor.content.insertBlock({ +// type : NEW_BLOCK_TYPE, +// block : editor.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 +// * +// * @protected +// * @param {int} currentInputIndex +// * @param {int} targetInputIndex +// * +// * @description gets two inputs indexes and merges into one +// */ +// content.mergeBlocks = function (currentInputIndex, targetInputIndex) { +// +// /** If current input index is zero, then prevent method execution */ +// if (currentInputIndex === 0) { +// +// return; +// +// } +// +// var targetInput, +// currentInputContent = editor.state.inputs[currentInputIndex].innerHTML; +// +// if (!targetInputIndex) { +// +// targetInput = editor.state.inputs[currentInputIndex - 1]; +// +// } else { +// +// targetInput = editor.state.inputs[targetInputIndex]; +// +// } +// +// targetInput.innerHTML += currentInputContent; +// +// }; +// +// /** +// * 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(editor.ui.className.BLOCK_CONTENT) ) { +// +// allChecked = true; +// +// } +// +// } +// +// return true; +// +// }; +// +// /** +// * Checks if all element right siblings is empty +// * @param node +// */ +// var allSiblingsEmpty_ = function (node) { +// +// /** +// * Нужно убедиться, что после пустого соседа ничего нет +// */ +// var sibling = node.nextSibling; +// +// while ( sibling ) { +// +// if (sibling.textContent.length) { +// +// return false; +// +// } +// +// sibling = sibling.nextSibling; +// +// } +// +// return true; +// +// }; +// +// /** +// * @public +// * +// * @param {string} htmlData - html content as string +// * @param {string} plainData - plain text +// * @return {string} - html content as string +// */ +// content.wrapTextWithParagraphs = function (htmlData, plainData) { +// +// if (!htmlData.trim()) { +// +// return wrapPlainTextWithParagraphs(plainData); +// +// } +// +// 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 = htmlData; +// 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; +// +// }; +// +// /** +// * Splits strings on new line and wraps paragraphs with
tag +// * @param plainText +// * @returns {string} +// */ +// var wrapPlainTextWithParagraphs = function (plainText) { +// +// if (!plainText) return ''; +// +// return '
' + plainText.split('\n\n').join('
') + '
'; +// +// }; +// +// /** +// * Finds closest Contenteditable parent from Element +// * @param {Element} node element looking from +// * @return {Element} node contenteditable +// */ +// content.getEditableParent = function (node) { +// +// while (node && node.contentEditable != 'true') { +// +// node = node.parentNode; +// +// } +// +// return node; +// +// }; +// +// /** +// * Clear editors content +// * +// * @param {Boolean} all — if true, delete all article data (content, id, etc.) +// */ +// content.clear = function (all) { +// +// editor.nodes.redactor.innerHTML = ''; +// editor.content.sync(); +// editor.ui.saveInputs(); +// if (all) { +// +// editor.state.blocks = {}; +// +// } else if (editor.state.blocks) { +// +// editor.state.blocks.items = []; +// +// } +// +// editor.content.currentNode = null; +// +// }; +// +// /** +// * +// * Load new data to editor +// * If editor is not empty, just append articleData.items +// * +// * @param articleData.items +// */ +// content.load = function (articleData) { +// +// var currentContent = Object.assign({}, editor.state.blocks); +// +// editor.content.clear(); +// +// if (!Object.keys(currentContent).length) { +// +// editor.state.blocks = articleData; +// +// } else if (!currentContent.items) { +// +// currentContent.items = articleData.items; +// editor.state.blocks = currentContent; +// +// } else { +// +// currentContent.items = currentContent.items.concat(articleData.items); +// editor.state.blocks = currentContent; +// +// } +// +// editor.renderer.makeBlocksFromData(); +// +// }; +// +// return content; +// +// })({}); + +/***/ }), + +/***/ "./src/components/modules/_destroyer.js": +/*!**********************************************!*\ + !*** ./src/components/modules/_destroyer.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"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 Destroyer module + * + * @auhor Codex Team + * @version 1.0 + */ + +module.exports = function (destroyer) { + var editor = codex.editor; + + destroyer.removeNodes = function () { + editor.nodes.wrapper.remove(); + editor.nodes.notifications.remove(); + }; + + destroyer.destroyPlugins = function () { + for (var tool in editor.tools) { + if (typeof editor.tools[tool].destroy === 'function') { + editor.tools[tool].destroy(); + } + } + }; + + destroyer.destroyScripts = function () { + var scripts = document.getElementsByTagName('SCRIPT'); + + for (var i = 0; i < scripts.length; i++) { + if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) { + scripts[i].remove(); + i--; + } + } + }; + + /** + * Delete editor data from webpage. + * You should send settings argument with boolean flags: + * @param settings.ui- remove redactor event listeners and DOM nodes + * @param settings.scripts - remove redactor scripts from DOM + * @param settings.plugins - remove plugin's objects + * @param settings.core - remove editor core. You can remove core only if UI and scripts flags is true + * } + * + */ + destroyer.destroy = function (settings) { + if (!settings || (typeof settings === 'undefined' ? 'undefined' : _typeof(settings)) !== 'object') { + return; + } + + if (settings.ui) { + destroyer.removeNodes(); + editor.listeners.removeAll(); + } + + if (settings.scripts) { + destroyer.destroyScripts(); + } + + if (settings.plugins) { + destroyer.destroyPlugins(); + } + + if (settings.ui && settings.scripts && settings.core) { + delete codex.editor; + } + }; + + return destroyer; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_notifications.js": +/*!**************************************************!*\ + !*** ./src/components/modules/_notifications.js ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor Notification Module + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = function (notifications) { + var editor = codex.editor; + + var queue = []; + + var addToQueue = function addToQueue(settings) { + queue.push(settings); + + var index = 0; + + while (index < queue.length && queue.length > 5) { + if (queue[index].type == 'confirm' || queue[index].type == 'prompt') { + index++; + continue; + } + + queue[index].close(); + queue.splice(index, 1); + } + }; + + notifications.createHolder = function () { + var holder = editor.draw.node('DIV', 'cdx-notifications-block'); + + editor.nodes.notifications = document.body.appendChild(holder); + + return holder; + }; + + /** + * Error notificator. Shows block with message + * @protected + */ + notifications.errorThrown = function (errorMsg, event) { + editor.notifications.notification({ message: 'This action is not available currently', type: event.type }); + }; + + /** + * + * Appends notification + * + * settings = { + * type - notification type (reserved types: alert, confirm, prompt). Just add class 'cdx-notification-'+type + * message - notification message + * okMsg - confirm button text (default - 'Ok') + * cancelBtn - cancel button text (default - 'Cancel'). Only for confirm and prompt types + * confirm - function-handler for ok button click + * cancel - function-handler for cancel button click. Only for confirm and prompt types + * time - time (in seconds) after which notification will close (default - 10s) + * } + * + * @param settings + */ + notifications.notification = function (constructorSettings) { + /** Private vars and methods */ + var notification = null, + cancel = null, + type = null, + confirm = null, + inputField = null; + + var confirmHandler = function confirmHandler() { + close(); + + if (typeof confirm !== 'function') { + return; + } + + if (type == 'prompt') { + confirm(inputField.value); + return; + } + + confirm(); + }; + + var cancelHandler = function cancelHandler() { + close(); + + if (typeof cancel !== 'function') { + return; + } + + cancel(); + }; + + /** Public methods */ + function create(settings) { + if (!(settings && settings.message)) { + editor.core.log('Can\'t create notification. Message is missed'); + return; + } + + settings.type = settings.type || 'alert'; + settings.time = settings.time * 1000 || 10000; + + var wrapper = editor.draw.node('DIV', 'cdx-notification'), + message = editor.draw.node('DIV', 'cdx-notification__message'), + input = editor.draw.node('INPUT', 'cdx-notification__input'), + okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'), + cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn'); + + message.textContent = settings.message; + okBtn.textContent = settings.okMsg || 'ОК'; + cancelBtn.textContent = settings.cancelMsg || 'Отмена'; + + editor.listeners.add(okBtn, 'click', confirmHandler); + editor.listeners.add(cancelBtn, 'click', cancelHandler); + + wrapper.appendChild(message); + + if (settings.type == 'prompt') { + wrapper.appendChild(input); + } + + wrapper.appendChild(okBtn); + + if (settings.type == 'prompt' || settings.type == 'confirm') { + wrapper.appendChild(cancelBtn); + } + + wrapper.classList.add('cdx-notification-' + settings.type); + wrapper.dataset.type = settings.type; + + notification = wrapper; + type = settings.type; + confirm = settings.confirm; + cancel = settings.cancel; + inputField = input; + + if (settings.type != 'prompt' && settings.type != 'confirm') { + window.setTimeout(close, settings.time); + } + }; + + /** + * Show notification block + */ + function send() { + editor.nodes.notifications.appendChild(notification); + inputField.focus(); + + editor.nodes.notifications.classList.add('cdx-notification__notification-appending'); + + window.setTimeout(function () { + editor.nodes.notifications.classList.remove('cdx-notification__notification-appending'); + }, 100); + + addToQueue({ type: type, close: close }); + }; + + /** + * Remove notification block + */ + function close() { + notification.remove(); + }; + + if (constructorSettings) { + create(constructorSettings); + send(); + } + + return { + create: create, + send: send, + close: close + }; + }; + + notifications.clear = function () { + editor.nodes.notifications.innerHTML = ''; + queue = []; + }; + + return notifications; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_parser.js": +/*!*******************************************!*\ + !*** ./src/components/modules/_parser.js ***! + \*******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor Parser Module + * + * @author Codex Team + * @version 1.1 + */ + +module.exports = function (parser) { + var editor = codex.editor; + + /** 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; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_paste.js": +/*!******************************************!*\ + !*** ./src/components/modules/_paste.js ***! + \******************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor Paste module + * + * @author Codex Team + * @version 1.1.1 + */ + +module.exports = function (paste) { + var editor = codex.editor; + + var patterns = []; + + paste.prepare = function () { + var tools = editor.tools; + + for (var tool in tools) { + if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) { + continue; + } + + tools[tool].renderOnPastePatterns.map(function (pattern) { + patterns.push(pattern); + }); + } + + return Promise.resolve(); + }; + + /** + * Saves data + * @param event + */ + paste.pasted = function (event) { + var clipBoardData = event.clipboardData || window.clipboardData, + content = clipBoardData.getData('Text'); + + var result = analize(content); + + if (result) { + event.preventDefault(); + event.stopImmediatePropagation(); + } + + return result; + }; + + /** + * Analizes pated string and calls necessary method + */ + + var analize = function analize(string) { + var result = false, + content = editor.content.currentNode, + plugin = content.dataset.tool; + + patterns.map(function (pattern) { + var execArray = pattern.regex.exec(string), + match = execArray && execArray[0]; + + if (match && match === string.trim()) { + /** current block is not empty */ + if (content.textContent.trim() && plugin == editor.settings.initialBlockPlugin) { + pasteToNewBlock_(); + } + + pattern.callback(string, pattern); + result = true; + } + }); + + return result; + }; + + var pasteToNewBlock_ = function pasteToNewBlock_() { + /** Create new initial block */ + editor.content.insertBlock({ + + type: editor.settings.initialBlockPlugin, + block: editor.tools[editor.settings.initialBlockPlugin].render({ + text: '' + }) + + }, false); + }; + + /** + * This method prevents default behaviour. + * + * @param {Object} event + * @protected + * + * @description 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 + */ + paste.blockPasteCallback = function (event) { + if (!needsToHandlePasteEvent(event.target)) { + return; + } + + /** Prevent default behaviour */ + event.preventDefault(); + + /** get html pasted data - dirty data */ + var htmlData = event.clipboardData.getData('text/html'), + plainData = event.clipboardData.getData('text/plain'); + + /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/ + var paragraphs = editor.draw.node('DIV', '', {}), + cleanData, + wrappedData; + + /** Create fragment, that we paste to range after proccesing */ + cleanData = editor.sanitizer.clean(htmlData); + + /** + * We wrap pasted text withtags to split it logically + * @type {string} + */ + wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData); + paragraphs.innerHTML = wrappedData; + + /** + * If there only one paragraph, just insert in at the caret location + */ + if (paragraphs.childNodes.length == 1) { + emulateUserAgentBehaviour(paragraphs.firstChild); + return; + } + + insertPastedParagraphs(paragraphs.childNodes); + }; + + /** + * Checks if we should handle paste event on block + * @param block + * + * @return {boolean} + */ + var needsToHandlePasteEvent = function needsToHandlePasteEvent(block) { + /** If area is input or textarea then allow default behaviour */ + if (editor.core.isNativeInput(block)) { + return false; + } + + var editableParent = editor.content.getEditableParent(block); + + /** Allow paste when event target placed in Editable element */ + if (!editableParent) { + return false; + } + + return true; + }; + + /** + * Inserts new initial plugin blocks with data in paragraphs + * + * @param {Array} paragraphs - array of paragraphs (
) whit content, that should be inserted + */ + var insertPastedParagraphs = function insertPastedParagraphs(paragraphs) { + var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin, + currentNode = editor.content.currentNode; + + paragraphs.forEach(function (paragraph) { + /** Don't allow empty paragraphs */ + if (editor.core.isBlockEmpty(paragraph)) { + return; + } + + editor.content.insertBlock({ + type: NEW_BLOCK_TYPE, + block: editor.tools[NEW_BLOCK_TYPE].render({ + text: paragraph.innerHTML + }) + }); + + editor.caret.inputIndex++; + }); + + editor.caret.setToPreviousBlock(editor.caret.getCurrentInputIndex() + 1); + + /** + * If there was no data in working node, remove it + */ + if (editor.core.isBlockEmpty(currentNode)) { + currentNode.remove(); + editor.ui.saveInputs(); + } + }; + + /** + * Inserts node content at the caret position + * + * @param {Node} node - DOM node (could be DocumentFragment), that should be inserted at the caret location + */ + var emulateUserAgentBehaviour = function emulateUserAgentBehaviour(node) { + var newNode; + + if (node.childElementCount) { + newNode = document.createDocumentFragment(); + + node.childNodes.forEach(function (current) { + if (!editor.core.isDomNode(current) && current.data.trim() === '') { + return; + } + + newNode.appendChild(current.cloneNode(true)); + }); + } else { + newNode = document.createTextNode(node.textContent); + } + + editor.caret.insertNode(newNode); + }; + + return paste; +}({}); + +/***/ }), + +/***/ "./src/components/modules/_transport.js": +/*!**********************************************!*\ + !*** ./src/components/modules/_transport.js ***! + \**********************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * + * Codex.Editor Transport Module + * + * @copyright 2017 Codex-Team + * @version 1.2.0 + */ + +module.exports = function (transport) { + var editor = codex.editor; + + /** + * @private {Object} current XmlHttpRequest instance + */ + var currentRequest = null; + + /** + * @type {null} | {DOMElement} input - keeps input element in memory + */ + transport.input = null; + + /** + * @property {Object} arguments - keep plugin settings and defined callbacks + */ + transport.arguments = null; + + /** + * Prepares input element where will be files + */ + transport.prepare = function () { + var input = editor.draw.node('INPUT', '', { type: 'file' }); + + editor.listeners.add(input, 'change', editor.transport.fileSelected); + editor.transport.input = input; + }; + + /** Clear input when files is uploaded */ + transport.clearInput = function () { + /** Remove old input */ + transport.input = null; + + /** Prepare new one */ + transport.prepare(); + }; + + /** + * Callback for file selection + * @param {Event} event + */ + transport.fileSelected = function () { + var input = this, + i, + files = input.files, + formData = new FormData(); + + if (editor.transport.arguments.multiple === true) { + for (i = 0; i < files.length; i++) { + formData.append('files[]', files[i], files[i].name); + } + } else { + formData.append('files', files[0], files[0].name); + } + + currentRequest = editor.core.ajax({ + type: 'POST', + data: formData, + url: editor.transport.arguments.url, + beforeSend: editor.transport.arguments.beforeSend, + success: editor.transport.arguments.success, + error: editor.transport.arguments.error, + progress: editor.transport.arguments.progress + }); + + /** Clear input */ + transport.clearInput(); + }; + + /** + * Use plugin callbacks + * @protected + * + * @param {Object} args - can have : + * @param {String} args.url - fetch URL + * @param {Function} args.beforeSend - function calls before sending ajax + * @param {Function} args.success - success callback + * @param {Function} args.error - on error handler + * @param {Function} args.progress - xhr onprogress handler + * @param {Boolean} args.multiple - allow select several files + * @param {String} args.accept - adds accept attribute + */ + transport.selectAndUpload = function (args) { + transport.arguments = args; + + if (args.multiple === true) { + transport.input.setAttribute('multiple', 'multiple'); + } + + if (args.accept) { + transport.input.setAttribute('accept', args.accept); + } + + transport.input.click(); + }; + + transport.abort = function () { + currentRequest.abort(); + + currentRequest = null; + }; + + return transport; +}({}); /***/ }), @@ -6671,6 +9477,947 @@ module.exports = exports['default']; /***/ }), +/***/ "./src/components/modules/toolbar/inline.js": +/*!**************************************************!*\ + !*** ./src/components/modules/toolbar/inline.js ***! + \**************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Inline toolbar + * + * Contains from tools: + * Bold, Italic, Underline and Anchor + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = function (inline) { + var editor = codex.editor; + + 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 */ + editor.listeners.add(action, '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; +}({}); + +/***/ }), + +/***/ "./src/components/modules/toolbar/settings.js": +/*!****************************************************!*\ + !*** ./src/components/modules/toolbar/settings.js ***! + \****************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Toolbar settings + * + * @version 1.0.5 + */ + +module.exports = function (settings) { + var editor = codex.editor; + + settings.opened = false; + + settings.setting = null; + settings.actions = 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) { + return; + } + + /** + * Draw settings block + */ + var settingsBlock = editor.tools[toolType].makeSettings(); + + editor.nodes.pluginSettings.appendChild(settingsBlock); + + /** Open settings block */ + editor.nodes.blockSettings.classList.add('opened'); + 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(); + } + }; + + /** + * 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: 'Отмена' }); + + editor.listeners.add(settingButton, 'click', editor.toolbar.settings.removeButtonClicked, false); + + editor.listeners.add(confirmAction, 'click', editor.toolbar.settings.confirmRemovingRequest, false); + + editor.listeners.add(cancelAction, '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; +}({}); + +/***/ }), + +/***/ "./src/components/modules/toolbar/toolbar.js": +/*!***************************************************!*\ + !*** ./src/components/modules/toolbar/toolbar.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (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 + */ + +module.exports = function (toolbar) { + var editor = codex.editor; + + toolbar.settings = __webpack_require__(/*! ./settings */ "./src/components/modules/toolbar/settings.js"); + toolbar.inline = __webpack_require__(/*! ./inline */ "./src/components/modules/toolbar/inline.js"); + toolbar.toolbox = __webpack_require__(/*! ./toolbox */ "./src/components/modules/toolbar/toolbox.js"); + + /** + * Margin between focused node and toolbar + */ + toolbar.defaultToolbarHeight = 49; + + toolbar.defaultOffset = 34; + + toolbar.opened = false; + + toolbar.current = null; + + /** + * @protected + */ + toolbar.open = function () { + if (editor.hideToolbar) { + return; + } + + var toolType = editor.content.currentNode.dataset.tool; + + if (!editor.tools[toolType] || !editor.tools[toolType].makeSettings) { + editor.nodes.showSettingsButton.classList.add('hide'); + } else { + editor.nodes.showSettingsButton.classList.remove('hide'); + } + + 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; +}({}); + +/***/ }), + +/***/ "./src/components/modules/toolbar/toolbox.js": +/*!***************************************************!*\ + !*** ./src/components/modules/toolbar/toolbox.js ***! + \***************************************************/ +/*! no static exports found */ +/***/ (function(module, exports, __webpack_require__) { + +"use strict"; + + +/** + * Codex Editor toolbox + * + * All tools be able to appended here + * + * @author Codex Team + * @version 1.0 + */ + +module.exports = function (toolbox) { + var editor = codex.editor; + + toolbox.opened = false; + toolbox.openedOnBlock = null; + + /** Shows toolbox */ + toolbox.open = function () { + /** Close setting if toolbox is opened */ + if (editor.toolbar.settings.opened) { + editor.toolbar.settings.close(); + } + + /** Add 'toolbar-opened' class for current block **/ + toolbox.openedOnBlock = editor.content.currentNode; + toolbox.openedOnBlock.classList.add('toolbar-opened'); + + /** 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 () { + /** Remove 'toolbar-opened' class from current block **/ + if (toolbox.openedOnBlock) toolbox.openedOnBlock.classList.remove('toolbar-opened'); + toolbox.openedOnBlock = null; + + /** Makes toolbox disappear */ + editor.nodes.toolbox.classList.remove('opened'); + + /** Rotate plus button */ + editor.nodes.plusButton.classList.remove('clicked'); + + /** toolbox state */ + editor.toolbar.toolbox.opened = false; + + editor.toolbar.current = null; + }; + + 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) % tools.length; + visibleTool = tools[nextToolIndex]; + + while (!editor.tools[visibleTool].displayInToolbox) { + nextToolIndex = (nextToolIndex + 1) % tools.length; + 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; +}({}); + +/***/ }), + /***/ "./src/components/modules/tools.js": /*!*****************************************!*\ !*** ./src/components/modules/tools.js ***! @@ -7285,7 +11032,7 @@ var UI = function (_Module) { /** * - /** Update current input index in memory when caret focused into existed input */ + /** Update current input index in memory when caret focused into existed input */ // if (event.target.contentEditable == 'true') { // // editor.caret.saveCurrentInputIndex(); @@ -8218,7 +11965,7 @@ exports = module.exports = __webpack_require__(/*! ../../node_modules/css-loader // module -exports.push([module.i, ":root {\n /**\n * Toolbar buttons\n */\n --bg-light: #eff2f5;\n\n /**\n * All gray texts: placeholders, settings\n */\n --grayText: #707684;\n\n /** Blue icons */\n --color-active-icon: #388AE5;\n\n /**\n * Block content width\n */\n --content-width: 650px;\n\n /**\n * Toolbar Plus Button and Toolbox buttons height and width\n */\n --toolbar-buttons-size: 34px;\n\n /**\n * Confirm deletion bg\n */\n --color-confirm: #E24A4A;\n}\n/**\n* Editor wrapper\n*/\n.codex-editor {\n position: relative;\n box-sizing: border-box;\n\n\n}\n.codex-editor .hide {\n display: none;\n }\n.codex-editor__redactor {\n padding-bottom: 300px;\n }\n.codex-editor svg {\n fill: currentColor;\n vertical-align: middle;\n max-height: 100%;\n }\n::-moz-selection{\n background-color: rgba(61,166,239,0.63);\n}\n::selection{\n background-color: rgba(61,166,239,0.63);\n}\n.ce-tune-moveup{}\n.ce-settings-delete:hover {\n cursor: pointer;\n }\n.ce-settings-delete::before {\n content: 'delete'\n }\n.ce-toolbar {\n position: absolute;\n left: 0;\n right: 0;\n top: 0;\n /*opacity: 0;*/\n /*visibility: hidden;*/\n transition: opacity 100ms ease;\n will-change: opacity, transform;\n display: none;\n}\n.ce-toolbar--opened {\n display: block;\n /*opacity: 1;*/\n /*visibility: visible;*/\n }\n.ce-toolbar__content {\n max-width: 650px;\n max-width: var(--content-width);\n margin: 0 auto;\n position: relative;\n }\n.ce-toolbar__plus {\n position: absolute;\n left: calc(calc(34px + 10px) * -1);\n left: calc(calc(var(--toolbar-buttons-size) + 10px) * -1);\n display: inline-block;\n background-color: #eff2f5;\n background-color: var(--bg-light);\n width: 34px;\n width: var(--toolbar-buttons-size);\n height: 34px;\n height: var(--toolbar-buttons-size);\n line-height: 34px;\n text-align: center;\n border-radius: 50%;\n cursor: pointer;\n }\n.ce-toolbar__plus--hidden {\n display: none;\n }\n/**\n * Block actions Zone\n * -------------------------\n */\n.ce-toolbar__actions {\n position: absolute;\n right: 0;\n top: 0;\n padding-right: 16px;\n }\n.ce-toolbar__actions-buttons {\n text-align: right;\n }\n.ce-toolbar__settings-btn {\n display: inline-block;\n width: 24px;\n height: 24px;\n color: #707684;\n color: var(--grayText);\n cursor: pointer;\n }\n.ce-toolbox {\n position: absolute;\n visibility: hidden;\n transition: opacity 100ms ease;\n will-change: opacity;\n}\n.ce-toolbox--opened {\n opacity: 1;\n visibility: visible;\n }\n.ce-toolbox__button {\n display: inline-block;\n list-style: none;\n margin: 0;\n background: #eff2f5;\n background: var(--bg-light);\n width: 34px;\n width: var(--toolbar-buttons-size);\n height: 34px;\n height: var(--toolbar-buttons-size);\n border-radius: 30px;\n overflow: hidden;\n text-align: center;\n line-height: 34px;\n line-height: var(--toolbar-buttons-size)\n }\n.ce-toolbox__button::before {\n content: attr(title);\n font-size: 22px;\n font-weight: 500;\n letter-spacing: 1em;\n -webkit-font-feature-settings: \"smcp\", \"c2sc\";\n font-feature-settings: \"smcp\", \"c2sc\";\n font-variant-caps: all-small-caps;\n padding-left: 11.5px;\n margin-top: -1px;\n display: inline-block;\n }\n.ce-inline-toolbar {\n position: absolute;\n background-color: #FFFFFF;\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\n border-radius: 4px;\n z-index: 2\n}\n.ce-inline-toolbar::before {\n content: '';\n width: 15px;\n height: 15px;\n position: absolute;\n top: -7px;\n left: 50%;\n margin-left: -7px;\n transform: rotate(-45deg);\n background-color: #fff;\n z-index: -1;\n }\n.ce-inline-toolbar {\n padding: 6px;\n transform: translateX(-50%);\n display: none;\n box-shadow: 0 6px 12px -6px rgba(131, 147, 173, 0.46),\n 5px -12px 34px -13px rgba(97, 105, 134, 0.6),\n 0 26px 52px 3px rgba(147, 165, 186, 0.24);\n}\n.ce-inline-toolbar--showed {\n display: block;\n }\n.ce-inline-tool {\n display: inline-block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n border-radius: 3px;\n cursor: pointer;\n border: 0;\n outline: none;\n background-color: transparent;\n vertical-align: bottom;\n color: #707684;\n color: var(--grayText)\n}\n.ce-inline-tool:not(:last-of-type){\n margin-right: 5px;\n }\n.ce-inline-tool:hover {\n background-color: #eff2f5;\n background-color: var(--bg-light);\n }\n.ce-inline-tool {\n line-height: normal;\n}\n.ce-inline-tool--active {\n color: #388AE5;\n color: var(--color-active-icon);\n }\n.ce-inline-tool--link .icon {\n margin-top: -2px;\n }\n.ce-inline-tool--link .icon--unlink {\n display: none;\n }\n.ce-inline-tool--unlink .icon--link {\n display: none;\n }\n.ce-inline-tool--unlink .icon--unlink {\n display: inline-block;\n }\n.ce-inline-tool-input {\n background-color: #eff2f5;\n background-color: var(--bg-light);\n outline: none;\n border: 0;\n border-radius: 3px;\n margin: 6px 0 0;\n font-size: 13px;\n padding: 8px;\n width: 100%;\n box-sizing: border-box;\n display: none\n }\n.ce-inline-tool-input::-webkit-input-placeholder {\n color: #707684;\n color: var(--grayText);\n }\n.ce-inline-tool-input:-ms-input-placeholder {\n color: #707684;\n color: var(--grayText);\n }\n.ce-inline-tool-input::placeholder {\n color: #707684;\n color: var(--grayText);\n }\n.ce-inline-tool-input--showed {\n display: block;\n }\n.ce-settings {\n position: absolute;\n background-color: #FFFFFF;\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\n border-radius: 4px;\n z-index: 2\n}\n.ce-settings::before {\n content: '';\n width: 15px;\n height: 15px;\n position: absolute;\n top: -7px;\n left: 50%;\n margin-left: -7px;\n transform: rotate(-45deg);\n background-color: #fff;\n z-index: -1;\n }\n.ce-settings {\n right: 5px;\n top: 35px;\n min-width: 124px\n}\n.ce-settings::before{\n left: auto;\n right: 12px;\n }\n.ce-settings {\n\n display: none;\n}\n.ce-settings--opened {\n display: block;\n }\n.ce-settings__plugin-zone:not(:empty){\n padding: 6px;\n }\n.ce-settings__default-zone:not(:empty){\n padding: 6px;\n }\n.ce-settings__button {\n display: inline-block;\n width: 34px;\n height: 34px;\n line-height: 34px;\n text-align: center;\n border-radius: 3px;\n cursor: pointer;\n border: 0;\n outline: none;\n background-color: transparent;\n vertical-align: bottom;\n color: #707684;\n color: var(--grayText)\n }\n.ce-settings__button:not(:last-of-type){\n margin-right: 5px;\n }\n.ce-settings__button:hover {\n background-color: #eff2f5;\n background-color: var(--bg-light);\n }\n.ce-settings__button--active {\n color: #388AE5;\n color: var(--color-active-icon);\n }\n.ce-settings__button--delete {\n transition: background-color 300ms ease;\n will-change: background-color;\n }\n.ce-settings__button--delete .icon {\n transition: transform 200ms ease-out;\n will-change: transform;\n }\n.ce-settings__button--confirm {\n background-color: #E24A4A;\n background-color: var(--color-confirm);\n color: #fff\n }\n.ce-settings__button--confirm:hover {\n background-color: rgb(213, 74, 74) !important;\n background-color: rgb(213, 74, 74) !important;\n }\n.ce-settings__button--confirm .icon {\n transform: rotate(90deg);\n }\n.ce-settings-move-up:hover {\n cursor: pointer;\n }\n.ce-settings-move-up--disabled {\n cursor: not-allowed !important;\n opacity: .3;\n }\n.ce-block:first-of-type {\n margin-top: 0;\n }\n.ce-block--selected {\n background-image: linear-gradient(17deg, rgba(243, 248, 255, 0.03) 63.45%, rgba(207, 214, 229, 0.27) 98%);\n border-radius: 3px;\n }\n.ce-block__content {\n max-width: 650px;\n max-width: var(--content-width);\n margin: 0 auto;\n }\n", ""]); +exports.push([module.i, ":root {\r\n /**\r\n * Toolbar buttons\r\n */\r\n --bg-light: #eff2f5;\r\n\r\n /**\r\n * All gray texts: placeholders, settings\r\n */\r\n --grayText: #707684;\r\n\r\n /** Blue icons */\r\n --color-active-icon: #388AE5;\r\n\r\n /**\r\n * Block content width\r\n */\r\n --content-width: 650px;\r\n\r\n /**\r\n * Toolbar Plus Button and Toolbox buttons height and width\r\n */\r\n --toolbar-buttons-size: 34px;\r\n\r\n /**\r\n * Confirm deletion bg\r\n */\r\n --color-confirm: #E24A4A;\r\n}\r\n/**\r\n* Editor wrapper\r\n*/\r\n.codex-editor {\r\n position: relative;\r\n box-sizing: border-box;\r\n\r\n\r\n}\r\n.codex-editor .hide {\r\n display: none;\r\n }\r\n.codex-editor__redactor {\r\n padding-bottom: 300px;\r\n }\r\n.codex-editor svg {\r\n fill: currentColor;\r\n vertical-align: middle;\r\n max-height: 100%;\r\n }\r\n::-moz-selection{\r\n background-color: rgba(61,166,239,0.63);\r\n}\r\n::selection{\r\n background-color: rgba(61,166,239,0.63);\r\n}\r\n.ce-tune-moveup{}\r\n.ce-settings-delete:hover {\r\n cursor: pointer;\r\n }\r\n.ce-settings-delete::before {\r\n content: 'delete'\r\n }\r\n.ce-toolbar {\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n top: 0;\r\n /*opacity: 0;*/\r\n /*visibility: hidden;*/\r\n transition: opacity 100ms ease;\r\n will-change: opacity, transform;\r\n display: none;\r\n}\r\n.ce-toolbar--opened {\r\n display: block;\r\n /*opacity: 1;*/\r\n /*visibility: visible;*/\r\n }\r\n.ce-toolbar__content {\r\n max-width: 650px;\r\n max-width: var(--content-width);\r\n margin: 0 auto;\r\n position: relative;\r\n }\r\n.ce-toolbar__plus {\r\n position: absolute;\r\n left: calc(calc(34px + 10px) * -1);\r\n left: calc(calc(var(--toolbar-buttons-size) + 10px) * -1);\r\n display: inline-block;\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n width: 34px;\r\n width: var(--toolbar-buttons-size);\r\n height: 34px;\r\n height: var(--toolbar-buttons-size);\r\n line-height: 34px;\r\n text-align: center;\r\n border-radius: 50%;\r\n cursor: pointer;\r\n }\r\n.ce-toolbar__plus--hidden {\r\n display: none;\r\n }\r\n/**\r\n * Block actions Zone\r\n * -------------------------\r\n */\r\n.ce-toolbar__actions {\r\n position: absolute;\r\n right: 0;\r\n top: 0;\r\n padding-right: 16px;\r\n }\r\n.ce-toolbar__actions-buttons {\r\n text-align: right;\r\n }\r\n.ce-toolbar__settings-btn {\r\n display: inline-block;\r\n width: 24px;\r\n height: 24px;\r\n color: #707684;\r\n color: var(--grayText);\r\n cursor: pointer;\r\n }\r\n.ce-toolbox {\r\n position: absolute;\r\n visibility: hidden;\r\n transition: opacity 100ms ease;\r\n will-change: opacity;\r\n}\r\n.ce-toolbox--opened {\r\n opacity: 1;\r\n visibility: visible;\r\n }\r\n.ce-toolbox__button {\r\n display: inline-block;\r\n list-style: none;\r\n margin: 0;\r\n background: #eff2f5;\r\n background: var(--bg-light);\r\n width: 34px;\r\n width: var(--toolbar-buttons-size);\r\n height: 34px;\r\n height: var(--toolbar-buttons-size);\r\n border-radius: 30px;\r\n overflow: hidden;\r\n text-align: center;\r\n line-height: 34px;\r\n line-height: var(--toolbar-buttons-size)\r\n }\r\n.ce-toolbox__button::before {\r\n content: attr(title);\r\n font-size: 22px;\r\n font-weight: 500;\r\n letter-spacing: 1em;\r\n -webkit-font-feature-settings: \"smcp\", \"c2sc\";\r\n font-feature-settings: \"smcp\", \"c2sc\";\r\n font-variant-caps: all-small-caps;\r\n padding-left: 11.5px;\r\n margin-top: -1px;\r\n display: inline-block;\r\n }\r\n.ce-inline-toolbar {\r\n position: absolute;\r\n background-color: #FFFFFF;\r\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\r\n border-radius: 4px;\r\n z-index: 2\r\n}\r\n.ce-inline-toolbar::before {\r\n content: '';\r\n width: 15px;\r\n height: 15px;\r\n position: absolute;\r\n top: -7px;\r\n left: 50%;\r\n margin-left: -7px;\r\n transform: rotate(-45deg);\r\n background-color: #fff;\r\n z-index: -1;\r\n }\r\n.ce-inline-toolbar {\r\n padding: 6px;\r\n transform: translateX(-50%);\r\n display: none;\r\n box-shadow: 0 6px 12px -6px rgba(131, 147, 173, 0.46),\r\n 5px -12px 34px -13px rgba(97, 105, 134, 0.6),\r\n 0 26px 52px 3px rgba(147, 165, 186, 0.24);\r\n}\r\n.ce-inline-toolbar--showed {\r\n display: block;\r\n }\r\n.ce-inline-tool {\r\n display: inline-block;\r\n width: 34px;\r\n height: 34px;\r\n line-height: 34px;\r\n text-align: center;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n border: 0;\r\n outline: none;\r\n background-color: transparent;\r\n vertical-align: bottom;\r\n color: #707684;\r\n color: var(--grayText)\r\n}\r\n.ce-inline-tool:not(:last-of-type){\r\n margin-right: 5px;\r\n }\r\n.ce-inline-tool:hover {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n }\r\n.ce-inline-tool {\r\n line-height: normal;\r\n}\r\n.ce-inline-tool--active {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-inline-tool--link .icon {\r\n margin-top: -2px;\r\n }\r\n.ce-inline-tool--link .icon--unlink {\r\n display: none;\r\n }\r\n.ce-inline-tool--unlink .icon--link {\r\n display: none;\r\n }\r\n.ce-inline-tool--unlink .icon--unlink {\r\n display: inline-block;\r\n }\r\n.ce-inline-tool-input {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n outline: none;\r\n border: 0;\r\n border-radius: 3px;\r\n margin: 6px 0 0;\r\n font-size: 13px;\r\n padding: 8px;\r\n width: 100%;\r\n box-sizing: border-box;\r\n display: none\r\n }\r\n.ce-inline-tool-input::-webkit-input-placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input:-ms-input-placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input::placeholder {\r\n color: #707684;\r\n color: var(--grayText);\r\n }\r\n.ce-inline-tool-input--showed {\r\n display: block;\r\n }\r\n.ce-settings {\r\n position: absolute;\r\n background-color: #FFFFFF;\r\n box-shadow: 0 8px 23px -6px rgba(21,40,54,0.31), 22px -14px 34px -18px rgba(33,48,73,0.26);\r\n border-radius: 4px;\r\n z-index: 2\r\n}\r\n.ce-settings::before {\r\n content: '';\r\n width: 15px;\r\n height: 15px;\r\n position: absolute;\r\n top: -7px;\r\n left: 50%;\r\n margin-left: -7px;\r\n transform: rotate(-45deg);\r\n background-color: #fff;\r\n z-index: -1;\r\n }\r\n.ce-settings {\r\n right: 5px;\r\n top: 35px;\r\n min-width: 124px\r\n}\r\n.ce-settings::before{\r\n left: auto;\r\n right: 12px;\r\n }\r\n.ce-settings {\r\n\r\n display: none;\r\n}\r\n.ce-settings--opened {\r\n display: block;\r\n }\r\n.ce-settings__plugin-zone:not(:empty){\r\n padding: 6px;\r\n }\r\n.ce-settings__default-zone:not(:empty){\r\n padding: 6px;\r\n }\r\n.ce-settings__button {\r\n display: inline-block;\r\n width: 34px;\r\n height: 34px;\r\n line-height: 34px;\r\n text-align: center;\r\n border-radius: 3px;\r\n cursor: pointer;\r\n border: 0;\r\n outline: none;\r\n background-color: transparent;\r\n vertical-align: bottom;\r\n color: #707684;\r\n color: var(--grayText)\r\n }\r\n.ce-settings__button:not(:last-of-type){\r\n margin-right: 5px;\r\n }\r\n.ce-settings__button:hover {\r\n background-color: #eff2f5;\r\n background-color: var(--bg-light);\r\n }\r\n.ce-settings__button--active {\r\n color: #388AE5;\r\n color: var(--color-active-icon);\r\n }\r\n.ce-settings__button--delete {\r\n transition: background-color 300ms ease;\r\n will-change: background-color;\r\n }\r\n.ce-settings__button--delete .icon {\r\n transition: transform 200ms ease-out;\r\n will-change: transform;\r\n }\r\n.ce-settings__button--confirm {\r\n background-color: #E24A4A;\r\n background-color: var(--color-confirm);\r\n color: #fff\r\n }\r\n.ce-settings__button--confirm:hover {\r\n background-color: rgb(213, 74, 74) !important;\r\n background-color: rgb(213, 74, 74) !important;\r\n }\r\n.ce-settings__button--confirm .icon {\r\n transform: rotate(90deg);\r\n }\r\n.ce-settings-move-up:hover {\r\n cursor: pointer;\r\n }\r\n.ce-settings-move-up--disabled {\r\n cursor: not-allowed !important;\r\n opacity: .3;\r\n }\r\n.ce-block:first-of-type {\r\n margin-top: 0;\r\n }\r\n.ce-block--selected {\r\n background-image: linear-gradient(17deg, rgba(243, 248, 255, 0.03) 63.45%, rgba(207, 214, 229, 0.27) 98%);\r\n border-radius: 3px;\r\n }\r\n.ce-block__content {\r\n max-width: 650px;\r\n max-width: var(--content-width);\r\n margin: 0 auto;\r\n }\r\n", ""]); // exports diff --git a/build/codex-editor.js.map b/build/codex-editor.js.map index aa78c8c9..aef6f48a 100644 --- a/build/codex-editor.js.map +++ b/build/codex-editor.js.map @@ -1 +1 @@ -{"version":3,"sources":["webpack://CodexEditor/webpack/universalModuleDefinition","webpack://CodexEditor/webpack/bootstrap","webpack://CodexEditor/./build/sprite.svg","webpack://CodexEditor/./node_modules/css-loader/lib/css-base.js","webpack://CodexEditor/./node_modules/html-janitor/src/html-janitor.js","webpack://CodexEditor/./src/codex.js","webpack://CodexEditor/./src/components/__module.ts","webpack://CodexEditor/./src/components/block-tunes/block-tune-delete.ts","webpack://CodexEditor/./src/components/block-tunes/block-tune-move-up.ts","webpack://CodexEditor/./src/components/block.js","webpack://CodexEditor/./src/components/dom.js","webpack://CodexEditor/./src/components/inline-tools/inline-tool-bold.ts","webpack://CodexEditor/./src/components/inline-tools/inline-tool-italic.ts","webpack://CodexEditor/./src/components/inline-tools/inline-tool-link.ts","webpack://CodexEditor/./src/components/modules sync nonrecursive [^_](api-blocks.ts|api-events.ts|api-listener.ts|api-sanitizer.ts|api-saver.ts|api-selection.ts|api-toolbar.ts|api.ts|block-events.ts|blockManager.js|caret.js|events.js|listeners.js|renderer.js|sanitizer.js|saver.js|toolbar-blockSettings.js|toolbar-inline.ts|toolbar-toolbox.js|toolbar.js|tools.js|ui.js)$","webpack://CodexEditor/./src/components/modules/api-blocks.ts","webpack://CodexEditor/./src/components/modules/api-events.ts","webpack://CodexEditor/./src/components/modules/api-listener.ts","webpack://CodexEditor/./src/components/modules/api-sanitizer.ts","webpack://CodexEditor/./src/components/modules/api-saver.ts","webpack://CodexEditor/./src/components/modules/api-selection.ts","webpack://CodexEditor/./src/components/modules/api-toolbar.ts","webpack://CodexEditor/./src/components/modules/api.ts","webpack://CodexEditor/./src/components/modules/block-events.ts","webpack://CodexEditor/./src/components/modules/blockManager.js","webpack://CodexEditor/./src/components/modules/caret.js","webpack://CodexEditor/./src/components/modules/events.js","webpack://CodexEditor/./src/components/modules/listeners.js","webpack://CodexEditor/./src/components/modules/renderer.js","webpack://CodexEditor/./src/components/modules/sanitizer.js","webpack://CodexEditor/./src/components/modules/saver.js","webpack://CodexEditor/./src/components/modules/toolbar-blockSettings.js","webpack://CodexEditor/./src/components/modules/toolbar-inline.ts","webpack://CodexEditor/./src/components/modules/toolbar-toolbox.js","webpack://CodexEditor/./src/components/modules/toolbar.js","webpack://CodexEditor/./src/components/modules/tools.js","webpack://CodexEditor/./src/components/modules/ui.js","webpack://CodexEditor/./src/components/polyfills.js","webpack://CodexEditor/./src/components/selection.js","webpack://CodexEditor/./src/components/utils.js","webpack://CodexEditor/./src/styles/main.css"],"names":["modules","editorModules","map","module","CodexEditor","config","moduleInstances","Promise","resolve","then","configuration","init","start","methods","API","method","console","log","catch","error","constructModules","configureModules","forEach","Module","displayName","e","name","state","getModulesDiff","diff","moduleName","prepareDecorator","prepare","Tools","UI","BlockManager","Renderer","render","data","items","initialBlock","type","holderId","placeholder","sanitizer","p","b","a","hideToolbar","tools","toolsConfig","_","isEmpty","length","Editor","new","target","TypeError","DeleteTune","api","CSS","wrapper","button","buttonDelete","buttonConfirm","nodes","resetConfirmation","setConfirmation","$","make","appendChild","svg","listener","on","event","handleClick","needConfirmation","events","off","blocks","delete","classList","add","MoveUpTune","btnDisabled","moveUpButton","getCurrentBlockIndex","currentBlockIndex","currentBlockElement","getBlockByIndex","html","previousBlockElement","currentBlockCoords","getBoundingClientRect","previousBlockCoords","scrollUpOffset","top","Math","abs","window","innerHeight","scrollBy","swap","Block","toolName","toolInstance","settings","apiMethods","tool","_html","compose","tunes","makeTunes","contentNode","content","pluginsContent","methodName","params","Function","call","merge","extractedBlock","save","measuringStart","performance","now","measuringEnd","finishedExtraction","time","isValid","validate","tunesList","tune","tunesElement","document","createDocumentFragment","append","contentless","emptyText","emptyMedia","hasMedia","mediaTags","querySelector","join","selected","remove","Dom","tag","tagName","includes","classNames","attributes","el","createElement","Array","isArray","attrName","createTextNode","width","height","icon","createElementNS","setAttribute","innerHTML","parent","elements","el1","el2","temp","parentNode","insertBefore","removeChild","selector","querySelectorAll","node","atLast","child","sibling","nodeType","Node","ELEMENT_NODE","nodeChild","isSingleTag","getDeepestNode","nativeInputs","nodeText","isElement","isNativeInput","value","textContent","replace","trim","childNodes","treeWalker","leafs","isNodeEmpty","push","firstChild","shift","isLeaf","nextSibling","every","leaf","BoldInlineTool","commandName","buttonActive","buttonModifier","range","execCommand","selection","isActive","queryCommandState","toggle","ItalicInlineTool","LinkInlineTool","commandLink","commandUnlink","ENTER_KEY","buttonUnlink","input","inputShowed","inputOpened","inlineToolbar","toolbar","Selection","addEventListener","keyCode","enterPressed","parentAnchor","findParentTag","expandToTag","unlink","closeActions","checkState","close","toggleActions","anchorTag","openActions","hrefAttr","getAttribute","needFocus","focus","clearSavedSelection","clearSaved","restore","preventDefault","validateURL","prepareLink","insertLink","stopPropagation","stopImmediatePropagation","str","test","link","addProtocol","isInternal","isAnchor","substring","isProtocolRelative","BlocksAPI","index","fromIndex","toIndex","Toolbar","move","blockIndex","removeBlock","insert","Caret","setToBlock","currentBlock","navigatePrevious","clear","EventsAPI","eventName","callback","Events","emit","ListenerAPI","element","eventType","handler","useCapture","Listeners","SanitizerAPI","taintString","Sanitizer","clean","SaverAPI","Saver","SelectionAPI","className","ToolbarAPI","open","caret","saver","BlockEvents","keyCodes","BACKSPACE","backspace","ENTER","enter","DOWN","RIGHT","arrowRightAndDownPressed","UP","LEFT","arrowLeftAndUpPressed","InlineToolbar","handleShowingEvent","apiSettings","IS_ENABLED_LINE_BREAKS","shiftKey","split","newCurrent","isInitial","plusButton","show","BM","isFirstBlock","canMergeBlocks","isAtStart","targetBlock","blockToMerge","mergeable","setCaretToTheEnd","mergeBlocks","setTimeout","navigateNext","_blocks","Blocks","redactor","Proxy","set","get","construct","block","bindEvents","keydown","mouseUp","keyup","composeBlock","blockToMergeIndex","indexOf","blockToMergeInfo","mergeWith","extractedFragment","extractFragmentFromCaretPosition","text","blockInserted","currentNode","firstLevelBlock","closest","childNode","parentFirstLevelBlock","Error","needAddInitialBlock","removeAll","isLastBlock","array","workingArea","first","second","secondBlock","deleteCount","splice","previousBlock","insertAdjacentElement","nextBlock","isNaN","newBlock","children","instance","Number","offset","atEnd","nodeToSet","delay","createRange","setStart","setEnd","removeAllRanges","addRange","lastBlock","rangeCount","selectRange","getRangeAt","blockElem","deleteContents","cloneRange","selectNodeContents","endContainer","endOffset","extractContents","from","direction","current","siblings","contentEditable","force","isAtEnd","isCollapsed","anchorNode","firstNode","firstLetterPosition","search","leftSiblings","getHigherLevelSiblings","nothingAtLeft","anchorOffset","lastNode","nothingAtRight","rightTrimmedText","subscribers","reduce","previousData","currentHandler","newData","i","allListeners","assignedEventData","alreadyExist","findOne","existingListeners","findAll","removeEventListener","listenersOnElement","listenersWithType","listenersWithHandler","foundListeners","found","foundByElements","findByElement","filter","chainData","function","insertBlock","sequence","item","available","defaultConfig","_sanitizerInstance","sanitizerConfig","sanitizerInstance","require","customConfig","library","tags","href","rel","newInstance","output","blocksData","all","allExtractedData","makeOutput","outputData","totalTime","groupCollapsed","extraction","groupEnd","Date","version","VERSION","BlockSettings","toolSettings","defaultSettings","makeSettings","renderTunes","wrapperOpened","addToolSettings","addDefaultSettings","opened","closed","contains","inlineToolbarShowed","buttonsWrapper","actionsWrapper","buttons","actions","toolbarVerticalMargin","addTools","allowedToShow","checkToolsState","selectionRect","rect","wrapperOffset","newCoords","x","left","y","floor","style","tagsConflictsWithSelection","currentSelection","selectedText","getBlock","toolConfig","IS_ENABLED_INLINE_TOOLBAR","addTool","renderActions","toolClicked","surround","toolsInstances","inline","Tool","Toolbox","toolbox","toolsAvailable","IS_DISPLAYED_IN_TOOLBOX","TOOLBAR_ICON_CLASS","toolboxButton","title","dataset","buttonClicked","toolButton","toolClasses","IS_IRREPLACEBLE_TOOL","toolboxOpened","blockActionsButtons","settingsToggler","plusButtonClicked","settingsIcon","forceClose","defaultToolbarHeight","defaultOffset","newYCoordinate","offsetTop","transform","toolbarOpened","settingsTogglerClicked","hide","plusButtonHidden","toolsUnavailable","Object","values","IS_INLINE","inlineToolRequiredMethods","notImplementedMethods","hasOwnProperty","reject","sequenceData","getListOfPrepareFunctions","success","fallback","toolPreparationList","toolClass","plugin","holder","appendSVGSprite","loadStyles","getElementById","editorWrapper","editorZone","styles","toString","head","redactorClicked","documentClicked","clickedOnInlineToolbarButton","clickedNode","setCurrentBlockByChildNode","setToTheLastBlock","isInitialBlock","isEmptyBlock","spriteHolder","sprite","Element","prototype","matches","msMatchesSelector","webkitMatchesSelector","s","documentElement","parentElement","savedSelectionRange","sel","getSelection","searchDepth","parentTag","focusNode","boundNodes","searchDepthIterable","boundingLeft","boundingTop","boundingWidth","boundingHeight","span","insertNode","spanParent","normalize","Util","msg","args","chains","previousValue","currentValue","iteration","waitNextBlock","successCallback","fallbackCallback","collection","slice","object","keys","constructor","timeout","context","arguments","apply","TAB","SHIFT","CTRL","ALT","ESC","SPACE","DELETE","META"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,CAAC;AACD,O;ACVA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;;;AAGA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA,kDAA0C,gCAAgC;AAC1E;AACA;;AAEA;AACA;AACA;AACA,gEAAwD,kBAAkB;AAC1E;AACA,yDAAiD,cAAc;AAC/D;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,iDAAyC,iCAAiC;AAC1E,wHAAgH,mBAAmB,EAAE;AACrI;AACA;;AAEA;AACA;AACA;AACA,mCAA2B,0BAA0B,EAAE;AACvD,yCAAiC,eAAe;AAChD;AACA;AACA;;AAEA;AACA,8DAAsD,+DAA+D;;AAErH;AACA;;;AAGA;AACA;;;;;;;;;;;;AClFA,4+H;;;;;;;;;;;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA,mCAAmC,gBAAgB;AACnD,IAAI;AACJ;AACA;AACA,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACA;AACA,gBAAgB,iBAAiB;AACjC;AACA;AACA;AACA;AACA,YAAY,oBAAoB;AAChC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,KAAK;AACL;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA,GAAG;;AAEH;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA,oDAAoD,cAAc;;AAElE;AACA;;;;;;;;;;;;AC3EA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA,GAAG,QAIH;AACA,CAAC;;AAED;AACA,aAAa,OAAO;AACpB,aAAa,QAAQ;AACrB;AACA;;AAEA;AACA;;AAEA;AACA,wBAAwB,iCAAiC,EAAE;AAC3D,6BAA6B,uEAAuE,EAAE;;AAEtG;AACA;AACA;;AA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AKjC,MAAL,CAAYiC,IAAZ,CAAiBC,KAAjB,GAAyB,CAAEC,YAAF,CAAzB;AACD,OAHD,MAGO;AACL,YAAI,CAAC,KAAKnC,MAAL,CAAYiC,IAAZ,CAAiBC,KAAlB,IAA2B,KAAKlC,MAAL,CAAYiC,IAAZ,CAAiBC,KAAjB,CAAuBc,MAAvB,KAAkC,CAAjE,EAAoE;AAClE,eAAKhD,MAAL,CAAYiC,IAAZ,CAAiBC,KAAjB,GAAyB,CAAEC,YAAF,CAAzB;AACD;AACF;;AAED;;;AAGA,UAAI,CAACnC,OAAOmC,YAAZ,EAA0B;AACxB,aAAK,KAAKnC,MAAL,CAAYmC,YAAjB,IAAiC,KAAKnC,MAAL,CAAY4C,KAA7C;AAAoD;AAApD;AACD,OAFD,MAEO;AACL,aAAK5C,MAAL,CAAYmC,YAAZ,GAA2BnC,OAAOmC,YAAlC;AACD;AACF;;AAED;;;;;wBAIoB;AAClB,aAAO,KAAKnC,MAAZ;AACD;;;;;;;kBAjHkBD,W;AA4MpB;;AAED;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;;;;;;;;ACjZA;;;;;;;;;IASqBmB,M;AACjB;;;;;AAKA,wBAAwB;AAAA,QAAVlB,MAAU,QAAVA,MAAU;;AAAA;;AACpB;;;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A,KAFL;AAGLua,iBAAU,OAAAC;AAHL,OAAP;AAKD;;;;EA5DgCxb,M;;AA+DnC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;kBA5NqB0Q,K;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACtBrB;;;;;;;;;;;IAWqB+K,a;;;AACnB;;;AAGA,+BAAsB;AAAA,QAAT3c,MAAS,QAATA,MAAS;;AAAA;;AAAA,8HACd,EAACA,cAAD,EADc;;AAGpB,UAAK4D,KAAL,GAAa;AACXJ,eAAS,IADE;AAEXoZ,oBAAc,IAFH;AAGXC,uBAAiB;AAHN,KAAb;AAHoB;AAQrB;;AAED;;;;;;;;;;AA2BA;;;;;;;2BAOO;AACL,WAAKjZ,KAAL,CAAWJ,OAAX,GAAqBO,EAAEC,IAAF,CAAO,KAAP,EAAc2Y,cAAcpZ,GAA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ACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;kBAxfqBW,E;;;;;;;;;;;;;;;;AC7BrB;;;;;AAKA,IAAI,CAACqhB,QAAQC,SAAR,CAAkBC,OAAvB,EACEF,QAAQC,SAAR,CAAkBC,OAAlB,GAA4BF,QAAQC,SAAR,CAAkBE,iBAAlB,IACtBH,QAAQC,SAAR,CAAkBG,qBADxB;;AAGF,IAAI,CAACJ,QAAQC,SAAR,CAAkBxN,OAAvB,EACEuN,QAAQC,SAAR,CAAkBxN,OAAlB,GAA4B,UAAU4N,CAAV,EAAa;AACvC,MAAIpa,KAAK,IAAT;;AAEA,MAAI,CAAClB,SAASub,eAAT,CAAyBnG,QAAzB,CAAkClU,EAAlC,CAAL,EAA4C,OAAO,IAAP;AAC5C,KAAG;AACD,QAAIA,GAAGia,OAAH,CAAWG,CAAX,CAAJ,EAAmB,OAAOpa,EAAP;AACnBA,SAAKA,GAAGsa,aAAH,IAAoBta,GAAGiB,UAA5B;AACD,GAHD,QAGSjB,OAAO,IAHhB;AAIA,SAAO,IAAP;AACD,CATD,C;;;;;;;;;;;;;;;;;;;;;;ACVF;;;;IAIqBwE,S;AAC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","file":"codex-editor.js","sourcesContent":["(function webpackUniversalModuleDefinition(root, factory) {\n\tif(typeof exports === 'object' && typeof module === 'object')\n\t\tmodule.exports = factory();\n\telse if(typeof define === 'function' && define.amd)\n\t\tdefine([], factory);\n\telse if(typeof exports === 'object')\n\t\texports[\"CodexEditor\"] = factory();\n\telse\n\t\troot[\"CodexEditor\"] = factory();\n})(window, function() {\nreturn "," \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 \t\t}\n \t\t// Create a new module (and put it into the cache)\n \t\tvar module = installedModules[moduleId] = {\n \t\t\ti: moduleId,\n \t\t\tl: false,\n \t\t\texports: {}\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.l = 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// define getter function for harmony exports\n \t__webpack_require__.d = function(exports, name, getter) {\n \t\tif(!__webpack_require__.o(exports, name)) {\n \t\t\tObject.defineProperty(exports, name, { enumerable: true, get: getter });\n \t\t}\n \t};\n\n \t// define __esModule on exports\n \t__webpack_require__.r = function(exports) {\n \t\tif(typeof Symbol !== 'undefined' && Symbol.toStringTag) {\n \t\t\tObject.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });\n \t\t}\n \t\tObject.defineProperty(exports, '__esModule', { value: true });\n \t};\n\n \t// create a fake namespace object\n \t// mode & 1: value is a module id, require it\n \t// mode & 2: merge all properties of value into the ns\n \t// mode & 4: return value when already ns object\n \t// mode & 8|1: behave like require\n \t__webpack_require__.t = function(value, mode) {\n \t\tif(mode & 1) value = __webpack_require__(value);\n \t\tif(mode & 8) return value;\n \t\tif((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;\n \t\tvar ns = Object.create(null);\n \t\t__webpack_require__.r(ns);\n \t\tObject.defineProperty(ns, 'default', { enumerable: true, value: value });\n \t\tif(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));\n \t\treturn ns;\n \t};\n\n \t// getDefaultExport function for compatibility with non-harmony modules\n \t__webpack_require__.n = function(module) {\n \t\tvar getter = module && module.__esModule ?\n \t\t\tfunction getDefault() { return module['default']; } :\n \t\t\tfunction getModuleExports() { return module; };\n \t\t__webpack_require__.d(getter, 'a', getter);\n \t\treturn getter;\n \t};\n\n \t// Object.prototype.hasOwnProperty.call\n \t__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };\n\n \t// __webpack_public_path__\n \t__webpack_require__.p = \"\";\n\n\n \t// Load entry module and return exports\n \treturn __webpack_require__(__webpack_require__.s = \"./src/codex.js\");\n","module.exports = \"\\n\"","/*\n\tMIT License http://www.opensource.org/licenses/mit-license.php\n\tAuthor Tobias Koppers @sokra\n*/\n// css base code, injected by the css-loader\nmodule.exports = function(useSourceMap) {\n\tvar list = [];\n\n\t// return the list of modules as css string\n\tlist.toString = function toString() {\n\t\treturn this.map(function (item) {\n\t\t\tvar content = cssWithMappingToString(item, useSourceMap);\n\t\t\tif(item[2]) {\n\t\t\t\treturn \"@media \" + item[2] + \"{\" + content + \"}\";\n\t\t\t} else {\n\t\t\t\treturn content;\n\t\t\t}\n\t\t}).join(\"\");\n\t};\n\n\t// import a list of modules into the list\n\tlist.i = function(modules, mediaQuery) {\n\t\tif(typeof modules === \"string\")\n\t\t\tmodules = [[null, modules, \"\"]];\n\t\tvar alreadyImportedModules = {};\n\t\tfor(var i = 0; i < this.length; i++) {\n\t\t\tvar id = this[i][0];\n\t\t\tif(typeof id === \"number\")\n\t\t\t\talreadyImportedModules[id] = true;\n\t\t}\n\t\tfor(i = 0; i < modules.length; i++) {\n\t\t\tvar item = modules[i];\n\t\t\t// skip already imported module\n\t\t\t// this implementation is not 100% perfect for weird media query combinations\n\t\t\t// when a module is imported multiple times with different media queries.\n\t\t\t// I hope this will never occur (Hey this way we have smaller bundles)\n\t\t\tif(typeof item[0] !== \"number\" || !alreadyImportedModules[item[0]]) {\n\t\t\t\tif(mediaQuery && !item[2]) {\n\t\t\t\t\titem[2] = mediaQuery;\n\t\t\t\t} else if(mediaQuery) {\n\t\t\t\t\titem[2] = \"(\" + item[2] + \") and (\" + mediaQuery + \")\";\n\t\t\t\t}\n\t\t\t\tlist.push(item);\n\t\t\t}\n\t\t}\n\t};\n\treturn list;\n};\n\nfunction cssWithMappingToString(item, useSourceMap) {\n\tvar content = item[1] || '';\n\tvar cssMapping = item[3];\n\tif (!cssMapping) {\n\t\treturn content;\n\t}\n\n\tif (useSourceMap && typeof btoa === 'function') {\n\t\tvar sourceMapping = toComment(cssMapping);\n\t\tvar sourceURLs = cssMapping.sources.map(function (source) {\n\t\t\treturn '/*# sourceURL=' + cssMapping.sourceRoot + source + ' */'\n\t\t});\n\n\t\treturn [content].concat(sourceURLs).concat([sourceMapping]).join('\\n');\n\t}\n\n\treturn [content].join('\\n');\n}\n\n// Adapted from convert-source-map (MIT)\nfunction toComment(sourceMap) {\n\t// eslint-disable-next-line no-undef\n\tvar base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));\n\tvar data = 'sourceMappingURL=data:application/json;charset=utf-8;base64,' + base64;\n\n\treturn '/*# ' + data + ' */';\n}\n","(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 * Codex Editor\n *\n * Short Description (눈_눈;)\n * @version 2.0.0\n *\n * How to start?\n * Example:\n * new CodexEditor({\n * holderId : 'codex-editor',\n * initialBlock : 'text',\n * placeholder : 'Write your story....',\n * tools: {\n * quote: Quote,\n * anotherTool : AnotherTool\n * },\n * toolsConfig: {\n * quote: {\n * iconClassname : 'quote-icon',\n * displayInToolbox : true,\n * enableLineBreaks : true\n * },\n * anotherTool: {\n * iconClassname : 'tool-icon'\n * }\n * }\n * });\n *\n * - tools is an object: {\n * pluginName: PluginClass,\n * .....\n * }\n * - toolsConfig is an additional configuration that uses Codex Editor API\n * iconClassname - CSS classname of toolbox icon\n * displayInToolbox - if you want to see your Tool in toolbox hided in \"plus\" button, than set \"True\". By default : \"False\"\n * enableLineBreaks - by default enter creates new block that set as initialblock, but if you set this property \"True\", enter will break the lines in current block\n *\n * @author CodeX-Team <-- first (and deepest) node is \n * |adaddad <-- anchor node\n * <-- first (and deepest) node is \n * ...); 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","/**\r\n * Codex Editor\r\n *\r\n * Short Description (눈_눈;)\r\n * @version 2.0.0\r\n *\r\n * How to start?\r\n * Example:\r\n * new CodexEditor({\r\n * holderId : 'codex-editor',\r\n * initialBlock : 'text',\r\n * placeholder : 'Write your story....',\r\n * tools: {\r\n * quote: Quote,\r\n * anotherTool : AnotherTool\r\n * },\r\n * toolsConfig: {\r\n * quote: {\r\n * iconClassname : 'quote-icon',\r\n * displayInToolbox : true,\r\n * enableLineBreaks : true\r\n * },\r\n * anotherTool: {\r\n * iconClassname : 'tool-icon'\r\n * }\r\n * }\r\n * });\r\n *\r\n * - tools is an object: {\r\n * pluginName: PluginClass,\r\n * .....\r\n * }\r\n * - toolsConfig is an additional configuration that uses Codex Editor API\r\n * iconClassname - CSS classname of toolbox icon\r\n * displayInToolbox - if you want to see your Tool in toolbox hided in \"plus\" button, than set \"True\". By default : \"False\"\r\n * enableLineBreaks - by default enter creates new block that set as initialblock, but if you set this property \"True\", enter will break the lines in current block\r\n *\r\n * @author CodeX-Team in same block by SHIFT+ENTER and forbids to prevent default browser behaviour\r\n */\r\n if ( event.shiftKey && !enableLineBreaks ) {\n editor.callback.enterPressedOnBlock(editor.content.currentBlock, event);\r\n event.preventDefault();\r\n return;\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 ) {\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()) {\n editor.toolbar.showPlusButton();\n }\n } else {\n var islastNode = editor.content.isLastNode(currentSelectedNode);\r\n\r\n if ( islastNode && caretAtTheEndOfText ) {\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();\n }\n }\r\n\r\n /** get all inputs after new appending block */\r\n editor.ui.saveInputs();\n };\r\n\r\n /**\r\n * Escape behaviour\r\n * @param event\r\n * @private\r\n *\r\n * @description Closes toolbox and toolbar. Prevents default behaviour\r\n */\r\n var escapeKeyPressedOnRedactorsZone_ = function (event) {\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();\n };\r\n\r\n /**\r\n * @param {Event} event\r\n * @private\r\n *\r\n * closes and moves toolbar\r\n */\r\n var arrowKeyPressed_ = function (event) {\n editor.content.workingNodeChanged();\r\n\r\n /* Closing toolbar */\r\n editor.toolbar.close();\r\n editor.toolbar.move();\n };\r\n\r\n /**\r\n * @private\r\n * @param {Event} event\r\n *\r\n * @description Closes all opened bars from toolbar.\r\n * If block is mark, clears highlightning\r\n */\r\n var defaultKeyPressedOnRedactorsZone_ = function () {\n editor.toolbar.close();\r\n\r\n if (!editor.toolbar.inline.actionsOpened) {\n editor.toolbar.inline.close();\r\n editor.content.clearMark();\n }\n };\r\n\r\n /**\r\n * Handler when clicked on redactors area\r\n *\r\n * @protected\r\n * @param event\r\n *\r\n * @description Detects clicked area. If it is first-level block area, marks as detected and\r\n * on next enter press will be inserted new block\r\n * Otherwise, save carets position (input index) and put caret to the editable zone.\r\n *\r\n * @see detectWhenClickedOnFirstLevelBlockArea_\r\n *\r\n */\r\n callbacks.redactorClicked = function (event) {\n detectWhenClickedOnFirstLevelBlockArea_();\r\n\r\n editor.content.workingNodeChanged(event.target);\r\n editor.ui.saveInputs();\r\n\r\n var selectedText = editor.toolbar.inline.getSelectionText(),\r\n firstLevelBlock;\r\n\r\n /** If selection range took off, then we hide inline toolbar */\r\n if (selectedText.length === 0) {\n editor.toolbar.inline.close();\n }\r\n\r\n /** Update current input index in memory when caret focused into existed input */\r\n if (event.target.contentEditable == 'true') {\n editor.caret.saveCurrentInputIndex();\n }\r\n\r\n if (editor.content.currentNode === null) {\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) {\n /** getting firstlevel parent of input */\r\n firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);\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) {\n editor.caret.setToBlock(indexOfLastInput);\n } else {\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) {\n editor.caret.setToBlock(indexOfLastInput);\n } else {\n /** Set caret to this appended input */\r\n editor.caret.setToNextBlock(indexOfLastInput);\n }\n }\n } else {\n /** Close all panels */\r\n editor.toolbar.settings.close();\r\n editor.toolbar.toolbox.close();\n }\r\n\r\n /**\r\n * Move toolbar and open\r\n */\r\n editor.toolbar.move();\r\n editor.toolbar.open();\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 if (!inputIsEmpty) {\n /** Mark current block */\r\n editor.content.markBlock();\n }\r\n\r\n if ( isInitialType && inputIsEmpty ) {\n /** Show plus button */\r\n editor.toolbar.showPlusButton();\n }\n };\r\n\r\n /**\r\n * This method allows to define, is caret in contenteditable element or not.\r\n *\r\n * @private\r\n *\r\n * @description 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 var detectWhenClickedOnFirstLevelBlockArea_ = function () {\n var selection = window.getSelection(),\r\n anchorNode = selection.anchorNode,\r\n flag = false;\r\n\r\n if (selection.rangeCount === 0) {\n editor.content.editorAreaHightlighted = true;\n } else {\n if (!editor.core.isDomNode(anchorNode)) {\n anchorNode = anchorNode.parentNode;\n }\r\n\r\n /** Already founded, without loop */\r\n if (anchorNode.contentEditable == 'true') {\n flag = true;\n }\r\n\r\n while (anchorNode.contentEditable != 'true') {\n anchorNode = anchorNode.parentNode;\r\n\r\n if (anchorNode.contentEditable == 'true') {\n flag = true;\n }\r\n\r\n if (anchorNode == document.body) {\n break;\n }\n }\r\n\r\n /** If editable element founded, flag is \"TRUE\", Therefore we return \"FALSE\" */\r\n editor.content.editorAreaHightlighted = !flag;\n }\n };\r\n\r\n /**\r\n * Toolbar button click handler\r\n *\r\n * @param {Object} event - cursor to the button\r\n * @protected\r\n *\r\n * @description gets current tool and calls render method\r\n */\r\n callbacks.toolbarButtonClicked = function (event) {\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();\n };\r\n\r\n /**\r\n * Show or Hide toolbox when plus button is clicked\r\n */\r\n callbacks.plusButtonClicked = function () {\n if (!editor.nodes.toolbox.classList.contains('opened')) {\n editor.toolbar.toolbox.open();\n } else {\n editor.toolbar.toolbox.close();\n }\n };\r\n\r\n /**\r\n * Block handlers for KeyDown events\r\n *\r\n * @protected\r\n * @param {Object} event\r\n *\r\n * Handles keydowns on block\r\n * @see blockRightOrDownArrowPressed_\r\n * @see backspacePressed_\r\n * @see blockLeftOrUpArrowPressed_\r\n */\r\n callbacks.blockKeydown = function (event) {\n let block = event.target; // event.target is input\r\n\r\n switch (event.keyCode) {\n case editor.core.keys.DOWN:\r\n case editor.core.keys.RIGHT:\r\n blockRightOrDownArrowPressed_(event);\r\n break;\r\n\r\n case editor.core.keys.BACKSPACE:\r\n backspacePressed_(block, event);\r\n break;\r\n\r\n case editor.core.keys.UP:\r\n case editor.core.keys.LEFT:\r\n blockLeftOrUpArrowPressed_(event);\r\n break;\n }\n };\r\n\r\n /**\r\n * RIGHT or DOWN keydowns on block\r\n *\r\n * @param {Object} event\r\n * @private\r\n *\r\n * @description watches the selection and gets closest editable element.\r\n * Uses method getDeepestTextNodeFromPosition to get the last node of next block\r\n * Sets caret if it is contenteditable\r\n */\r\n var blockRightOrDownArrowPressed_ = function (event) {\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) {\n return false;\n }\r\n\r\n /** Looking for closest (parent) contentEditable element of focused node */\r\n while (focusedNode.contentEditable != 'true') {\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\n editableElementIndex ++;\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) {\n editor.caret.setToNextBlock(editableElementIndex);\r\n return;\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)) {\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);\n } else {\n deepestTextnode = lastChild;\n }\r\n\r\n caretInLastChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;\r\n\r\n if ( !caretInLastChild || !caretAtTheEndOfText ) {\n editor.core.log('arrow [down|right] : caret does not reached the end');\r\n return false;\n }\r\n\r\n editor.caret.setToNextBlock(editableElementIndex);\n };\r\n\r\n /**\r\n * LEFT or UP keydowns on block\r\n *\r\n * @param {Object} event\r\n * @private\r\n *\r\n * watches the selection and gets closest editable element.\r\n * Uses method getDeepestTextNodeFromPosition to get the last node of previous block\r\n * Sets caret if it is contenteditable\r\n *\r\n */\r\n var blockLeftOrUpArrowPressed_ = function (event) {\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) {\n return false;\n }\r\n\r\n /**\r\n * LEFT or UP not at the beginning\r\n */\r\n if ( selection.anchorOffset !== 0) {\n return false;\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\n editableElementIndex ++;\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) {\n editor.caret.setToPreviousBlock(editableElementIndex);\r\n return;\n }\r\n\r\n firstChild = focusedNode.childNodes[0];\r\n\r\n if (editor.core.isDomNode(firstChild)) {\n deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);\n } else {\n deepestTextnode = firstChild;\n }\r\n\r\n caretInFirstChild = selection.anchorNode == deepestTextnode;\r\n caretAtTheBeginning = selection.anchorOffset === 0;\r\n\r\n if ( caretInFirstChild && caretAtTheBeginning ) {\n editor.caret.setToPreviousBlock(editableElementIndex);\n }\n };\r\n\r\n /**\r\n * Handles backspace keydown\r\n *\r\n * @param {Element} block\r\n * @param {Object} event\r\n * @private\r\n *\r\n * @description if block is empty, delete the block and set caret to the previous block\r\n * If block is not empty, try to merge two blocks - current and previous\r\n * But it we try'n to remove first block, then we should set caret to the next block, not previous.\r\n * If we removed the last block, create new one\r\n */\r\n var backspacePressed_ = function (block, event) {\n var currentInputIndex = editor.caret.getCurrentInputIndex(),\r\n range,\r\n selectionLength,\r\n firstLevelBlocksCount;\r\n\r\n if (editor.core.isNativeInput(event.target)) {\n /** If input value is empty - remove block */\r\n if (event.target.value.trim() == '') {\n block.remove();\n } else {\n return;\n }\n }\r\n\r\n if (block.textContent.trim()) {\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]) {\n editor.content.mergeBlocks(currentInputIndex);\n } else {\n return;\n }\n }\r\n\r\n if (!selectionLength) {\n block.remove();\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) {\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 () {\n editor.caret.setToPreviousBlock(1);\n }, 10);\n } else {\n if (editor.caret.inputIndex !== 0) {\n /** Target block is not first */\r\n editor.caret.setToPreviousBlock(editor.caret.inputIndex);\n } else {\n /** If we try to delete first block */\r\n editor.caret.setToNextBlock(editor.caret.inputIndex);\n }\n }\r\n\r\n editor.toolbar.move();\r\n\r\n if (!editor.toolbar.opened) {\n editor.toolbar.open();\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();\n };\r\n\r\n /**\r\n * used by UI module\r\n * Clicks on block settings button\r\n *\r\n * @param {Object} event\r\n * @protected\r\n * @description Opens toolbar settings\r\n */\r\n callbacks.showSettingsButtonClicked = function (event) {\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();\n };\r\n\r\n return callbacks;\n})({});","/**\r\n * Codex Editor Caret Module\r\n *\r\n * @author Codex Team\r\n * @version 1.0\r\n */\r\n\r\nmodule.exports = (function (caret) {\n let editor = codex.editor;\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) {\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 ) {\n nodeToSet = el;\n } else {\n nodeToSet = childs[index];\n }\r\n\r\n /** If Element is INPUT */\r\n if (el.contentEditable != 'true') {\n el.focus();\r\n return;\n }\r\n\r\n if (editor.core.isDomNode(nodeToSet)) {\n nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);\n }\r\n\r\n var range = document.createRange(),\r\n selection = window.getSelection();\r\n\r\n window.setTimeout(function () {\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();\n }, 20);\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 () {\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) {\n return;\n }\r\n\r\n /** Looking for parent contentEditable block */\r\n while (focusedNode.contentEditable != 'true') {\n focusedNodeHolder = focusedNode.parentNode;\r\n focusedNode = focusedNodeHolder;\n }\r\n\r\n /** Input index in DOM level */\r\n var editableElementIndex = 0;\r\n\r\n while (focusedNode != inputs[editableElementIndex]) {\n editableElementIndex ++;\n }\r\n\r\n caret.inputIndex = editableElementIndex;\n };\r\n\r\n /**\r\n * Returns current input index (caret object)\r\n */\r\n caret.getCurrentInputIndex = function () {\n return caret.inputIndex;\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) {\n var inputs = editor.state.inputs,\r\n nextInput = inputs[index + 1];\r\n\r\n if (!nextInput) {\n editor.core.log('We are reached the end');\r\n return;\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) {\n var emptyTextElement = document.createTextNode('');\r\n\r\n nextInput.appendChild(emptyTextElement);\n }\r\n\r\n editor.caret.inputIndex = index + 1;\r\n editor.caret.set(nextInput, 0, 0);\r\n editor.content.workingNodeChanged(nextInput);\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) {\n var inputs = editor.state.inputs,\r\n targetInput = inputs[index];\r\n\r\n if ( !targetInput ) {\n return;\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) {\n var emptyTextElement = document.createTextNode('');\r\n\r\n targetInput.appendChild(emptyTextElement);\n }\r\n\r\n editor.caret.inputIndex = index;\r\n editor.caret.set(targetInput, 0, 0);\r\n editor.content.workingNodeChanged(targetInput);\n };\r\n\r\n /**\r\n * @param {int} index - index of input\r\n */\r\n caret.setToPreviousBlock = function (index) {\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) {\n editor.core.log('We are reached first node');\r\n return;\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) {\n emptyTextElement = document.createTextNode('');\r\n previousInput.appendChild(emptyTextElement);\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]);\n };\r\n\r\n caret.position = {\r\n\r\n atStart : function () {\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)) {\n anchorNode = anchorNode.parentNode;\n }\r\n\r\n var isFirstNode = anchorNode === pluginsRender.childNodes[0],\r\n isOffsetZero = anchorOffset === 0;\r\n\r\n return isFirstNode && isOffsetZero;\n },\r\n\r\n atTheEnd : function () {\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;\n }\r\n };\r\n\r\n\r\n /**\r\n * Inserts node at the caret location\r\n * @param {HTMLElement|DocumentFragment} node\r\n */\r\n caret.insertNode = function (node) {\n var selection, range,\r\n lastNode = node;\r\n\r\n if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) {\n lastNode = node.lastChild;\n }\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(node);\r\n\r\n range.setStartAfter(lastNode);\r\n range.collapse(true);\r\n\r\n selection.removeAllRanges();\r\n selection.addRange(range);\n };\r\n\r\n return caret;\n})({});","/**\r\n * Codex Editor Content Module\r\n * Works with DOM\r\n *\r\n * @class Content\r\n * @classdesc Class works provides COdex Editor appearance logic\r\n *\r\n * @author Codex Team\r\n * @version 2.0.0\r\n */\r\n\r\nimport $ from '../dom';\r\n\r\nmodule.exports = class Content {\n /**\r\n * Module key name\r\n * @returns {string}\r\n */\r\n static get name() {\n return 'Content';\n }\r\n\r\n /**\r\n * @constructor\r\n *\r\n * @param {EditorConfig} config\r\n */\r\n constructor(config) {\n this.config = config;\r\n this.Editor = null;\r\n\r\n this.CSS = {\r\n block: 'ce-block',\r\n content: 'ce-block__content',\r\n stretched: 'ce-block--stretched',\r\n highlighted: 'ce-block--highlighted',\r\n };\r\n\r\n this._currentNode = null;\r\n this._currentIndex = 0;\n }\r\n\r\n /**\r\n * Editor modules setter\r\n * @param {object} Editor\r\n */\r\n set state(Editor) {\n this.Editor = Editor;\n }\r\n\r\n /**\r\n * Get current working node\r\n *\r\n * @returns {null|HTMLElement}\r\n */\r\n get currentNode() {\n return this._currentNode;\n }\r\n\r\n /**\r\n * Set working node. Working node should be first level block, so we find it before set one to _currentNode property\r\n *\r\n * @param {HTMLElement} node\r\n */\r\n set currentNode(node) {\n let firstLevelBlock = this.getFirstLevelBlock(node);\r\n\r\n this._currentNode = firstLevelBlock;\n }\r\n\r\n\r\n /**\r\n * @private\r\n * @param pluginHTML\r\n * @param {Boolean} isStretched - make stretched block or not\r\n *\r\n * @description adds necessary information to wrap new created block by first-level holder\r\n */\r\n composeBlock_(pluginHTML, isStretched = false) {\n let block = $.make('DIV', this.CSS.block),\r\n blockContent = $.make('DIV', this.CSS.content);\r\n\r\n blockContent.appendChild(pluginHTML);\r\n block.appendChild(blockContent);\r\n\r\n if (isStretched) {\n blockContent.classList.add(this.CSS.stretched);\n }\r\n\r\n block.dataset.toolId = this._currentIndex++;\r\n\r\n return block;\n };\r\n\r\n /**\r\n * Finds first-level block\r\n * @description looks for first-level block.\r\n * gets parent while node is not first-level\r\n *\r\n * @param {Element} node - selected or clicked in redactors area node\r\n * @protected\r\n *\r\n */\r\n getFirstLevelBlock(node) {\n if (!$.isElement(node)) {\n node = node.parentNode;\n }\r\n\r\n if (node === this.Editor.ui.nodes.redactor || node === document.body) {\n return null;\n } else {\n while(node.classList && !node.classList.contains(this.CSS.block)) {\n node = node.parentNode;\n }\r\n\r\n return node;\n }\n };\r\n\r\n /**\r\n * Insert new block to working area\r\n *\r\n * @param {HTMLElement} tool\r\n *\r\n * @returns {Number} tool index\r\n *\r\n */\r\n insertBlock(tool) {\n let newBlock = this.composeBlock_(tool);\r\n\r\n if (this.currentNode) {\n this.currentNode.insertAdjacentElement('afterend', newBlock);\n } else {\n /**\r\n * If redactor is empty, append as first child\r\n */\r\n this.Editor.ui.nodes.redactor.appendChild(newBlock);\n }\r\n\r\n /**\r\n * Set new node as current\r\n */\r\n this.currentNode = newBlock;\r\n\r\n return newBlock.dataset.toolId;\n }\n};\r\n\r\n// module.exports = (function (content) {\r\n//\r\n// let editor = codex.editor;\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// * @deprecated\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// * Appends background to the block\r\n// *\r\n// * @description add CSS class to highlight visually first-level block area\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// * @description clears styles that highlights block\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// * Finds first-level block\r\n// *\r\n// * @param {Element} node - selected or clicked in redactors area node\r\n// * @protected\r\n// *\r\n// * @description looks for first-level block.\r\n// * gets parent while node is not first-level\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// * @protected\r\n// *\r\n// * @description 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// content.currentNode = content.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// /** 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// * @protected\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// isStretched = blockData.stretched;\r\n//\r\n// var newBlock = composeNewBlock_(newBlockContent, blockType, isStretched);\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// tool = tool || editor.content.currentNode.dataset.tool;\r\n// var newBlockComposed = 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// * @protected\r\n// *\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// * @param {Element} block - current plugins render\r\n// * @param {String} tool - plugins name\r\n// * @param {Boolean} isStretched - make stretched block or not\r\n// *\r\n// * @description adds necessary information to wrap new created block by first-level holder\r\n// */\r\n// var 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// return newBlock;\r\n//\r\n// };\r\n//\r\n// /**\r\n// * Returns Range object of current selection\r\n// * @protected\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// *\r\n// * @protected\r\n// * @param {int} inputIndex - target input index\r\n// *\r\n// * @description splits current input content to the separate blocks\r\n// * When enter is pressed among the words, that text will be splited.\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// * @protected\r\n// * @param {int} currentInputIndex\r\n// * @param {int} targetInputIndex\r\n// *\r\n// * @description gets two inputs indexes and merges into one\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// * 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} htmlData - html content as string\r\n// * @param {string} plainData - plain text\r\n// * @return {string} - html content as string\r\n// */\r\n// content.wrapTextWithParagraphs = function (htmlData, plainData) {\r\n//\r\n// if (!htmlData.trim()) {\r\n//\r\n// return wrapPlainTextWithParagraphs(plainData);\r\n//\r\n// }\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 = htmlData;\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// /**\r\n// * Splits strings on new line and wraps paragraphs with tag\r\n// * @param plainText\r\n// * @returns {string}\r\n// */\r\n// var wrapPlainTextWithParagraphs = function (plainText) {\r\n//\r\n// if (!plainText) return '';\r\n//\r\n// return ' ' + plainText.split('\\n\\n').join(' ') + ' tags to split it logically\r\n * @type {string}\r\n */\r\n wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData);\r\n paragraphs.innerHTML = wrappedData;\r\n\r\n /**\r\n * If there only one paragraph, just insert in at the caret location\r\n */\r\n if (paragraphs.childNodes.length == 1) {\n emulateUserAgentBehaviour(paragraphs.firstChild);\r\n return;\n }\r\n\r\n insertPastedParagraphs(paragraphs.childNodes);\n };\r\n\r\n /**\r\n * Checks if we should handle paste event on block\r\n * @param block\r\n *\r\n * @return {boolean}\r\n */\r\n var needsToHandlePasteEvent = function (block) {\n /** If area is input or textarea then allow default behaviour */\r\n if ( editor.core.isNativeInput(block) ) {\n return false;\n }\r\n\r\n var editableParent = editor.content.getEditableParent(block);\r\n\r\n /** Allow paste when event target placed in Editable element */\r\n if (!editableParent) {\n return false;\n }\r\n\r\n return true;\n };\r\n\r\n /**\r\n * Inserts new initial plugin blocks with data in paragraphs\r\n *\r\n * @param {Array} paragraphs - array of paragraphs ( <-- first (and deepest) node is \r\n * |adaddad <-- anchor node\r\n * <-- first (and deepest) node is \r\n * in same block by SHIFT+ENTER and forbids to prevent default browser behaviour
*/
- if ( event.shiftKey && !enableLineBreaks ) {
+ if ( event.shiftKey && !enableLineBreaks ) {
+ editor.callback.enterPressedOnBlock(editor.content.currentBlock, event);
+ event.preventDefault();
+ return;
+ }
- editor.callback.enterPressedOnBlock(editor.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';
+ isTextNodeHasParentBetweenContenteditable = currentSelectedNode && currentSelectedNode.parentNode.contentEditable != 'true';
- /**
+ /**
* Split blocks when input has several nodes and caret placed in textNode
*/
- if (
- currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT &&
+ if (
+ currentSelectedNode.nodeType == editor.core.nodeTypes.TEXT &&
!isTextNodeHasParentBetweenContenteditable &&
!caretAtTheEndOfText
- ) {
+ ) {
+ event.preventDefault();
- event.preventDefault();
+ editor.core.log('Splitting Text node...');
- editor.core.log('Splitting Text node...');
+ editor.content.splitBlock(currentInputIndex);
- editor.content.splitBlock(currentInputIndex);
+ /** Show plus button when next input after split is empty*/
+ if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) {
+ editor.toolbar.showPlusButton();
+ }
+ } else {
+ var islastNode = editor.content.isLastNode(currentSelectedNode);
- /** Show plus button when next input after split is empty*/
- if (!editor.state.inputs[currentInputIndex + 1].textContent.trim()) {
+ if ( islastNode && caretAtTheEndOfText ) {
+ event.preventDefault();
+ event.stopPropagation();
+ event.stopImmediatePropagation();
- editor.toolbar.showPlusButton();
+ editor.core.log('ENTER clicked in last textNode. Create new BLOCK');
- }
+ editor.content.insertBlock({
+ type: NEW_BLOCK_TYPE,
+ block: editor.tools[NEW_BLOCK_TYPE].render()
+ }, true);
- } else {
+ editor.toolbar.move();
+ editor.toolbar.open();
- var islastNode = editor.content.isLastNode(currentSelectedNode);
+ /** Show plus button with empty block */
+ editor.toolbar.showPlusButton();
+ }
+ }
- if ( islastNode && caretAtTheEndOfText ) {
-
- event.preventDefault();
- event.stopPropagation();
- event.stopImmediatePropagation();
-
- editor.core.log('ENTER clicked in last textNode. Create new BLOCK');
-
- editor.content.insertBlock({
- type: NEW_BLOCK_TYPE,
- block: editor.tools[NEW_BLOCK_TYPE].render()
- }, true);
-
- editor.toolbar.move();
- editor.toolbar.open();
-
- /** Show plus button with empty block */
- editor.toolbar.showPlusButton();
-
- }
-
- }
-
- /** get all inputs after new appending block */
- editor.ui.saveInputs();
-
- };
+ /** get all inputs after new appending block */
+ editor.ui.saveInputs();
+ };
/**
* Escape behaviour
@@ -293,17 +246,15 @@ module.exports = (function (callbacks) {
*
* @description Closes toolbox and toolbar. Prevents default behaviour
*/
- var escapeKeyPressedOnRedactorsZone_ = function (event) {
+ var escapeKeyPressedOnRedactorsZone_ = function (event) {
+ /** Close all toolbar */
+ editor.toolbar.close();
- /** Close all toolbar */
- editor.toolbar.close();
+ /** Close toolbox */
+ editor.toolbar.toolbox.close();
- /** Close toolbox */
- editor.toolbar.toolbox.close();
-
- event.preventDefault();
-
- };
+ event.preventDefault();
+ };
/**
* @param {Event} event
@@ -311,15 +262,13 @@ module.exports = (function (callbacks) {
*
* closes and moves toolbar
*/
- var arrowKeyPressed_ = function (event) {
+ var arrowKeyPressed_ = function (event) {
+ editor.content.workingNodeChanged();
- editor.content.workingNodeChanged();
-
- /* Closing toolbar */
- editor.toolbar.close();
- editor.toolbar.move();
-
- };
+ /* Closing toolbar */
+ editor.toolbar.close();
+ editor.toolbar.move();
+ };
/**
* @private
@@ -328,18 +277,14 @@ module.exports = (function (callbacks) {
* @description Closes all opened bars from toolbar.
* If block is mark, clears highlightning
*/
- var defaultKeyPressedOnRedactorsZone_ = function () {
+ var defaultKeyPressedOnRedactorsZone_ = function () {
+ editor.toolbar.close();
- editor.toolbar.close();
-
- if (!editor.toolbar.inline.actionsOpened) {
-
- editor.toolbar.inline.close();
- editor.content.clearMark();
-
- }
-
- };
+ if (!editor.toolbar.inline.actionsOpened) {
+ editor.toolbar.inline.close();
+ editor.content.clearMark();
+ }
+ };
/**
* Handler when clicked on redactors area
@@ -354,114 +299,89 @@ module.exports = (function (callbacks) {
* @see detectWhenClickedOnFirstLevelBlockArea_
*
*/
- callbacks.redactorClicked = function (event) {
+ callbacks.redactorClicked = function (event) {
+ detectWhenClickedOnFirstLevelBlockArea_();
- detectWhenClickedOnFirstLevelBlockArea_();
+ editor.content.workingNodeChanged(event.target);
+ editor.ui.saveInputs();
- editor.content.workingNodeChanged(event.target);
- editor.ui.saveInputs();
+ var selectedText = editor.toolbar.inline.getSelectionText(),
+ firstLevelBlock;
- var selectedText = editor.toolbar.inline.getSelectionText(),
- firstLevelBlock;
+ /** If selection range took off, then we hide inline toolbar */
+ if (selectedText.length === 0) {
+ editor.toolbar.inline.close();
+ }
- /** If selection range took off, then we hide inline toolbar */
- if (selectedText.length === 0) {
+ /** Update current input index in memory when caret focused into existed input */
+ if (event.target.contentEditable == 'true') {
+ editor.caret.saveCurrentInputIndex();
+ }
- editor.toolbar.inline.close();
-
- }
-
- /** Update current input index in memory when caret focused into existed input */
- if (event.target.contentEditable == 'true') {
-
- editor.caret.saveCurrentInputIndex();
-
- }
-
- if (editor.content.currentNode === null) {
-
- /**
+ if (editor.content.currentNode === null) {
+ /**
* If inputs in redactor does not exits, then we put input index 0 not -1
*/
- var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0;
+ var indexOfLastInput = editor.state.inputs.length > 0 ? editor.state.inputs.length - 1 : 0;
- /** If we have any inputs */
- if (editor.state.inputs.length) {
+ /** If we have any inputs */
+ if (editor.state.inputs.length) {
+ /** getting firstlevel parent of input */
+ firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);
+ }
- /** getting firstlevel parent of input */
- firstLevelBlock = editor.content.getFirstLevelBlock(editor.state.inputs[indexOfLastInput]);
+ /** If input is empty, then we set caret to the last input */
+ if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) {
+ editor.caret.setToBlock(indexOfLastInput);
+ } else {
+ /** Create new input when caret clicked in redactors area */
+ var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;
- }
-
- /** If input is empty, then we set caret to the last input */
- if (editor.state.inputs.length && editor.state.inputs[indexOfLastInput].textContent === '' && firstLevelBlock.dataset.tool == editor.settings.initialBlockPlugin) {
-
- editor.caret.setToBlock(indexOfLastInput);
-
- } else {
-
- /** Create new input when caret clicked in redactors area */
- var NEW_BLOCK_TYPE = editor.settings.initialBlockPlugin;
-
- editor.content.insertBlock({
- type : NEW_BLOCK_TYPE,
- block : editor.tools[NEW_BLOCK_TYPE].render()
- });
-
- /** If there is no inputs except inserted */
- if (editor.state.inputs.length === 1) {
-
- editor.caret.setToBlock(indexOfLastInput);
-
- } else {
-
- /** Set caret to this appended input */
- editor.caret.setToNextBlock(indexOfLastInput);
-
- }
-
- }
+ editor.content.insertBlock({
+ type : NEW_BLOCK_TYPE,
+ block : editor.tools[NEW_BLOCK_TYPE].render()
+ });
+ /** If there is no inputs except inserted */
+ if (editor.state.inputs.length === 1) {
+ editor.caret.setToBlock(indexOfLastInput);
} else {
-
- /** Close all panels */
- editor.toolbar.settings.close();
- editor.toolbar.toolbox.close();
-
+ /** Set caret to this appended input */
+ editor.caret.setToNextBlock(indexOfLastInput);
}
-
- /**
- * Move toolbar and open
- */
- editor.toolbar.move();
- editor.toolbar.open();
-
- var inputIsEmpty = !editor.content.currentNode.textContent.trim(),
- currentNodeType = editor.content.currentNode.dataset.tool,
- isInitialType = currentNodeType == editor.settings.initialBlockPlugin;
-
-
- /** Hide plus buttons */
- editor.toolbar.hidePlusButton();
-
- if (!inputIsEmpty) {
-
- /** Mark current block */
- editor.content.markBlock();
-
- }
-
- if ( isInitialType && inputIsEmpty ) {
-
- /** Show plus button */
- editor.toolbar.showPlusButton();
-
- }
-
-
- };
+ }
+ } else {
+ /** Close all panels */
+ editor.toolbar.settings.close();
+ editor.toolbar.toolbox.close();
+ }
/**
+ * Move toolbar and open
+ */
+ editor.toolbar.move();
+ editor.toolbar.open();
+
+ var inputIsEmpty = !editor.content.currentNode.textContent.trim(),
+ currentNodeType = editor.content.currentNode.dataset.tool,
+ isInitialType = currentNodeType == editor.settings.initialBlockPlugin;
+
+
+ /** Hide plus buttons */
+ editor.toolbar.hidePlusButton();
+
+ if (!inputIsEmpty) {
+ /** Mark current block */
+ editor.content.markBlock();
+ }
+
+ if ( isInitialType && inputIsEmpty ) {
+ /** Show plus button */
+ editor.toolbar.showPlusButton();
+ }
+ };
+
+ /**
* This method allows to define, is caret in contenteditable element or not.
*
* @private
@@ -471,55 +391,39 @@ module.exports = (function (callbacks) {
* 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.
*/
- var detectWhenClickedOnFirstLevelBlockArea_ = function () {
+ var detectWhenClickedOnFirstLevelBlockArea_ = function () {
+ var selection = window.getSelection(),
+ anchorNode = selection.anchorNode,
+ flag = false;
- var selection = window.getSelection(),
- anchorNode = selection.anchorNode,
- flag = false;
+ if (selection.rangeCount === 0) {
+ editor.content.editorAreaHightlighted = true;
+ } else {
+ if (!editor.core.isDomNode(anchorNode)) {
+ anchorNode = anchorNode.parentNode;
+ }
- if (selection.rangeCount === 0) {
+ /** Already founded, without loop */
+ if (anchorNode.contentEditable == 'true') {
+ flag = true;
+ }
- editor.content.editorAreaHightlighted = true;
-
- } else {
-
- if (!editor.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" */
- editor.content.editorAreaHightlighted = !flag;
+ 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" */
+ editor.content.editorAreaHightlighted = !flag;
+ }
+ };
/**
* Toolbar button click handler
@@ -529,35 +433,27 @@ module.exports = (function (callbacks) {
*
* @description gets current tool and calls render method
*/
- callbacks.toolbarButtonClicked = function (event) {
+ callbacks.toolbarButtonClicked = function (event) {
+ var button = this;
- var button = this;
+ editor.toolbar.current = button.dataset.type;
- editor.toolbar.current = button.dataset.type;
+ editor.toolbar.toolbox.toolClicked(event);
+ editor.toolbar.close();
+ };
- editor.toolbar.toolbox.toolClicked(event);
- editor.toolbar.close();
-
- };
-
- /**
+ /**
* Show or Hide toolbox when plus button is clicked
*/
- callbacks.plusButtonClicked = function () {
+ callbacks.plusButtonClicked = function () {
+ if (!editor.nodes.toolbox.classList.contains('opened')) {
+ editor.toolbar.toolbox.open();
+ } else {
+ editor.toolbar.toolbox.close();
+ }
+ };
- if (!editor.nodes.toolbox.classList.contains('opened')) {
-
- editor.toolbar.toolbox.open();
-
- } else {
-
- editor.toolbar.toolbox.close();
-
- }
-
- };
-
- /**
+ /**
* Block handlers for KeyDown events
*
* @protected
@@ -568,31 +464,27 @@ module.exports = (function (callbacks) {
* @see backspacePressed_
* @see blockLeftOrUpArrowPressed_
*/
- callbacks.blockKeydown = function (event) {
+ callbacks.blockKeydown = function (event) {
+ let block = event.target; // event.target is input
- let block = event.target; // event.target is input
+ switch (event.keyCode) {
+ case editor.core.keys.DOWN:
+ case editor.core.keys.RIGHT:
+ blockRightOrDownArrowPressed_(event);
+ break;
- switch (event.keyCode) {
+ case editor.core.keys.BACKSPACE:
+ backspacePressed_(block, event);
+ break;
- case editor.core.keys.DOWN:
- case editor.core.keys.RIGHT:
- blockRightOrDownArrowPressed_(event);
- break;
+ case editor.core.keys.UP:
+ case editor.core.keys.LEFT:
+ blockLeftOrUpArrowPressed_(event);
+ break;
+ }
+ };
- case editor.core.keys.BACKSPACE:
- backspacePressed_(block, event);
- break;
-
- case editor.core.keys.UP:
- case editor.core.keys.LEFT:
- blockLeftOrUpArrowPressed_(event);
- break;
-
- }
-
- };
-
- /**
+ /**
* RIGHT or DOWN keydowns on block
*
* @param {Object} event
@@ -602,82 +494,66 @@ module.exports = (function (callbacks) {
* Uses method getDeepestTextNodeFromPosition to get the last node of next block
* Sets caret if it is contenteditable
*/
- var blockRightOrDownArrowPressed_ = function (event) {
+ var blockRightOrDownArrowPressed_ = function (event) {
+ var selection = window.getSelection(),
+ inputs = editor.state.inputs,
+ focusedNode = selection.anchorNode,
+ focusedNodeHolder;
- var selection = window.getSelection(),
- inputs = editor.state.inputs,
- focusedNode = selection.anchorNode,
- focusedNodeHolder;
+ /** Check for caret existance */
+ if (!focusedNode) {
+ return false;
+ }
- /** Check for caret existance */
- if (!focusedNode) {
+ /** Looking for closest (parent) contentEditable element of focused node */
+ while (focusedNode.contentEditable != 'true') {
+ focusedNodeHolder = focusedNode.parentNode;
+ focusedNode = focusedNodeHolder;
+ }
- return false;
+ /** Input index in DOM level */
+ var editableElementIndex = 0;
- }
+ while (focusedNode != inputs[editableElementIndex]) {
+ editableElementIndex ++;
+ }
- /** 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) {
+ if (!focusedNode.textContent) {
+ editor.caret.setToNextBlock(editableElementIndex);
+ return;
+ }
- editor.caret.setToNextBlock(editableElementIndex);
- return;
-
- }
-
- /**
+ /**
* Do nothing when caret doesn not reaches the end of last child
*/
- var caretInLastChild = false,
- caretAtTheEndOfText = false;
+ var caretInLastChild = false,
+ caretAtTheEndOfText = false;
- var lastChild,
- deepestTextnode;
+ var lastChild,
+ deepestTextnode;
- lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ];
+ lastChild = focusedNode.childNodes[focusedNode.childNodes.length - 1 ];
- if (editor.core.isDomNode(lastChild)) {
+ if (editor.core.isDomNode(lastChild)) {
+ deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);
+ } else {
+ deepestTextnode = lastChild;
+ }
- deepestTextnode = editor.content.getDeepestTextNodeFromPosition(lastChild, lastChild.childNodes.length);
+ caretInLastChild = selection.anchorNode == deepestTextnode;
+ caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;
- } else {
+ if ( !caretInLastChild || !caretAtTheEndOfText ) {
+ editor.core.log('arrow [down|right] : caret does not reached the end');
+ return false;
+ }
- deepestTextnode = lastChild;
-
- }
-
- caretInLastChild = selection.anchorNode == deepestTextnode;
- caretAtTheEndOfText = deepestTextnode.length == selection.anchorOffset;
-
- if ( !caretInLastChild || !caretAtTheEndOfText ) {
-
- editor.core.log('arrow [down|right] : caret does not reached the end');
- return false;
-
- }
-
- editor.caret.setToNextBlock(editableElementIndex);
-
- };
+ editor.caret.setToNextBlock(editableElementIndex);
+ };
/**
* LEFT or UP keydowns on block
@@ -690,88 +566,70 @@ module.exports = (function (callbacks) {
* Sets caret if it is contenteditable
*
*/
- var blockLeftOrUpArrowPressed_ = function (event) {
+ var blockLeftOrUpArrowPressed_ = function (event) {
+ var selection = window.getSelection(),
+ inputs = editor.state.inputs,
+ focusedNode = selection.anchorNode,
+ focusedNodeHolder;
- var selection = window.getSelection(),
- inputs = editor.state.inputs,
- focusedNode = selection.anchorNode,
- focusedNodeHolder;
+ /** Check for caret existance */
+ if (!focusedNode) {
+ return false;
+ }
- /** Check for caret existance */
- if (!focusedNode) {
-
- return false;
-
- }
-
- /**
+ /**
* LEFT or UP not at the beginning
*/
- if ( selection.anchorOffset !== 0) {
+ if ( selection.anchorOffset !== 0) {
+ return false;
+ }
- return false;
+ /** Looking for parent contentEditable block */
+ while (focusedNode.contentEditable != 'true') {
+ focusedNodeHolder = focusedNode.parentNode;
+ focusedNode = focusedNodeHolder;
+ }
- }
+ /** Input index in DOM level */
+ var editableElementIndex = 0;
- /** Looking for parent contentEditable block */
- while (focusedNode.contentEditable != 'true') {
+ while (focusedNode != inputs[editableElementIndex]) {
+ editableElementIndex ++;
+ }
- 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 caretInFirstChild = false,
+ caretAtTheBeginning = false;
- var firstChild,
- deepestTextnode;
+ var firstChild,
+ deepestTextnode;
- /**
+ /**
* Founded contentEditable element doesn't have childs
* Or maybe New created block
*/
- if (!focusedNode.textContent) {
+ if (!focusedNode.textContent) {
+ editor.caret.setToPreviousBlock(editableElementIndex);
+ return;
+ }
- editor.caret.setToPreviousBlock(editableElementIndex);
- return;
+ firstChild = focusedNode.childNodes[0];
- }
+ if (editor.core.isDomNode(firstChild)) {
+ deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);
+ } else {
+ deepestTextnode = firstChild;
+ }
- firstChild = focusedNode.childNodes[0];
+ caretInFirstChild = selection.anchorNode == deepestTextnode;
+ caretAtTheBeginning = selection.anchorOffset === 0;
- if (editor.core.isDomNode(firstChild)) {
-
- deepestTextnode = editor.content.getDeepestTextNodeFromPosition(firstChild, 0);
-
- } else {
-
- deepestTextnode = firstChild;
-
- }
-
- caretInFirstChild = selection.anchorNode == deepestTextnode;
- caretAtTheBeginning = selection.anchorOffset === 0;
-
- if ( caretInFirstChild && caretAtTheBeginning ) {
-
- editor.caret.setToPreviousBlock(editableElementIndex);
-
- }
-
- };
+ if ( caretInFirstChild && caretAtTheBeginning ) {
+ editor.caret.setToPreviousBlock(editableElementIndex);
+ }
+ };
/**
* Handles backspace keydown
@@ -785,106 +643,78 @@ module.exports = (function (callbacks) {
* But it we try'n to remove first block, then we should set caret to the next block, not previous.
* If we removed the last block, create new one
*/
- var backspacePressed_ = function (block, event) {
+ var backspacePressed_ = function (block, event) {
+ var currentInputIndex = editor.caret.getCurrentInputIndex(),
+ range,
+ selectionLength,
+ firstLevelBlocksCount;
- var currentInputIndex = editor.caret.getCurrentInputIndex(),
- range,
- selectionLength,
- firstLevelBlocksCount;
+ if (editor.core.isNativeInput(event.target)) {
+ /** If input value is empty - remove block */
+ if (event.target.value.trim() == '') {
+ block.remove();
+ } else {
+ return;
+ }
+ }
- if (editor.core.isNativeInput(event.target)) {
+ if (block.textContent.trim()) {
+ range = editor.content.getRange();
+ selectionLength = range.endOffset - range.startOffset;
- /** If input value is empty - remove block */
- if (event.target.value.trim() == '') {
+ if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) {
+ editor.content.mergeBlocks(currentInputIndex);
+ } else {
+ return;
+ }
+ }
- block.remove();
-
- } else {
-
- return;
-
- }
-
- }
-
- if (block.textContent.trim()) {
-
- range = editor.content.getRange();
- selectionLength = range.endOffset - range.startOffset;
-
- if (editor.caret.position.atStart() && !selectionLength && editor.state.inputs[currentInputIndex - 1]) {
-
- editor.content.mergeBlocks(currentInputIndex);
-
- } else {
-
- return;
-
- }
-
- }
-
- if (!selectionLength) {
-
- block.remove();
-
- }
+ if (!selectionLength) {
+ block.remove();
+ }
- firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;
+ firstLevelBlocksCount = editor.nodes.redactor.childNodes.length;
- /**
+ /**
* If all blocks are removed
*/
- if (firstLevelBlocksCount === 0) {
+ if (firstLevelBlocksCount === 0) {
+ /** update currentNode variable */
+ editor.content.currentNode = null;
- /** update currentNode variable */
- editor.content.currentNode = null;
+ /** Inserting new empty initial block */
+ editor.ui.addInitialBlock();
- /** Inserting new empty initial block */
- editor.ui.addInitialBlock();
+ /** Updating inputs state after deleting last block */
+ editor.ui.saveInputs();
- /** Updating inputs state after deleting last block */
- editor.ui.saveInputs();
+ /** Set to current appended block */
+ window.setTimeout(function () {
+ editor.caret.setToPreviousBlock(1);
+ }, 10);
+ } else {
+ if (editor.caret.inputIndex !== 0) {
+ /** Target block is not first */
+ editor.caret.setToPreviousBlock(editor.caret.inputIndex);
+ } else {
+ /** If we try to delete first block */
+ editor.caret.setToNextBlock(editor.caret.inputIndex);
+ }
+ }
- /** Set to current appended block */
- window.setTimeout(function () {
+ editor.toolbar.move();
- editor.caret.setToPreviousBlock(1);
+ if (!editor.toolbar.opened) {
+ editor.toolbar.open();
+ }
- }, 10);
+ /** Updating inputs state */
+ editor.ui.saveInputs();
- } else {
-
- if (editor.caret.inputIndex !== 0) {
-
- /** Target block is not first */
- editor.caret.setToPreviousBlock(editor.caret.inputIndex);
-
- } else {
-
- /** If we try to delete first block */
- editor.caret.setToNextBlock(editor.caret.inputIndex);
-
- }
-
- }
-
- editor.toolbar.move();
-
- if (!editor.toolbar.opened) {
-
- editor.toolbar.open();
-
- }
-
- /** Updating inputs state */
- editor.ui.saveInputs();
-
- /** Prevent default browser behaviour */
- event.preventDefault();
-
- };
+ /** Prevent default browser behaviour */
+ event.preventDefault();
+ };
/**
* used by UI module
@@ -894,24 +724,21 @@ module.exports = (function (callbacks) {
* @protected
* @description Opens toolbar settings
*/
- callbacks.showSettingsButtonClicked = function (event) {
-
- /**
+ callbacks.showSettingsButtonClicked = function (event) {
+ /**
* 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 = editor.content.currentNode.dataset.tool;
+ var currentToolType = editor.content.currentNode.dataset.tool;
- editor.toolbar.settings.toggle(currentToolType);
+ editor.toolbar.settings.toggle(currentToolType);
- /** Close toolbox when settings button is active */
- editor.toolbar.toolbox.close();
- editor.toolbar.settings.hideRemoveActions();
-
- };
-
- return callbacks;
+ /** Close toolbox when settings button is active */
+ editor.toolbar.toolbox.close();
+ editor.toolbar.settings.hideRemoveActions();
+ };
+ return callbacks;
})({});
\ No newline at end of file
diff --git a/src/components/modules/_caret.js b/src/components/modules/_caret.js
index 8c151709..ecae4c0c 100644
--- a/src/components/modules/_caret.js
+++ b/src/components/modules/_caret.js
@@ -6,300 +6,247 @@
*/
module.exports = (function (caret) {
+ let editor = codex.editor;
- let editor = codex.editor;
-
- /**
+ /**
* @var {int} InputIndex - editable element in DOM
*/
- caret.inputIndex = null;
+ caret.inputIndex = null;
- /**
+ /**
* @var {int} offset - caret position in a text node.
*/
- caret.offset = null;
+ caret.offset = null;
- /**
+ /**
* @var {int} focusedNodeIndex - we get index of child node from first-level block
*/
- caret.focusedNodeIndex = null;
+ 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) {
+ caret.set = function ( el, index, offset) {
+ offset = offset || caret.offset || 0;
+ index = index || caret.focusedNodeIndex || 0;
- offset = offset || caret.offset || 0;
- index = index || caret.focusedNodeIndex || 0;
+ var childs = el.childNodes,
+ nodeToSet;
- var childs = el.childNodes,
- nodeToSet;
+ if ( childs.length === 0 ) {
+ nodeToSet = el;
+ } else {
+ nodeToSet = childs[index];
+ }
- if ( childs.length === 0 ) {
+ /** If Element is INPUT */
+ if (el.contentEditable != 'true') {
+ el.focus();
+ return;
+ }
- nodeToSet = el;
+ if (editor.core.isDomNode(nodeToSet)) {
+ nodeToSet = editor.content.getDeepestTextNodeFromPosition(nodeToSet, nodeToSet.childNodes.length);
+ }
- } else {
+ var range = document.createRange(),
+ selection = window.getSelection();
- nodeToSet = childs[index];
+ window.setTimeout(function () {
+ range.setStart(nodeToSet, offset);
+ range.setEnd(nodeToSet, offset);
- }
+ selection.removeAllRanges();
+ selection.addRange(range);
- /** If Element is INPUT */
- if (el.contentEditable != 'true') {
+ editor.caret.saveCurrentInputIndex();
+ }, 20);
+ };
- 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 () {
+ caret.saveCurrentInputIndex = function () {
+ /** Index of Input that we paste sanitized content */
+ var selection = window.getSelection(),
+ inputs = editor.state.inputs,
+ focusedNode = selection.anchorNode,
+ focusedNodeHolder;
- /** Index of Input that we paste sanitized content */
- var selection = window.getSelection(),
- inputs = editor.state.inputs,
- focusedNode = selection.anchorNode,
- focusedNodeHolder;
+ if (!focusedNode) {
+ return;
+ }
- if (!focusedNode) {
+ /** Looking for parent contentEditable block */
+ while (focusedNode.contentEditable != 'true') {
+ focusedNodeHolder = focusedNode.parentNode;
+ focusedNode = focusedNodeHolder;
+ }
- return;
+ /** Input index in DOM level */
+ var editableElementIndex = 0;
- }
+ while (focusedNode != inputs[editableElementIndex]) {
+ editableElementIndex ++;
+ }
- /** Looking for parent contentEditable block */
- while (focusedNode.contentEditable != 'true') {
+ caret.inputIndex = editableElementIndex;
+ };
- 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 () {
+ caret.getCurrentInputIndex = function () {
+ return caret.inputIndex;
+ };
- return caret.inputIndex;
-
- };
-
- /**
+ /**
* @param {int} index - index of first-level block after that we set caret into next input
*/
- caret.setToNextBlock = function (index) {
+ caret.setToNextBlock = function (index) {
+ var inputs = editor.state.inputs,
+ nextInput = inputs[index + 1];
- var inputs = editor.state.inputs,
- nextInput = inputs[index + 1];
+ if (!nextInput) {
+ editor.core.log('We are reached the end');
+ return;
+ }
- 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) {
+ if (!nextInput.childNodes.length) {
+ var emptyTextElement = document.createTextNode('');
- var emptyTextElement = document.createTextNode('');
+ nextInput.appendChild(emptyTextElement);
+ }
- nextInput.appendChild(emptyTextElement);
+ editor.caret.inputIndex = index + 1;
+ editor.caret.set(nextInput, 0, 0);
+ editor.content.workingNodeChanged(nextInput);
+ };
- }
-
- 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) {
+ caret.setToBlock = function (index) {
+ var inputs = editor.state.inputs,
+ targetInput = inputs[index];
- var inputs = editor.state.inputs,
- targetInput = inputs[index];
+ if ( !targetInput ) {
+ return;
+ }
- 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) {
+ if (!targetInput.childNodes.length) {
+ var emptyTextElement = document.createTextNode('');
- var emptyTextElement = document.createTextNode('');
+ targetInput.appendChild(emptyTextElement);
+ }
- targetInput.appendChild(emptyTextElement);
+ editor.caret.inputIndex = index;
+ editor.caret.set(targetInput, 0, 0);
+ editor.content.workingNodeChanged(targetInput);
+ };
- }
-
- editor.caret.inputIndex = index;
- editor.caret.set(targetInput, 0, 0);
- editor.content.workingNodeChanged(targetInput);
-
- };
-
- /**
+ /**
* @param {int} index - index of input
*/
- caret.setToPreviousBlock = function (index) {
+ caret.setToPreviousBlock = function (index) {
+ index = index || 0;
- index = index || 0;
-
- var inputs = editor.state.inputs,
- previousInput = inputs[index - 1],
- lastChildNode,
- lengthOfLastChildNode,
- emptyTextElement;
+ var inputs = editor.state.inputs,
+ previousInput = inputs[index - 1],
+ lastChildNode,
+ lengthOfLastChildNode,
+ emptyTextElement;
- if (!previousInput) {
+ if (!previousInput) {
+ editor.core.log('We are reached first node');
+ return;
+ }
- editor.core.log('We are reached first node');
- return;
+ lastChildNode = editor.content.getDeepestTextNodeFromPosition(previousInput, previousInput.childNodes.length);
+ lengthOfLastChildNode = lastChildNode.length;
- }
-
- 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) {
+ 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]);
+ };
- emptyTextElement = document.createTextNode('');
- previousInput.appendChild(emptyTextElement);
+ caret.position = {
- }
- editor.caret.inputIndex = index - 1;
- editor.caret.set(previousInput, previousInput.childNodes.length - 1, lengthOfLastChildNode);
- editor.content.workingNodeChanged(inputs[index - 1]);
+ atStart : function () {
+ 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;
+ }
- caret.position = {
+ var isFirstNode = anchorNode === pluginsRender.childNodes[0],
+ isOffsetZero = anchorOffset === 0;
- atStart : function () {
+ return isFirstNode && isOffsetZero;
+ },
- var selection = window.getSelection(),
- anchorOffset = selection.anchorOffset,
- anchorNode = selection.anchorNode,
- firstLevelBlock = editor.content.getFirstLevelBlock(anchorNode),
- pluginsRender = firstLevelBlock.childNodes[0];
+ atTheEnd : function () {
+ var selection = window.getSelection(),
+ anchorOffset = selection.anchorOffset,
+ anchorNode = selection.anchorNode;
- if (!editor.core.isDomNode(anchorNode)) {
-
- anchorNode = anchorNode.parentNode;
-
- }
-
- var isFirstNode = anchorNode === pluginsRender.childNodes[0],
- isOffsetZero = anchorOffset === 0;
-
- return isFirstNode && isOffsetZero;
-
- },
-
- atTheEnd : function () {
-
- var selection = window.getSelection(),
- anchorOffset = selection.anchorOffset,
- anchorNode = selection.anchorNode;
-
- /** Caret is at the end of input */
- return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;
-
- }
- };
+ /** Caret is at the end of input */
+ return !anchorNode || !anchorNode.length || anchorOffset === anchorNode.length;
+ }
+ };
- /**
+ /**
* Inserts node at the caret location
* @param {HTMLElement|DocumentFragment} node
*/
- caret.insertNode = function (node) {
+ caret.insertNode = function (node) {
+ var selection, range,
+ lastNode = node;
- var selection, range,
- lastNode = node;
+ if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) {
+ lastNode = node.lastChild;
+ }
- if (node.nodeType == editor.core.nodeTypes.DOCUMENT_FRAGMENT) {
+ selection = window.getSelection();
- lastNode = node.lastChild;
+ range = selection.getRangeAt(0);
+ range.deleteContents();
- }
+ range.insertNode(node);
- selection = window.getSelection();
+ range.setStartAfter(lastNode);
+ range.collapse(true);
- range = selection.getRangeAt(0);
- range.deleteContents();
-
- range.insertNode(node);
-
- range.setStartAfter(lastNode);
- range.collapse(true);
-
- selection.removeAllRanges();
- selection.addRange(range);
-
-
- };
-
- return caret;
+ selection.removeAllRanges();
+ selection.addRange(range);
+ };
+ return caret;
})({});
\ No newline at end of file
diff --git a/src/components/modules/_content.js b/src/components/modules/_content.js
index 6623ac8f..10ff5eb2 100644
--- a/src/components/modules/_content.js
+++ b/src/components/modules/_content.js
@@ -12,102 +12,87 @@
import $ from '../dom';
module.exports = class Content {
-
- /**
+ /**
* Module key name
* @returns {string}
*/
- static get name() {
+ static get name() {
+ return 'Content';
+ }
- return 'Content';
-
- }
-
- /**
+ /**
* @constructor
*
* @param {EditorConfig} config
*/
- constructor(config) {
+ constructor(config) {
+ this.config = config;
+ this.Editor = null;
- this.config = config;
- this.Editor = null;
+ this.CSS = {
+ block: 'ce-block',
+ content: 'ce-block__content',
+ stretched: 'ce-block--stretched',
+ highlighted: 'ce-block--highlighted',
+ };
- this.CSS = {
- block: 'ce-block',
- content: 'ce-block__content',
- stretched: 'ce-block--stretched',
- highlighted: 'ce-block--highlighted',
- };
+ this._currentNode = null;
+ this._currentIndex = 0;
+ }
- this._currentNode = null;
- this._currentIndex = 0;
-
- }
-
- /**
+ /**
* Editor modules setter
* @param {object} Editor
*/
- set state(Editor) {
+ set state(Editor) {
+ this.Editor = Editor;
+ }
- this.Editor = Editor;
-
- }
-
- /**
+ /**
* Get current working node
*
* @returns {null|HTMLElement}
*/
- get currentNode() {
+ get currentNode() {
+ return this._currentNode;
+ }
- return this._currentNode;
-
- }
-
- /**
+ /**
* Set working node. Working node should be first level block, so we find it before set one to _currentNode property
*
* @param {HTMLElement} node
*/
- set currentNode(node) {
+ set currentNode(node) {
+ let firstLevelBlock = this.getFirstLevelBlock(node);
- let firstLevelBlock = this.getFirstLevelBlock(node);
-
- this._currentNode = firstLevelBlock;
-
- }
+ this._currentNode = firstLevelBlock;
+ }
- /**
+ /**
* @private
* @param pluginHTML
* @param {Boolean} isStretched - make stretched block or not
*
* @description adds necessary information to wrap new created block by first-level holder
*/
- composeBlock_(pluginHTML, isStretched = false) {
+ composeBlock_(pluginHTML, isStretched = false) {
+ let block = $.make('DIV', this.CSS.block),
+ blockContent = $.make('DIV', this.CSS.content);
- let block = $.make('DIV', this.CSS.block),
- blockContent = $.make('DIV', this.CSS.content);
+ blockContent.appendChild(pluginHTML);
+ block.appendChild(blockContent);
- blockContent.appendChild(pluginHTML);
- block.appendChild(blockContent);
+ if (isStretched) {
+ blockContent.classList.add(this.CSS.stretched);
+ }
- if (isStretched) {
+ block.dataset.toolId = this._currentIndex++;
- blockContent.classList.add(this.CSS.stretched);
+ return block;
+ };
- }
-
- block.dataset.toolId = this._currentIndex++;
-
- return block;
-
- };
-
- /**
+ /**
* Finds first-level block
* @description looks for first-level block.
* gets parent while node is not first-level
@@ -116,33 +101,23 @@ module.exports = class Content {
* @protected
*
*/
- getFirstLevelBlock(node) {
+ getFirstLevelBlock(node) {
+ if (!$.isElement(node)) {
+ node = node.parentNode;
+ }
- if (!$.isElement(node)) {
+ if (node === this.Editor.ui.nodes.redactor || node === document.body) {
+ return null;
+ } else {
+ while(node.classList && !node.classList.contains(this.CSS.block)) {
+ node = node.parentNode;
+ }
- node = node.parentNode;
+ return node;
+ }
+ };
- }
-
- if (node === this.Editor.ui.nodes.redactor || node === document.body) {
-
- return null;
-
- } else {
-
- while(node.classList && !node.classList.contains(this.CSS.block)) {
-
- node = node.parentNode;
-
- }
-
- return node;
-
- }
-
- };
-
- /**
+ /**
* Insert new block to working area
*
* @param {HTMLElement} tool
@@ -150,32 +125,25 @@ module.exports = class Content {
* @returns {Number} tool index
*
*/
- insertBlock(tool) {
+ insertBlock(tool) {
+ let newBlock = this.composeBlock_(tool);
- let newBlock = this.composeBlock_(tool);
-
- if (this.currentNode) {
-
- this.currentNode.insertAdjacentElement('afterend', newBlock);
-
- } else {
-
- /**
+ if (this.currentNode) {
+ this.currentNode.insertAdjacentElement('afterend', newBlock);
+ } else {
+ /**
* If redactor is empty, append as first child
*/
- this.Editor.ui.nodes.redactor.appendChild(newBlock);
-
- }
-
- /**
- * Set new node as current
- */
- this.currentNode = newBlock;
-
- return newBlock.dataset.toolId;
-
+ this.Editor.ui.nodes.redactor.appendChild(newBlock);
}
+ /**
+ * Set new node as current
+ */
+ this.currentNode = newBlock;
+
+ return newBlock.dataset.toolId;
+ }
};
// module.exports = (function (content) {
diff --git a/src/components/modules/_destroyer.js b/src/components/modules/_destroyer.js
index 989f9478..c168f3fb 100644
--- a/src/components/modules/_destroyer.js
+++ b/src/components/modules/_destroyer.js
@@ -6,49 +6,34 @@
*/
module.exports = function (destroyer) {
+ let editor = codex.editor;
- let editor = codex.editor;
+ destroyer.removeNodes = function () {
+ editor.nodes.wrapper.remove();
+ editor.nodes.notifications.remove();
+ };
- destroyer.removeNodes = function () {
+ destroyer.destroyPlugins = function () {
+ for (var tool in editor.tools) {
+ if (typeof editor.tools[tool].destroy === 'function') {
+ editor.tools[tool].destroy();
+ }
+ }
+ };
- editor.nodes.wrapper.remove();
- editor.nodes.notifications.remove();
+ destroyer.destroyScripts = function () {
+ var scripts = document.getElementsByTagName('SCRIPT');
- };
-
- destroyer.destroyPlugins = function () {
-
- for (var tool in editor.tools) {
-
- if (typeof editor.tools[tool].destroy === 'function') {
-
- editor.tools[tool].destroy();
-
- }
-
- }
-
- };
-
- destroyer.destroyScripts = function () {
-
- var scripts = document.getElementsByTagName('SCRIPT');
-
- for (var i = 0; i < scripts.length; i++) {
-
- if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) {
-
- scripts[i].remove();
- i--;
-
- }
-
- }
-
- };
+ for (var i = 0; i < scripts.length; i++) {
+ if (scripts[i].id.indexOf(editor.scriptPrefix) + 1) {
+ scripts[i].remove();
+ i--;
+ }
+ }
+ };
- /**
+ /**
* Delete editor data from webpage.
* You should send settings argument with boolean flags:
* @param settings.ui- remove redactor event listeners and DOM nodes
@@ -58,41 +43,28 @@ module.exports = function (destroyer) {
* }
*
*/
- destroyer.destroy = function (settings) {
+ destroyer.destroy = function (settings) {
+ if (!settings || typeof settings !== 'object') {
+ return;
+ }
- if (!settings || typeof settings !== 'object') {
+ if (settings.ui) {
+ destroyer.removeNodes();
+ editor.listeners.removeAll();
+ }
- return;
+ if (settings.scripts) {
+ destroyer.destroyScripts();
+ }
- }
+ if (settings.plugins) {
+ destroyer.destroyPlugins();
+ }
- if (settings.ui) {
-
- destroyer.removeNodes();
- editor.listeners.removeAll();
-
- }
-
- if (settings.scripts) {
-
- destroyer.destroyScripts();
-
- }
-
- if (settings.plugins) {
-
- destroyer.destroyPlugins();
-
- }
-
- if (settings.ui && settings.scripts && settings.core) {
-
- delete codex.editor;
-
- }
-
- };
-
- return destroyer;
+ if (settings.ui && settings.scripts && settings.core) {
+ delete codex.editor;
+ }
+ };
+ return destroyer;
}({});
\ No newline at end of file
diff --git a/src/components/modules/_notifications.js b/src/components/modules/_notifications.js
index ffeda9b8..1746ae6f 100644
--- a/src/components/modules/_notifications.js
+++ b/src/components/modules/_notifications.js
@@ -6,55 +6,44 @@
*/
module.exports = (function (notifications) {
+ let editor = codex.editor;
- let editor = codex.editor;
+ var queue = [];
- var queue = [];
+ var addToQueue = function (settings) {
+ queue.push(settings);
- var addToQueue = function (settings) {
+ var index = 0;
- queue.push(settings);
+ while ( index < queue.length && queue.length > 5) {
+ if (queue[index].type == 'confirm' || queue[index].type == 'prompt') {
+ index++;
+ continue;
+ }
- var index = 0;
+ queue[index].close();
+ queue.splice(index, 1);
+ }
+ };
- while ( index < queue.length && queue.length > 5) {
+ notifications.createHolder = function () {
+ var holder = editor.draw.node('DIV', 'cdx-notifications-block');
- if (queue[index].type == 'confirm' || queue[index].type == 'prompt') {
+ editor.nodes.notifications = document.body.appendChild(holder);
- index++;
- continue;
-
- }
-
- queue[index].close();
- queue.splice(index, 1);
-
- }
-
- };
-
- notifications.createHolder = function () {
-
- var holder = editor.draw.node('DIV', 'cdx-notifications-block');
-
- editor.nodes.notifications = document.body.appendChild(holder);
-
- return holder;
-
- };
+ return holder;
+ };
- /**
+ /**
* Error notificator. Shows block with message
* @protected
*/
- notifications.errorThrown = function (errorMsg, event) {
+ notifications.errorThrown = function (errorMsg, event) {
+ editor.notifications.notification({message: 'This action is not available currently', type: event.type});
+ };
- editor.notifications.notification({message: 'This action is not available currently', type: event.type});
-
- };
-
- /**
+ /**
*
* Appends notification
*
@@ -70,162 +59,129 @@ module.exports = (function (notifications) {
*
* @param settings
*/
- notifications.notification = function (constructorSettings) {
+ notifications.notification = function (constructorSettings) {
+ /** Private vars and methods */
+ var notification = null,
+ cancel = null,
+ type = null,
+ confirm = null,
+ inputField = null;
- /** Private vars and methods */
- var notification = null,
- cancel = null,
- type = null,
- confirm = null,
- inputField = null;
+ var confirmHandler = function () {
+ close();
- var confirmHandler = function () {
+ if (typeof confirm !== 'function' ) {
+ return;
+ }
- close();
+ if (type == 'prompt') {
+ confirm(inputField.value);
+ return;
+ }
- if (typeof confirm !== 'function' ) {
+ confirm();
+ };
- return;
+ var cancelHandler = function () {
+ close();
- }
+ if (typeof cancel !== 'function' ) {
+ return;
+ }
- if (type == 'prompt') {
-
- confirm(inputField.value);
- return;
-
- }
-
- confirm();
-
- };
-
- var cancelHandler = function () {
-
- close();
-
- if (typeof cancel !== 'function' ) {
-
- return;
-
- }
-
- cancel();
-
- };
+ cancel();
+ };
- /** Public methods */
- function create(settings) {
+ /** Public methods */
+ function create(settings) {
+ if (!(settings && settings.message)) {
+ editor.core.log('Can\'t create notification. Message is missed');
+ return;
+ }
- if (!(settings && settings.message)) {
+ settings.type = settings.type || 'alert';
+ settings.time = settings.time*1000 || 10000;
- editor.core.log('Can\'t create notification. Message is missed');
- return;
+ var wrapper = editor.draw.node('DIV', 'cdx-notification'),
+ message = editor.draw.node('DIV', 'cdx-notification__message'),
+ input = editor.draw.node('INPUT', 'cdx-notification__input'),
+ okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'),
+ cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn');
- }
+ message.textContent = settings.message;
+ okBtn.textContent = settings.okMsg || 'ОК';
+ cancelBtn.textContent = settings.cancelMsg || 'Отмена';
- settings.type = settings.type || 'alert';
- settings.time = settings.time*1000 || 10000;
+ editor.listeners.add(okBtn, 'click', confirmHandler);
+ editor.listeners.add(cancelBtn, 'click', cancelHandler);
- var wrapper = editor.draw.node('DIV', 'cdx-notification'),
- message = editor.draw.node('DIV', 'cdx-notification__message'),
- input = editor.draw.node('INPUT', 'cdx-notification__input'),
- okBtn = editor.draw.node('SPAN', 'cdx-notification__ok-btn'),
- cancelBtn = editor.draw.node('SPAN', 'cdx-notification__cancel-btn');
+ wrapper.appendChild(message);
- message.textContent = settings.message;
- okBtn.textContent = settings.okMsg || 'ОК';
- cancelBtn.textContent = settings.cancelMsg || 'Отмена';
+ if (settings.type == 'prompt') {
+ wrapper.appendChild(input);
+ }
- editor.listeners.add(okBtn, 'click', confirmHandler);
- editor.listeners.add(cancelBtn, 'click', cancelHandler);
+ wrapper.appendChild(okBtn);
- wrapper.appendChild(message);
+ if (settings.type == 'prompt' || settings.type == 'confirm') {
+ wrapper.appendChild(cancelBtn);
+ }
- if (settings.type == 'prompt') {
+ wrapper.classList.add('cdx-notification-' + settings.type);
+ wrapper.dataset.type = settings.type;
- wrapper.appendChild(input);
+ notification = wrapper;
+ type = settings.type;
+ confirm = settings.confirm;
+ cancel = settings.cancel;
+ inputField = input;
- }
+ if (settings.type != 'prompt' && settings.type != 'confirm') {
+ window.setTimeout(close, settings.time);
+ }
+ };
- wrapper.appendChild(okBtn);
-
- if (settings.type == 'prompt' || settings.type == 'confirm') {
-
- wrapper.appendChild(cancelBtn);
-
- }
-
- wrapper.classList.add('cdx-notification-' + settings.type);
- wrapper.dataset.type = settings.type;
-
- notification = wrapper;
- type = settings.type;
- confirm = settings.confirm;
- cancel = settings.cancel;
- inputField = input;
-
- if (settings.type != 'prompt' && settings.type != 'confirm') {
-
- window.setTimeout(close, settings.time);
-
- }
-
- };
-
- /**
+ /**
* Show notification block
*/
- function send() {
+ function send() {
+ editor.nodes.notifications.appendChild(notification);
+ inputField.focus();
- editor.nodes.notifications.appendChild(notification);
- inputField.focus();
+ editor.nodes.notifications.classList.add('cdx-notification__notification-appending');
- editor.nodes.notifications.classList.add('cdx-notification__notification-appending');
+ window.setTimeout(function () {
+ editor.nodes.notifications.classList.remove('cdx-notification__notification-appending');
+ }, 100);
- window.setTimeout(function () {
+ addToQueue({type: type, close: close});
+ };
- editor.nodes.notifications.classList.remove('cdx-notification__notification-appending');
-
- }, 100);
-
- addToQueue({type: type, close: close});
-
- };
-
- /**
+ /**
* Remove notification block
*/
- function close() {
-
- notification.remove();
-
- };
-
-
- if (constructorSettings) {
-
- create(constructorSettings);
- send();
-
- }
-
- return {
- create: create,
- send: send,
- close: close
- };
-
+ function close() {
+ notification.remove();
};
- notifications.clear = function () {
- editor.nodes.notifications.innerHTML = '';
- queue = [];
+ if (constructorSettings) {
+ create(constructorSettings);
+ send();
+ }
+ return {
+ create: create,
+ send: send,
+ close: close
};
+ };
- return notifications;
+ notifications.clear = function () {
+ editor.nodes.notifications.innerHTML = '';
+ queue = [];
+ };
+ return notifications;
})({});
\ No newline at end of file
diff --git a/src/components/modules/_parser.js b/src/components/modules/_parser.js
index 70debc9c..6eefb459 100644
--- a/src/components/modules/_parser.js
+++ b/src/components/modules/_parser.js
@@ -6,31 +6,25 @@
*/
module.exports = (function (parser) {
+ let editor = codex.editor;
- let editor = codex.editor;
+ /** inserting text */
+ parser.insertPastedContent = function (blockType, tag) {
+ editor.content.insertBlock({
+ type : blockType.type,
+ block : blockType.render({
+ text : tag.innerHTML
+ })
+ });
+ };
- /** 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 &&
+ parser.isFirstLevelBlock = function (node) {
+ return node.nodeType == editor.core.nodeTypes.TAG &&
node.classList.contains(editor.ui.className.BLOCK_CLASSNAME);
+ };
- };
-
- return parser;
-
+ return parser;
})({});
diff --git a/src/components/modules/_paste.js b/src/components/modules/_paste.js
index ae810185..61fc63de 100644
--- a/src/components/modules/_paste.js
+++ b/src/components/modules/_paste.js
@@ -6,106 +6,82 @@
*/
module.exports = function (paste) {
+ let editor = codex.editor;
- let editor = codex.editor;
+ var patterns = [];
- var patterns = [];
+ paste.prepare = function () {
+ var tools = editor.tools;
- paste.prepare = function () {
+ for (var tool in tools) {
+ if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) {
+ continue;
+ }
- var tools = editor.tools;
+ tools[tool].renderOnPastePatterns.map(function (pattern) {
+ patterns.push(pattern);
+ });
+ }
- for (var tool in tools) {
+ return Promise.resolve();
+ };
- if (!tools[tool].renderOnPastePatterns || !Array.isArray(tools[tool].renderOnPastePatterns)) {
-
- continue;
-
- }
-
- tools[tool].renderOnPastePatterns.map(function (pattern) {
-
-
- patterns.push(pattern);
-
- });
-
- }
-
- return Promise.resolve();
-
- };
-
- /**
+ /**
* Saves data
* @param event
*/
- paste.pasted = function (event) {
+ paste.pasted = function (event) {
+ var clipBoardData = event.clipboardData || window.clipboardData,
+ content = clipBoardData.getData('Text');
- var clipBoardData = event.clipboardData || window.clipboardData,
- content = clipBoardData.getData('Text');
+ var result = analize(content);
- var result = analize(content);
+ if (result) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ }
- if (result) {
+ return result;
+ };
- event.preventDefault();
- event.stopImmediatePropagation();
-
- }
-
- return result;
-
- };
-
- /**
+ /**
* Analizes pated string and calls necessary method
*/
- var analize = function (string) {
+ var analize = function (string) {
+ var result = false,
+ content = editor.content.currentNode,
+ plugin = content.dataset.tool;
- var result = false,
- content = editor.content.currentNode,
- plugin = content.dataset.tool;
+ patterns.map( function (pattern) {
+ var execArray = pattern.regex.exec(string),
+ match = execArray && execArray[0];
- patterns.map( function (pattern) {
+ if ( match && match === string.trim()) {
+ /** current block is not empty */
+ if ( content.textContent.trim() && plugin == editor.settings.initialBlockPlugin ) {
+ pasteToNewBlock_();
+ }
- var execArray = pattern.regex.exec(string),
- match = execArray && execArray[0];
+ pattern.callback(string, pattern);
+ result = true;
+ }
+ });
- if ( match && match === string.trim()) {
+ return result;
+ };
- /** current block is not empty */
- if ( content.textContent.trim() && plugin == editor.settings.initialBlockPlugin ) {
+ var pasteToNewBlock_ = function () {
+ /** Create new initial block */
+ editor.content.insertBlock({
- pasteToNewBlock_();
+ type : editor.settings.initialBlockPlugin,
+ block : editor.tools[editor.settings.initialBlockPlugin].render({
+ text : ''
+ })
- }
-
- pattern.callback(string, pattern);
- result = true;
-
- }
-
- });
-
- return result;
-
- };
-
- var pasteToNewBlock_ = function () {
-
- /** Create new initial block */
- editor.content.insertBlock({
-
- type : editor.settings.initialBlockPlugin,
- block : editor.tools[editor.settings.initialBlockPlugin].render({
- text : ''
- })
-
- }, false);
-
- };
+ }, false);
+ };
/**
* This method prevents default behaviour.
@@ -117,162 +93,129 @@ module.exports = function (paste) {
* 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
*/
- paste.blockPasteCallback = function (event) {
+ paste.blockPasteCallback = function (event) {
+ if (!needsToHandlePasteEvent(event.target)) {
+ return;
+ }
+ /** Prevent default behaviour */
+ event.preventDefault();
- if (!needsToHandlePasteEvent(event.target)) {
+ /** get html pasted data - dirty data */
+ var htmlData = event.clipboardData.getData('text/html'),
+ plainData = event.clipboardData.getData('text/plain');
- return;
+ /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/
+ var paragraphs = editor.draw.node('DIV', '', {}),
+ cleanData,
+ wrappedData;
- }
+ /** Create fragment, that we paste to range after proccesing */
+ cleanData = editor.sanitizer.clean(htmlData);
- /** Prevent default behaviour */
- event.preventDefault();
-
- /** get html pasted data - dirty data */
- var htmlData = event.clipboardData.getData('text/html'),
- plainData = event.clipboardData.getData('text/plain');
-
- /** Temporary DIV that is used to work with text's paragraphs as DOM-elements*/
- var paragraphs = editor.draw.node('DIV', '', {}),
- cleanData,
- wrappedData;
-
- /** Create fragment, that we paste to range after proccesing */
- cleanData = editor.sanitizer.clean(htmlData);
-
- /**
+ /**
* We wrap pasted text with tags to split it logically
* @type {string}
*/
- wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData);
- paragraphs.innerHTML = wrappedData;
-
- /**
- * If there only one paragraph, just insert in at the caret location
- */
- if (paragraphs.childNodes.length == 1) {
-
- emulateUserAgentBehaviour(paragraphs.firstChild);
- return;
-
- }
-
- insertPastedParagraphs(paragraphs.childNodes);
-
- };
+ wrappedData = editor.content.wrapTextWithParagraphs(cleanData, plainData);
+ paragraphs.innerHTML = wrappedData;
/**
+ * If there only one paragraph, just insert in at the caret location
+ */
+ if (paragraphs.childNodes.length == 1) {
+ emulateUserAgentBehaviour(paragraphs.firstChild);
+ return;
+ }
+
+ insertPastedParagraphs(paragraphs.childNodes);
+ };
+
+ /**
* Checks if we should handle paste event on block
* @param block
*
* @return {boolean}
*/
- var needsToHandlePasteEvent = function (block) {
+ var needsToHandlePasteEvent = function (block) {
+ /** If area is input or textarea then allow default behaviour */
+ if ( editor.core.isNativeInput(block) ) {
+ return false;
+ }
- /** If area is input or textarea then allow default behaviour */
- if ( editor.core.isNativeInput(block) ) {
+ var editableParent = editor.content.getEditableParent(block);
- return false;
+ /** Allow paste when event target placed in Editable element */
+ if (!editableParent) {
+ return false;
+ }
- }
-
- var editableParent = editor.content.getEditableParent(block);
-
- /** Allow paste when event target placed in Editable element */
- if (!editableParent) {
-
- return false;
-
- }
-
- return true;
-
- };
+ return true;
+ };
/**
* Inserts new initial plugin blocks with data in paragraphs
*
* @param {Array} paragraphs - array of paragraphs ( where line breaks should be handled by default behaviour.\n */\n if (toolsConfig && toolsConfig[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) {\n return;\n }\n /**\n * Allow to create linebreaks by Shift+Enter\n */\n if (event.shiftKey) {\n return;\n }\n /**\n * Split the Current Block into two blocks\n */\n this.Editor.BlockManager.split();\n /**\n * Renew local current node after split\n */\n const newCurrent = this.Editor.BlockManager.currentBlock;\n this.Editor.Toolbar.move();\n this.Editor.Toolbar.open();\n if (this.Editor.Tools.isInitial(newCurrent.tool) && newCurrent.isEmpty) {\n this.Editor.Toolbar.plusButton.show();\n }\n event.preventDefault();\n }\n /**\n * Handle backspace keydown on Block\n * @param {KeyboardEvent} event - keydown\n */\n backspace(event) {\n const BM = this.Editor.BlockManager;\n const isFirstBlock = BM.currentBlockIndex === 0, canMergeBlocks = this.Editor.Caret.isAtStart && !isFirstBlock;\n if (!canMergeBlocks) {\n return;\n }\n // preventing browser default behaviour\n event.preventDefault();\n const targetBlock = BM.getBlockByIndex(BM.currentBlockIndex - 1), blockToMerge = BM.currentBlock;\n /**\n * Blocks that can be merged:\n * 1) with the same Name\n * 2) Tool has 'merge' method\n *\n * other case will handle as usual ARROW LEFT behaviour\n */\n if (blockToMerge.name !== targetBlock.name || !targetBlock.mergeable) {\n if (this.Editor.Caret.navigatePrevious()) {\n this.Editor.Toolbar.close();\n }\n }\n const setCaretToTheEnd = !targetBlock.isEmpty;\n BM.mergeBlocks(targetBlock, blockToMerge)\n .then(() => {\n // @todo figure out without timeout\n window.setTimeout(() => {\n // set caret to the block without offset at the end\n this.Editor.Caret.setToBlock(BM.currentBlock, 0, setCaretToTheEnd);\n this.Editor.Toolbar.close();\n }, 10);\n });\n }\n /**\n * Handle right and down keyboard keys\n */\n arrowRightAndDownPressed() {\n this.Editor.Caret.navigateNext();\n this.Editor.Toolbar.close();\n }\n /**\n * Handle left and up keyboard keys\n */\n arrowLeftAndUpPressed() {\n this.Editor.Caret.navigatePrevious();\n this.Editor.Toolbar.close();\n }\n}\n","/**\n * @class BlockManager\n * @classdesc Manage editor`s blocks storage and appearance\n *\n * @module BlockManager\n *\n * @version 2.0.0\n */\n\nimport Block from '../block';\n\n/**\n * @typedef {BlockManager} BlockManager\n * @property {Number} currentBlockIndex - Index of current working block\n * @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks}\n */\nexport default class BlockManager extends Module {\n /**\n * @constructor\n * @param {EditorConfig} config\n */\n constructor({config}) {\n super({config});\n\n /**\n * Proxy for Blocks instance {@link Blocks}\n *\n * @type {Proxy}\n * @private\n */\n this._blocks = null;\n\n /**\n * Index of current working block\n *\n * @type {number}\n * @private\n */\n this.currentBlockIndex = -1;\n }\n\n /**\n * Should be called after Editor.UI preparation\n * Define this._blocks property\n *\n * @returns {Promise}\n */\n prepare() {\n return new Promise(resolve => {\n let blocks = new Blocks(this.Editor.UI.nodes.redactor);\n\n /**\n * We need to use Proxy to overload set/get [] operator.\n * So we can use array-like syntax to access blocks\n *\n * @example\n * this._blocks[0] = new Block(...);\n *\n * block = this._blocks[0];\n *\n * @todo proxy the enumerate method\n *\n * @type {Proxy}\n * @private\n */\n this._blocks = new Proxy(blocks, {\n set: Blocks.set,\n get: Blocks.get\n });\n\n resolve();\n });\n }\n\n /**\n * Creates Block instance by tool name\n *\n * @param {String} toolName - tools passed in editor config {@link EditorConfig#tools}\n * @param {Object} data - constructor params\n * @param {Object} settings - block settings\n *\n * @return {Block}\n */\n composeBlock(toolName, data, settings) {\n let toolInstance = this.Editor.Tools.construct(toolName, data),\n block = new Block(toolName, toolInstance, settings, this.Editor.API.methods);\n\n this.bindEvents(block);\n /**\n * Apply callback before inserting html\n */\n block.call('appendCallback', {});\n\n return block;\n }\n\n /**\n * Bind Events\n * @param {Object} block\n */\n bindEvents(block) {\n this.Editor.Listeners.on(block.pluginsContent, 'keydown', (event) => this.Editor.BlockEvents.keydown(event));\n this.Editor.Listeners.on(block.pluginsContent, 'mouseup', (event) => this.Editor.BlockEvents.mouseUp(event));\n this.Editor.Listeners.on(block.pluginsContent, 'keyup', (event) => this.Editor.BlockEvents.keyup(event));\n }\n\n /**\n * Insert new block into _blocks\n *\n * @param {String} toolName — plugin name, by default method inserts initial block type\n * @param {Object} data — plugin data\n * @param {Object} settings - default settings\n *\n * @return {Block}\n */\n insert(toolName = this.config.initialBlock, data = {}, settings = {}) {\n let block = this.composeBlock(toolName, data, settings);\n\n this._blocks[++this.currentBlockIndex] = block;\n this.Editor.Caret.setToBlock(block);\n\n return block;\n }\n\n /**\n * Merge two blocks\n * @param {Block} targetBlock - previous block will be append to this block\n * @param {Block} blockToMerge - block that will be merged with target block\n *\n * @return {Promise} - the sequence that can be continued\n */\n mergeBlocks(targetBlock, blockToMerge) {\n let blockToMergeIndex = this._blocks.indexOf(blockToMerge);\n\n return Promise.resolve()\n .then( () => {\n if (blockToMerge.isEmpty) {\n return;\n }\n\n return blockToMerge.data\n .then((blockToMergeInfo) => {\n targetBlock.mergeWith(blockToMergeInfo.data);\n });\n })\n .then( () => {\n this.removeBlock(blockToMergeIndex);\n this.currentBlockIndex = this._blocks.indexOf(targetBlock);\n });\n }\n\n /**\n * Remove block with passed index or remove last\n * @param {Number|null} index\n */\n removeBlock(index) {\n if (!index) {\n index = this.currentBlockIndex;\n }\n this._blocks.remove(index);\n }\n\n /**\n * Split current Block\n * 1. Extract content from Caret position to the Block`s end\n * 2. Insert a new Block below current one with extracted content\n */\n split() {\n let extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(),\n wrapper = $.make('div');\n\n wrapper.append(extractedFragment);\n\n /**\n * @todo make object in accordance with Tool\n */\n let data = {\n text: $.isEmpty(wrapper) ? '' : wrapper.innerHTML,\n };\n\n /**\n * Renew current Block\n * @type {Block}\n */\n const blockInserted = this.insert(this.config.initialBlock, data);\n\n this.currentNode = blockInserted.pluginsContent;\n }\n\n /**\n * Replace current working block\n *\n * @param {String} toolName — plugin name\n * @param {Object} data — plugin data\n */\n replace(toolName, data = {}) {\n let block = this.composeBlock(toolName, data);\n\n this._blocks.insert(this.currentBlockIndex, block, true);\n }\n\n /**\n * returns last Block\n * @return {Block}\n */\n get lastBlock() {\n return this._blocks[this._blocks.length - 1];\n }\n\n /**\n * Returns Block by passed index\n * @param {Number} index\n * @return {Block}\n */\n getBlockByIndex(index) {\n return this._blocks[index];\n }\n\n /**\n * Get Block instance by html element\n * @param {Node} element\n * @returns {Block}\n */\n getBlock(element) {\n if (!$.isElement(element)) {\n element = element.parentNode;\n }\n\n let nodes = this._blocks.nodes,\n firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`),\n index = nodes.indexOf(firstLevelBlock);\n\n if (index >= 0) {\n return this._blocks[index];\n }\n }\n\n /**\n * Get current Block instance\n *\n * @return {Block}\n */\n get currentBlock() {\n return this._blocks[this.currentBlockIndex];\n }\n\n /**\n * Returns next Block instance\n * @return {Block|null}\n */\n get nextBlock() {\n let isLastBlock = this.currentBlockIndex === (this._blocks.length - 1);\n\n if (isLastBlock) {\n return null;\n }\n\n return this._blocks[this.currentBlockIndex + 1];\n }\n\n /**\n * Returns previous Block instance\n * @return {Block|null}\n */\n get previousBlock() {\n let isFirstBlock = this.currentBlockIndex === 0;\n\n if (isFirstBlock) {\n return null;\n }\n\n return this._blocks[this.currentBlockIndex - 1];\n }\n\n /**\n * Get working html element\n *\n * @return {HTMLElement}\n */\n get currentNode() {\n return this._blocks.nodes[this.currentBlockIndex];\n }\n\n /**\n * Set currentBlockIndex to passed block\n * @param {HTMLElement} element\n */\n set currentNode(element) {\n let nodes = this._blocks.nodes,\n firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`);\n\n /**\n * Update current Block's index\n * @type {number}\n */\n this.currentBlockIndex = nodes.indexOf(firstLevelBlock);\n\n /**\n * Remove previous selected Block's state\n */\n this.blocks.forEach( block => block.selected = false);\n\n /**\n * Mark current Block as selected\n * @type {boolean}\n */\n this.currentBlock.selected = true;\n }\n\n /**\n * Get array of Block instances\n *\n * @returns {Block[]} {@link Blocks#array}\n */\n get blocks() {\n return this._blocks.array;\n }\n\n /**\n * 1) Find first-level Block from passed child Node\n * 2) Mark it as current\n *\n * @param {Element|Text} childNode - look ahead from this node.\n * @throws Error - when passed Node is not included at the Block\n */\n setCurrentBlockByChildNode(childNode) {\n /**\n * If node is Text TextNode\n */\n if (!$.isElement(childNode)) {\n childNode = childNode.parentNode;\n }\n\n let parentFirstLevelBlock = childNode.closest(`.${Block.CSS.wrapper}`);\n\n if (parentFirstLevelBlock) {\n this.currentNode = parentFirstLevelBlock;\n } else {\n throw new Error('Can not find a Block from this child Node');\n }\n }\n\n /**\n * Swap Blocks Position\n * @param {Number} fromIndex\n * @param {Number} toIndex\n */\n swap(fromIndex, toIndex) {\n /** Move up current Block */\n this._blocks.swap(fromIndex, toIndex);\n\n /** Now actual block moved up so that current block index decreased */\n this.currentBlockIndex = toIndex;\n }\n /**\n * Clears Editor\n * @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render)\n * we don't need to add empty initial block\n * 2) in api.blocks.clear we should add empty block\n */\n clear(needAddInitialBlock = false) {\n this._blocks.removeAll();\n this.currentBlockIndex = -1;\n\n if (needAddInitialBlock) {\n this.insert(this.config.initialBlock);\n }\n }\n};\n\n/**\n * @class Blocks\n * @classdesc Class to work with Block instances array\n *\n * @private\n *\n * @property {HTMLElement} workingArea — editor`s working node\n *\n */\nclass Blocks {\n /**\n * @constructor\n *\n * @param {HTMLElement} workingArea — editor`s working node\n */\n constructor(workingArea) {\n this.blocks = [];\n this.workingArea = workingArea;\n }\n\n /**\n * Push back new Block\n *\n * @param {Block} block\n */\n push(block) {\n this.blocks.push(block);\n this.workingArea.appendChild(block.html);\n }\n\n /**\n * Swaps blocks with indexes first and second\n * @param {Number} first - first block index\n * @param {Number} second - second block index\n */\n swap(first, second) {\n let secondBlock = this.blocks[second];\n\n /**\n * Change in DOM\n */\n $.swap(this.blocks[first].html, secondBlock.html);\n\n /**\n * Change in array\n */\n this.blocks[second] = this.blocks[first];\n this.blocks[first] = secondBlock;\n }\n\n /**\n * Insert new Block at passed index\n *\n * @param {Number} index — index to insert Block\n * @param {Block} block — Block to insert\n * @param {Boolean} replace — it true, replace block on given index\n */\n insert(index, block, replace = false) {\n if (!this.length) {\n this.push(block);\n return;\n }\n\n if (index > this.length) {\n index = this.length;\n }\n\n if (replace) {\n this.blocks[index].html.remove();\n }\n\n let deleteCount = replace ? 1 : 0;\n\n this.blocks.splice(index, deleteCount, block);\n\n if (index > 0) {\n let previousBlock = this.blocks[index - 1];\n\n previousBlock.html.insertAdjacentElement('afterend', block.html);\n } else {\n let nextBlock = this.blocks[index + 1];\n\n if (nextBlock) {\n nextBlock.html.insertAdjacentElement('beforebegin', block.html);\n } else {\n this.workingArea.appendChild(block.html);\n }\n }\n }\n\n /**\n * Remove block\n * @param {Number|null} index\n */\n remove(index) {\n if (isNaN(index)) {\n index = this.length - 1;\n }\n\n this.blocks[index].html.remove();\n this.blocks.splice(index, 1);\n }\n\n /**\n * Remove all blocks\n */\n removeAll() {\n this.workingArea.innerHTML = '';\n this.blocks.length = 0;\n }\n\n /**\n * Insert Block after passed target\n *\n * @todo decide if this method is necessary\n *\n * @param {Block} targetBlock — target after wich Block should be inserted\n * @param {Block} newBlock — Block to insert\n */\n insertAfter(targetBlock, newBlock) {\n let index = this.blocks.indexOf(targetBlock);\n\n this.insert(index + 1, newBlock);\n }\n\n /**\n * Get Block by index\n *\n * @param {Number} index — Block index\n * @returns {Block}\n */\n get(index) {\n return this.blocks[index];\n }\n\n /**\n * Return index of passed Block\n *\n * @param {Block} block\n * @returns {Number}\n */\n indexOf(block) {\n return this.blocks.indexOf(block);\n }\n\n /**\n * Get length of Block instances array\n *\n * @returns {Number}\n */\n get length() {\n return this.blocks.length;\n }\n\n /**\n * Get Block instances array\n *\n * @returns {Block[]}\n */\n get array() {\n return this.blocks;\n }\n\n /**\n * Get blocks html elements array\n *\n * @returns {HTMLElement[]}\n */\n get nodes() {\n return _.array(this.workingArea.children);\n }\n\n /**\n * Proxy trap to implement array-like setter\n *\n * @example\n * blocks[0] = new Block(...)\n *\n * @param {Blocks} instance — Blocks instance\n * @param {Number|String} index — block index\n * @param {Block} block — Block to set\n * @returns {Boolean}\n */\n static set(instance, index, block) {\n if (isNaN(Number(index))) {\n return false;\n }\n\n instance.insert(index, block);\n\n return true;\n }\n\n /**\n * Proxy trap to implement array-like getter\n *\n * @param {Blocks} instance — Blocks instance\n * @param {Number|String} index — Block index\n * @returns {Block|*}\n */\n static get(instance, index) {\n if (isNaN(Number(index))) {\n return instance[index];\n }\n\n return instance.get(index);\n }\n}\n","/**\n * @class Caret\n * @classdesc Contains methods for working Caret\n *\n * Uses Range methods to manipulate with caret\n *\n * @module Caret\n *\n * @version 2.0.0\n */\n\nimport Selection from '../selection';\n\n/**\n * @typedef {Caret} Caret\n */\nexport default class Caret extends Module {\n /**\n * @constructor\n */\n constructor({config}) {\n super({config});\n }\n\n /**\n * Method gets Block instance and puts caret to the text node with offset\n * There two ways that method applies caret position:\n * - first found text node: sets at the beginning, but you can pass an offset\n * - last found text node: sets at the end of the node. Also, you can customize the behaviour\n *\n * @param {Block} block - Block class\n * @param {Number} offset - caret offset regarding to the text node\n * @param {Boolean} atEnd - put caret at the end of the text node or not\n */\n setToBlock(block, offset = 0, atEnd = false) {\n let element = block.pluginsContent;\n\n /** If Element is INPUT */\n if ($.isNativeInput(element)) {\n element.focus();\n return;\n }\n\n let nodeToSet = $.getDeepestNode(element, atEnd);\n\n if (atEnd || offset > nodeToSet.length) {\n offset = nodeToSet.length;\n }\n\n /** if found deepest node is native input */\n if ($.isNativeInput(nodeToSet)) {\n nodeToSet.focus();\n return;\n }\n\n /**\n * @todo try to fix via Promises or use querySelectorAll to not to use timeout\n */\n _.delay( () => {\n this.set(nodeToSet, offset);\n }, 20)();\n\n this.Editor.BlockManager.currentNode = block.wrapper;\n }\n\n /**\n * Creates Document Range and sets caret to the element with offset\n * @param {Element} element - target node.\n * @param {Number} offset - offset\n */\n set( element, offset = 0) {\n let range = document.createRange(),\n selection = Selection.get();\n\n range.setStart(element, offset);\n range.setEnd(element, offset);\n\n selection.removeAllRanges();\n selection.addRange(range);\n };\n\n /**\n * Set Caret to the last Block\n * If last block is not empty, append another empty block\n */\n setToTheLastBlock() {\n let lastBlock = this.Editor.BlockManager.lastBlock;\n\n if (!lastBlock) return;\n\n /**\n * If last block is empty and it is an initialBlock, set to that.\n * Otherwise, append new empty block and set to that\n */\n if (lastBlock.isEmpty) {\n this.setToBlock(lastBlock);\n } else {\n this.Editor.BlockManager.insert(this.config.initialBlock);\n }\n }\n\n /**\n * Extract content fragment of current Block from Caret position to the end of the Block\n */\n extractFragmentFromCaretPosition() {\n let selection = Selection.get();\n\n if (selection.rangeCount) {\n let selectRange = selection.getRangeAt(0),\n blockElem = this.Editor.BlockManager.currentBlock.pluginsContent;\n\n selectRange.deleteContents();\n\n if (blockElem) {\n let range = selectRange.cloneRange(true);\n\n range.selectNodeContents(blockElem);\n range.setStart(selectRange.endContainer, selectRange.endOffset);\n return range.extractContents();\n }\n }\n }\n\n /**\n * Get all first-level (first child of [contenteditabel]) siblings from passed node\n * Then you can check it for emptiness\n *\n * @example\n * adaddad
<-- passed node for example \n * |\n * | right first-level siblings\n * |\n *
where line breaks should be handled by default behaviour.\r\n */\r\n if (toolsConfig && toolsConfig[this.Editor.Tools.apiSettings.IS_ENABLED_LINE_BREAKS]) {\r\n return;\r\n }\r\n /**\r\n * Allow to create linebreaks by Shift+Enter\r\n */\r\n if (event.shiftKey) {\r\n return;\r\n }\r\n /**\r\n * Split the Current Block into two blocks\r\n */\r\n this.Editor.BlockManager.split();\r\n /**\r\n * Renew local current node after split\r\n */\r\n const newCurrent = this.Editor.BlockManager.currentBlock;\r\n this.Editor.Toolbar.move();\r\n this.Editor.Toolbar.open();\r\n if (this.Editor.Tools.isInitial(newCurrent.tool) && newCurrent.isEmpty) {\r\n this.Editor.Toolbar.plusButton.show();\r\n }\r\n event.preventDefault();\r\n }\r\n /**\r\n * Handle backspace keydown on Block\r\n * @param {KeyboardEvent} event - keydown\r\n */\r\n backspace(event) {\r\n const BM = this.Editor.BlockManager;\r\n const isFirstBlock = BM.currentBlockIndex === 0, canMergeBlocks = this.Editor.Caret.isAtStart && !isFirstBlock;\r\n if (!canMergeBlocks) {\r\n return;\r\n }\r\n // preventing browser default behaviour\r\n event.preventDefault();\r\n const targetBlock = BM.getBlockByIndex(BM.currentBlockIndex - 1), blockToMerge = BM.currentBlock;\r\n /**\r\n * Blocks that can be merged:\r\n * 1) with the same Name\r\n * 2) Tool has 'merge' method\r\n *\r\n * other case will handle as usual ARROW LEFT behaviour\r\n */\r\n if (blockToMerge.name !== targetBlock.name || !targetBlock.mergeable) {\r\n if (this.Editor.Caret.navigatePrevious()) {\r\n this.Editor.Toolbar.close();\r\n }\r\n }\r\n const setCaretToTheEnd = !targetBlock.isEmpty;\r\n BM.mergeBlocks(targetBlock, blockToMerge)\r\n .then(() => {\r\n // @todo figure out without timeout\r\n window.setTimeout(() => {\r\n // set caret to the block without offset at the end\r\n this.Editor.Caret.setToBlock(BM.currentBlock, 0, setCaretToTheEnd);\r\n this.Editor.Toolbar.close();\r\n }, 10);\r\n });\r\n }\r\n /**\r\n * Handle right and down keyboard keys\r\n */\r\n arrowRightAndDownPressed() {\r\n this.Editor.Caret.navigateNext();\r\n this.Editor.Toolbar.close();\r\n }\r\n /**\r\n * Handle left and up keyboard keys\r\n */\r\n arrowLeftAndUpPressed() {\r\n this.Editor.Caret.navigatePrevious();\r\n this.Editor.Toolbar.close();\r\n }\r\n}\r\n","/**\r\n * @class BlockManager\r\n * @classdesc Manage editor`s blocks storage and appearance\r\n *\r\n * @module BlockManager\r\n *\r\n * @version 2.0.0\r\n */\r\n\r\nimport Block from '../block';\r\n\r\n/**\r\n * @typedef {BlockManager} BlockManager\r\n * @property {Number} currentBlockIndex - Index of current working block\r\n * @property {Proxy} _blocks - Proxy for Blocks instance {@link Blocks}\r\n */\r\nexport default class BlockManager extends Module {\r\n /**\r\n * @constructor\r\n * @param {EditorConfig} config\r\n */\r\n constructor({config}) {\r\n super({config});\r\n\r\n /**\r\n * Proxy for Blocks instance {@link Blocks}\r\n *\r\n * @type {Proxy}\r\n * @private\r\n */\r\n this._blocks = null;\r\n\r\n /**\r\n * Index of current working block\r\n *\r\n * @type {number}\r\n * @private\r\n */\r\n this.currentBlockIndex = -1;\r\n }\r\n\r\n /**\r\n * Should be called after Editor.UI preparation\r\n * Define this._blocks property\r\n *\r\n * @returns {Promise}\r\n */\r\n prepare() {\r\n return new Promise(resolve => {\r\n let blocks = new Blocks(this.Editor.UI.nodes.redactor);\r\n\r\n /**\r\n * We need to use Proxy to overload set/get [] operator.\r\n * So we can use array-like syntax to access blocks\r\n *\r\n * @example\r\n * this._blocks[0] = new Block(...);\r\n *\r\n * block = this._blocks[0];\r\n *\r\n * @todo proxy the enumerate method\r\n *\r\n * @type {Proxy}\r\n * @private\r\n */\r\n this._blocks = new Proxy(blocks, {\r\n set: Blocks.set,\r\n get: Blocks.get\r\n });\r\n\r\n resolve();\r\n });\r\n }\r\n\r\n /**\r\n * Creates Block instance by tool name\r\n *\r\n * @param {String} toolName - tools passed in editor config {@link EditorConfig#tools}\r\n * @param {Object} data - constructor params\r\n * @param {Object} settings - block settings\r\n *\r\n * @return {Block}\r\n */\r\n composeBlock(toolName, data, settings) {\r\n let toolInstance = this.Editor.Tools.construct(toolName, data),\r\n block = new Block(toolName, toolInstance, settings, this.Editor.API.methods);\r\n\r\n this.bindEvents(block);\r\n /**\r\n * Apply callback before inserting html\r\n */\r\n block.call('appendCallback', {});\r\n\r\n return block;\r\n }\r\n\r\n /**\r\n * Bind Events\r\n * @param {Object} block\r\n */\r\n bindEvents(block) {\r\n this.Editor.Listeners.on(block.pluginsContent, 'keydown', (event) => this.Editor.BlockEvents.keydown(event));\r\n this.Editor.Listeners.on(block.pluginsContent, 'mouseup', (event) => this.Editor.BlockEvents.mouseUp(event));\r\n this.Editor.Listeners.on(block.pluginsContent, 'keyup', (event) => this.Editor.BlockEvents.keyup(event));\r\n }\r\n\r\n /**\r\n * Insert new block into _blocks\r\n *\r\n * @param {String} toolName — plugin name, by default method inserts initial block type\r\n * @param {Object} data — plugin data\r\n * @param {Object} settings - default settings\r\n *\r\n * @return {Block}\r\n */\r\n insert(toolName = this.config.initialBlock, data = {}, settings = {}) {\r\n let block = this.composeBlock(toolName, data, settings);\r\n\r\n this._blocks[++this.currentBlockIndex] = block;\r\n this.Editor.Caret.setToBlock(block);\r\n\r\n return block;\r\n }\r\n\r\n /**\r\n * Merge two blocks\r\n * @param {Block} targetBlock - previous block will be append to this block\r\n * @param {Block} blockToMerge - block that will be merged with target block\r\n *\r\n * @return {Promise} - the sequence that can be continued\r\n */\r\n mergeBlocks(targetBlock, blockToMerge) {\r\n let blockToMergeIndex = this._blocks.indexOf(blockToMerge);\r\n\r\n return Promise.resolve()\r\n .then( () => {\r\n if (blockToMerge.isEmpty) {\r\n return;\r\n }\r\n\r\n return blockToMerge.data\r\n .then((blockToMergeInfo) => {\r\n targetBlock.mergeWith(blockToMergeInfo.data);\r\n });\r\n })\r\n .then( () => {\r\n this.removeBlock(blockToMergeIndex);\r\n this.currentBlockIndex = this._blocks.indexOf(targetBlock);\r\n });\r\n }\r\n\r\n /**\r\n * Remove block with passed index or remove last\r\n * @param {Number|null} index\r\n */\r\n removeBlock(index) {\r\n if (!index) {\r\n index = this.currentBlockIndex;\r\n }\r\n this._blocks.remove(index);\r\n }\r\n\r\n /**\r\n * Split current Block\r\n * 1. Extract content from Caret position to the Block`s end\r\n * 2. Insert a new Block below current one with extracted content\r\n */\r\n split() {\r\n let extractedFragment = this.Editor.Caret.extractFragmentFromCaretPosition(),\r\n wrapper = $.make('div');\r\n\r\n wrapper.append(extractedFragment);\r\n\r\n /**\r\n * @todo make object in accordance with Tool\r\n */\r\n let data = {\r\n text: $.isEmpty(wrapper) ? '' : wrapper.innerHTML,\r\n };\r\n\r\n /**\r\n * Renew current Block\r\n * @type {Block}\r\n */\r\n const blockInserted = this.insert(this.config.initialBlock, data);\r\n\r\n this.currentNode = blockInserted.pluginsContent;\r\n }\r\n\r\n /**\r\n * Replace current working block\r\n *\r\n * @param {String} toolName — plugin name\r\n * @param {Object} data — plugin data\r\n */\r\n replace(toolName, data = {}) {\r\n let block = this.composeBlock(toolName, data);\r\n\r\n this._blocks.insert(this.currentBlockIndex, block, true);\r\n }\r\n\r\n /**\r\n * returns last Block\r\n * @return {Block}\r\n */\r\n get lastBlock() {\r\n return this._blocks[this._blocks.length - 1];\r\n }\r\n\r\n /**\r\n * Returns Block by passed index\r\n * @param {Number} index\r\n * @return {Block}\r\n */\r\n getBlockByIndex(index) {\r\n return this._blocks[index];\r\n }\r\n\r\n /**\r\n * Get Block instance by html element\r\n * @param {Node} element\r\n * @returns {Block}\r\n */\r\n getBlock(element) {\r\n if (!$.isElement(element)) {\r\n element = element.parentNode;\r\n }\r\n\r\n let nodes = this._blocks.nodes,\r\n firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`),\r\n index = nodes.indexOf(firstLevelBlock);\r\n\r\n if (index >= 0) {\r\n return this._blocks[index];\r\n }\r\n }\r\n\r\n /**\r\n * Get current Block instance\r\n *\r\n * @return {Block}\r\n */\r\n get currentBlock() {\r\n return this._blocks[this.currentBlockIndex];\r\n }\r\n\r\n /**\r\n * Returns next Block instance\r\n * @return {Block|null}\r\n */\r\n get nextBlock() {\r\n let isLastBlock = this.currentBlockIndex === (this._blocks.length - 1);\r\n\r\n if (isLastBlock) {\r\n return null;\r\n }\r\n\r\n return this._blocks[this.currentBlockIndex + 1];\r\n }\r\n\r\n /**\r\n * Returns previous Block instance\r\n * @return {Block|null}\r\n */\r\n get previousBlock() {\r\n let isFirstBlock = this.currentBlockIndex === 0;\r\n\r\n if (isFirstBlock) {\r\n return null;\r\n }\r\n\r\n return this._blocks[this.currentBlockIndex - 1];\r\n }\r\n\r\n /**\r\n * Get working html element\r\n *\r\n * @return {HTMLElement}\r\n */\r\n get currentNode() {\r\n return this._blocks.nodes[this.currentBlockIndex];\r\n }\r\n\r\n /**\r\n * Set currentBlockIndex to passed block\r\n * @param {HTMLElement} element\r\n */\r\n set currentNode(element) {\r\n let nodes = this._blocks.nodes,\r\n firstLevelBlock = element.closest(`.${Block.CSS.wrapper}`);\r\n\r\n /**\r\n * Update current Block's index\r\n * @type {number}\r\n */\r\n this.currentBlockIndex = nodes.indexOf(firstLevelBlock);\r\n\r\n /**\r\n * Remove previous selected Block's state\r\n */\r\n this.blocks.forEach( block => block.selected = false);\r\n\r\n /**\r\n * Mark current Block as selected\r\n * @type {boolean}\r\n */\r\n this.currentBlock.selected = true;\r\n }\r\n\r\n /**\r\n * Get array of Block instances\r\n *\r\n * @returns {Block[]} {@link Blocks#array}\r\n */\r\n get blocks() {\r\n return this._blocks.array;\r\n }\r\n\r\n /**\r\n * 1) Find first-level Block from passed child Node\r\n * 2) Mark it as current\r\n *\r\n * @param {Element|Text} childNode - look ahead from this node.\r\n * @throws Error - when passed Node is not included at the Block\r\n */\r\n setCurrentBlockByChildNode(childNode) {\r\n /**\r\n * If node is Text TextNode\r\n */\r\n if (!$.isElement(childNode)) {\r\n childNode = childNode.parentNode;\r\n }\r\n\r\n let parentFirstLevelBlock = childNode.closest(`.${Block.CSS.wrapper}`);\r\n\r\n if (parentFirstLevelBlock) {\r\n this.currentNode = parentFirstLevelBlock;\r\n } else {\r\n throw new Error('Can not find a Block from this child Node');\r\n }\r\n }\r\n\r\n /**\r\n * Swap Blocks Position\r\n * @param {Number} fromIndex\r\n * @param {Number} toIndex\r\n */\r\n swap(fromIndex, toIndex) {\r\n /** Move up current Block */\r\n this._blocks.swap(fromIndex, toIndex);\r\n\r\n /** Now actual block moved up so that current block index decreased */\r\n this.currentBlockIndex = toIndex;\r\n }\r\n /**\r\n * Clears Editor\r\n * @param {boolean} needAddInitialBlock - 1) in internal calls (for example, in api.blocks.render)\r\n * we don't need to add empty initial block\r\n * 2) in api.blocks.clear we should add empty block\r\n */\r\n clear(needAddInitialBlock = false) {\r\n this._blocks.removeAll();\r\n this.currentBlockIndex = -1;\r\n\r\n if (needAddInitialBlock) {\r\n this.insert(this.config.initialBlock);\r\n }\r\n }\r\n};\r\n\r\n/**\r\n * @class Blocks\r\n * @classdesc Class to work with Block instances array\r\n *\r\n * @private\r\n *\r\n * @property {HTMLElement} workingArea — editor`s working node\r\n *\r\n */\r\nclass Blocks {\r\n /**\r\n * @constructor\r\n *\r\n * @param {HTMLElement} workingArea — editor`s working node\r\n */\r\n constructor(workingArea) {\r\n this.blocks = [];\r\n this.workingArea = workingArea;\r\n }\r\n\r\n /**\r\n * Push back new Block\r\n *\r\n * @param {Block} block\r\n */\r\n push(block) {\r\n this.blocks.push(block);\r\n this.workingArea.appendChild(block.html);\r\n }\r\n\r\n /**\r\n * Swaps blocks with indexes first and second\r\n * @param {Number} first - first block index\r\n * @param {Number} second - second block index\r\n */\r\n swap(first, second) {\r\n let secondBlock = this.blocks[second];\r\n\r\n /**\r\n * Change in DOM\r\n */\r\n $.swap(this.blocks[first].html, secondBlock.html);\r\n\r\n /**\r\n * Change in array\r\n */\r\n this.blocks[second] = this.blocks[first];\r\n this.blocks[first] = secondBlock;\r\n }\r\n\r\n /**\r\n * Insert new Block at passed index\r\n *\r\n * @param {Number} index — index to insert Block\r\n * @param {Block} block — Block to insert\r\n * @param {Boolean} replace — it true, replace block on given index\r\n */\r\n insert(index, block, replace = false) {\r\n if (!this.length) {\r\n this.push(block);\r\n return;\r\n }\r\n\r\n if (index > this.length) {\r\n index = this.length;\r\n }\r\n\r\n if (replace) {\r\n this.blocks[index].html.remove();\r\n }\r\n\r\n let deleteCount = replace ? 1 : 0;\r\n\r\n this.blocks.splice(index, deleteCount, block);\r\n\r\n if (index > 0) {\r\n let previousBlock = this.blocks[index - 1];\r\n\r\n previousBlock.html.insertAdjacentElement('afterend', block.html);\r\n } else {\r\n let nextBlock = this.blocks[index + 1];\r\n\r\n if (nextBlock) {\r\n nextBlock.html.insertAdjacentElement('beforebegin', block.html);\r\n } else {\r\n this.workingArea.appendChild(block.html);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Remove block\r\n * @param {Number|null} index\r\n */\r\n remove(index) {\r\n if (isNaN(index)) {\r\n index = this.length - 1;\r\n }\r\n\r\n this.blocks[index].html.remove();\r\n this.blocks.splice(index, 1);\r\n }\r\n\r\n /**\r\n * Remove all blocks\r\n */\r\n removeAll() {\r\n this.workingArea.innerHTML = '';\r\n this.blocks.length = 0;\r\n }\r\n\r\n /**\r\n * Insert Block after passed target\r\n *\r\n * @todo decide if this method is necessary\r\n *\r\n * @param {Block} targetBlock — target after wich Block should be inserted\r\n * @param {Block} newBlock — Block to insert\r\n */\r\n insertAfter(targetBlock, newBlock) {\r\n let index = this.blocks.indexOf(targetBlock);\r\n\r\n this.insert(index + 1, newBlock);\r\n }\r\n\r\n /**\r\n * Get Block by index\r\n *\r\n * @param {Number} index — Block index\r\n * @returns {Block}\r\n */\r\n get(index) {\r\n return this.blocks[index];\r\n }\r\n\r\n /**\r\n * Return index of passed Block\r\n *\r\n * @param {Block} block\r\n * @returns {Number}\r\n */\r\n indexOf(block) {\r\n return this.blocks.indexOf(block);\r\n }\r\n\r\n /**\r\n * Get length of Block instances array\r\n *\r\n * @returns {Number}\r\n */\r\n get length() {\r\n return this.blocks.length;\r\n }\r\n\r\n /**\r\n * Get Block instances array\r\n *\r\n * @returns {Block[]}\r\n */\r\n get array() {\r\n return this.blocks;\r\n }\r\n\r\n /**\r\n * Get blocks html elements array\r\n *\r\n * @returns {HTMLElement[]}\r\n */\r\n get nodes() {\r\n return _.array(this.workingArea.children);\r\n }\r\n\r\n /**\r\n * Proxy trap to implement array-like setter\r\n *\r\n * @example\r\n * blocks[0] = new Block(...)\r\n *\r\n * @param {Blocks} instance — Blocks instance\r\n * @param {Number|String} index — block index\r\n * @param {Block} block — Block to set\r\n * @returns {Boolean}\r\n */\r\n static set(instance, index, block) {\r\n if (isNaN(Number(index))) {\r\n return false;\r\n }\r\n\r\n instance.insert(index, block);\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Proxy trap to implement array-like getter\r\n *\r\n * @param {Blocks} instance — Blocks instance\r\n * @param {Number|String} index — Block index\r\n * @returns {Block|*}\r\n */\r\n static get(instance, index) {\r\n if (isNaN(Number(index))) {\r\n return instance[index];\r\n }\r\n\r\n return instance.get(index);\r\n }\r\n}\r\n","/**\r\n * @class Caret\r\n * @classdesc Contains methods for working Caret\r\n *\r\n * Uses Range methods to manipulate with caret\r\n *\r\n * @module Caret\r\n *\r\n * @version 2.0.0\r\n */\r\n\r\nimport Selection from '../selection';\r\n\r\n/**\r\n * @typedef {Caret} Caret\r\n */\r\nexport default class Caret extends Module {\r\n /**\r\n * @constructor\r\n */\r\n constructor({config}) {\r\n super({config});\r\n }\r\n\r\n /**\r\n * Method gets Block instance and puts caret to the text node with offset\r\n * There two ways that method applies caret position:\r\n * - first found text node: sets at the beginning, but you can pass an offset\r\n * - last found text node: sets at the end of the node. Also, you can customize the behaviour\r\n *\r\n * @param {Block} block - Block class\r\n * @param {Number} offset - caret offset regarding to the text node\r\n * @param {Boolean} atEnd - put caret at the end of the text node or not\r\n */\r\n setToBlock(block, offset = 0, atEnd = false) {\r\n let element = block.pluginsContent;\r\n\r\n /** If Element is INPUT */\r\n if ($.isNativeInput(element)) {\r\n element.focus();\r\n return;\r\n }\r\n\r\n let nodeToSet = $.getDeepestNode(element, atEnd);\r\n\r\n if (atEnd || offset > nodeToSet.length) {\r\n offset = nodeToSet.length;\r\n }\r\n\r\n /** if found deepest node is native input */\r\n if ($.isNativeInput(nodeToSet)) {\r\n nodeToSet.focus();\r\n return;\r\n }\r\n\r\n /**\r\n * @todo try to fix via Promises or use querySelectorAll to not to use timeout\r\n */\r\n _.delay( () => {\r\n this.set(nodeToSet, offset);\r\n }, 20)();\r\n\r\n this.Editor.BlockManager.currentNode = block.wrapper;\r\n }\r\n\r\n /**\r\n * Creates Document Range and sets caret to the element with offset\r\n * @param {Element} element - target node.\r\n * @param {Number} offset - offset\r\n */\r\n set( element, offset = 0) {\r\n let range = document.createRange(),\r\n selection = Selection.get();\r\n\r\n range.setStart(element, offset);\r\n range.setEnd(element, offset);\r\n\r\n selection.removeAllRanges();\r\n selection.addRange(range);\r\n };\r\n\r\n /**\r\n * Set Caret to the last Block\r\n * If last block is not empty, append another empty block\r\n */\r\n setToTheLastBlock() {\r\n let lastBlock = this.Editor.BlockManager.lastBlock;\r\n\r\n if (!lastBlock) return;\r\n\r\n /**\r\n * If last block is empty and it is an initialBlock, set to that.\r\n * Otherwise, append new empty block and set to that\r\n */\r\n if (lastBlock.isEmpty) {\r\n this.setToBlock(lastBlock);\r\n } else {\r\n this.Editor.BlockManager.insert(this.config.initialBlock);\r\n }\r\n }\r\n\r\n /**\r\n * Extract content fragment of current Block from Caret position to the end of the Block\r\n */\r\n extractFragmentFromCaretPosition() {\r\n let selection = Selection.get();\r\n\r\n if (selection.rangeCount) {\r\n let selectRange = selection.getRangeAt(0),\r\n blockElem = this.Editor.BlockManager.currentBlock.pluginsContent;\r\n\r\n selectRange.deleteContents();\r\n\r\n if (blockElem) {\r\n let range = selectRange.cloneRange(true);\r\n\r\n range.selectNodeContents(blockElem);\r\n range.setStart(selectRange.endContainer, selectRange.endOffset);\r\n return range.extractContents();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Get all first-level (first child of [contenteditabel]) siblings from passed node\r\n * Then you can check it for emptiness\r\n *\r\n * @example\r\n * adaddad
<-- passed node for example \r\n * |\r\n * | right first-level siblings\r\n * |\r\n *
or