mirror of
https://github.com/codex-team/editor.js
synced 2024-06-27 01:40:22 +02:00
911 lines
23 KiB
JavaScript
911 lines
23 KiB
JavaScript
/**
|
|
* @module Codex Editor Callbacks module
|
|
* @description Module works with editor added Elements
|
|
*
|
|
* @author Codex Team
|
|
* @version 1.4.0
|
|
*/
|
|
|
|
module.exports = (function (callbacks) {
|
|
|
|
let 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 (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 () {
|
|
|
|
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 () {
|
|
|
|
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 (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 <p> 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 (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 (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 () {
|
|
|
|
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 () {
|
|
|
|
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) {
|
|
|
|
let 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 (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 (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 (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;
|
|
|
|
})({}); |