find caret offset and calculate editor text in same tree-walking algo

instead of having the same logic twice
This commit is contained in:
Bruno Windels 2019-05-13 16:42:00 +01:00
parent 7ebb6ce621
commit 9e0816c51c
3 changed files with 115 additions and 103 deletions

View file

@ -14,85 +14,41 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
export function getCaretOffset(editor) {
const sel = document.getSelection();
console.info("getCaretOffset", sel.focusNode, sel.focusOffset);
// when deleting the last character of a node,
// the caret gets reported as being after the focusOffset-th node,
// with the focusNode being the editor
let offset = 0;
let node;
let atNodeEnd = true;
if (sel.focusNode.nodeType === Node.TEXT_NODE) {
node = sel.focusNode;
offset = sel.focusOffset;
atNodeEnd = sel.focusOffset === sel.focusNode.textContent.length;
} else if (sel.focusNode.nodeType === Node.ELEMENT_NODE) {
node = sel.focusNode.childNodes[sel.focusOffset];
offset = nodeLength(node);
}
while (node !== editor) {
while (node.previousSibling) {
node = node.previousSibling;
offset += nodeLength(node);
}
// then 1 move up
node = node.parentElement;
}
return {offset, atNodeEnd};
// // first make sure we're at the level of a direct child of editor
// if (node.parentElement !== editor) {
// // include all preceding siblings of the non-direct editor children
// while (node.previousSibling) {
// node = node.previousSibling;
// offset += nodeLength(node);
// }
// // then move up
// // I guess technically there could be preceding text nodes in the parents here as well,
// // but we're assuming there are no mixed text and element nodes
// while (node.parentElement !== editor) {
// node = node.parentElement;
// }
// }
// // now include the text length of all preceding direct editor children
// while (node.previousSibling) {
// node = node.previousSibling;
// offset += nodeLength(node);
// }
// {
// const {focusOffset, focusNode} = sel;
// console.log("selection", {focusOffset, focusNode, position, atNodeEnd});
// }
}
function nodeLength(node) {
if (node.nodeType === Node.ELEMENT_NODE) {
const isBlock = node.tagName === "DIV";
const isLastDiv = !node.nextSibling || node.nextSibling.tagName !== "DIV";
return node.textContent.length + ((isBlock && !isLastDiv) ? 1 : 0);
} else {
return node.textContent.length;
}
}
export function setCaretPosition(editor, caretPosition) {
export function setCaretPosition(editor, model, caretPosition) {
const sel = document.getSelection();
sel.removeAllRanges();
const range = document.createRange();
let focusNode = editor.childNodes[caretPosition.index];
const {parts} = model;
let lineIndex = 0;
let nodeIndex = -1;
for (let i = 0; i <= caretPosition.index; ++i) {
const part = parts[i];
if (part && part.type === "newline") {
lineIndex += 1;
nodeIndex = -1;
} else {
nodeIndex += 1;
}
}
let focusNode;
const lineNode = editor.childNodes[lineIndex];
if (lineNode) {
if (lineNode.childNodes.length === 0 && caretPosition.offset === 0) {
focusNode = lineNode;
} else {
focusNode = lineNode.childNodes[nodeIndex];
if (focusNode && focusNode.nodeType === Node.ELEMENT_NODE) {
focusNode = focusNode.childNodes[0];
}
}
}
// node not found, set caret at end
if (!focusNode) {
range.selectNodeContents(editor);
range.collapse(false);
} else {
// make sure we have a text node
if (focusNode.nodeType === Node.ELEMENT_NODE) {
focusNode = focusNode.childNodes[0];
}
range.setStart(focusNode, caretPosition.offset);
range.collapse(true);
}