editor.js/modules/caret.js
Taly b9b07135f7 check for a coords or focusedNode is null (#255)
* check for a coords or focusedNode is null

* bump version
2018-05-25 17:49:46 +03:00

306 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 && 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;
})({});