mirror of
https://github.com/codex-team/editor.js
synced 2024-06-27 01:40:22 +02:00
305 lines
7.1 KiB
JavaScript
305 lines
7.1 KiB
JavaScript
/**
|
|
* Codex Editor Caret Module
|
|
*
|
|
* @author Codex Team
|
|
* @version 1.0
|
|
*/
|
|
|
|
module.exports = (function (caret) {
|
|
|
|
let 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 () {
|
|
|
|
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 () {
|
|
|
|
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;
|
|
|
|
})({}); |