import {getContenteditableElement, getNextBlock, getPreviousBlock, hasPreviousSibling} from "../wysiwyg/getBlock"; import {hasClosestByMatchTag} from "./hasClosest"; import {countSelectWord} from "../../layout/status"; const selectIsEditor = (editor: Element, range?: Range) => { if (!range) { if (getSelection().rangeCount === 0) { return false; } else { range = getSelection().getRangeAt(0); } } const container = range.commonAncestorContainer; return editor.isEqualNode(container) || editor.contains(container); }; export const selectAll = (protyle: IProtyle, nodeElement: Element, range: Range) => { const editElement = getContenteditableElement(nodeElement); if (editElement) { let position; if (editElement.tagName === "TABLE") { const cellElement = hasClosestByMatchTag(range.startContainer, "TD") || hasClosestByMatchTag(range.startContainer, "TH"); if (cellElement) { position = getSelectionOffset(cellElement, nodeElement, range); if (position.start !== 0 || position.end !== cellElement.textContent.length) { range.setStart(cellElement.firstChild, 0); range.setEndAfter(cellElement.lastChild); protyle.toolbar.render(protyle, range); countSelectWord(range); return true; } } } else { position = getSelectionOffset(editElement, nodeElement, range); if (position.start !== 0 || position.end !== editElement.textContent.length) { // 全选后 rang 不对 https://ld246.com/article/1654848722251 let firstChild = editElement.firstChild; while (firstChild) { if (firstChild.nodeType === 3) { if (firstChild.textContent !== "") { range.setStart(firstChild, 0); break; } firstChild = firstChild.nextSibling; } else { if ((firstChild as HTMLElement).classList.contains("render-node") || (firstChild as HTMLElement).classList.contains("img")) { range.setStartBefore(firstChild); break; } firstChild = firstChild.firstChild; } } let lastChild = editElement.lastChild; while (lastChild) { if (lastChild.nodeType === 3) { if (lastChild.textContent !== "") { range.setEnd(lastChild, lastChild.textContent.length); break; } lastChild = lastChild.previousSibling; } else { if ((lastChild as HTMLElement).classList.contains("render-node") || (lastChild as HTMLElement).classList.contains("img")) { range.setEndAfter(lastChild); break; } lastChild = lastChild.lastChild; } } protyle.toolbar.render(protyle, range); countSelectWord(range); return true; } } } range.collapse(true); const selectElements = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"); if (protyle.wysiwyg.element.childElementCount === selectElements.length && selectElements[0].parentElement.isSameNode(protyle.wysiwyg.element)) { return true; } selectElements.forEach(item => { item.classList.remove("protyle-wysiwyg--select"); }); Array.from(protyle.wysiwyg.element.children).forEach(item => { item.classList.add("protyle-wysiwyg--select"); }); }; export const getEditorRange = (element: Element) => { let range: Range; if (getSelection().rangeCount > 0) { range = getSelection().getRangeAt(0); if (element.isEqualNode(range.startContainer) || element.contains(range.startContainer)) { return range; } } // 代码块过长,在代码块的下一个块前删除,代码块会滚动到顶部,因粗需要 preventScroll (element as HTMLElement).focus({preventScroll: true}); let targetElement; if (element.classList.contains("table")) { // 当光标不在表格区域中时表格无法被复制 https://ld246.com/article/1650510736504 targetElement = element.querySelector("th") || element.querySelector("td"); } else { targetElement = getContenteditableElement(element); if (!targetElement) { targetElement = element; } } range = targetElement.ownerDocument.createRange(); range.setStart(targetElement || element, 0); range.collapse(true); return range; }; export const getSelectionPosition = (nodeElement: Element, range?: Range) => { if (!range) { range = getEditorRange(nodeElement); } if (!nodeElement.contains(range.startContainer)) { return { left: 0, top: 0, }; } let cursorRect; if (range.getClientRects().length === 0) { if (range.startContainer.nodeType === 3) { // 空行时,会出现没有 br 的情况,需要根据父元素
获取位置信息
const parent = range.startContainer.parentElement;
if (parent && parent.getClientRects().length > 0) {
cursorRect = parent.getClientRects()[0];
} else {
return {
left: 0,
top: 0,
};
}
} else {
const children = (range.startContainer as Element).children;
if (children[range.startOffset] &&
children[range.startOffset].getClientRects().length > 0) {
// markdown 模式回车
cursorRect = children[range.startOffset].getClientRects()[0];
} else if (range.startContainer.childNodes.length > 0) {
// in table or code block
const cloneRange = range.cloneRange();
range.selectNode(range.startContainer.childNodes[Math.max(0, range.startOffset - 1)]);
cursorRect = range.getClientRects()[0];
range.setEnd(cloneRange.endContainer, cloneRange.endOffset);
range.setStart(cloneRange.startContainer, cloneRange.startOffset);
} else {
cursorRect = (range.startContainer as HTMLElement).getClientRects()[0];
}
if (!cursorRect) {
let parentElement = range.startContainer.childNodes[range.startOffset] as HTMLElement;
if (!parentElement) {
parentElement = range.startContainer.childNodes[range.startOffset - 1] as HTMLElement;
}
while (!parentElement.getClientRects || (parentElement.getClientRects && parentElement.getClientRects().length === 0)) {
parentElement = parentElement.parentElement;
}
cursorRect = parentElement.getClientRects()[0];
}
}
} else {
cursorRect = range.getBoundingClientRect();
}
return {
left: cursorRect.left,
top: cursorRect.top,
};
};
export const getSelectionOffset = (selectElement: Element, editorElement?: Element, range?: Range) => {
const position = {
end: 0,
start: 0,
};
if (!range) {
if (getSelection().rangeCount === 0) {
return position;
}
range = window.getSelection().getRangeAt(0);
}
if (editorElement && !selectIsEditor(editorElement, range)) {
return position;
}
const preSelectionRange = range.cloneRange();
if (selectElement.childNodes[0] && selectElement.childNodes[0].childNodes[0]) {
preSelectionRange.setStart(selectElement.childNodes[0].childNodes[0], 0);
} else {
preSelectionRange.selectNodeContents(selectElement);
}
preSelectionRange.setEnd(range.startContainer, range.startOffset);
position.start = preSelectionRange.toString().length;
position.end = position.start + range.toString().length;
return position;
};
function searchNode(
container: Node,
startNode: Node,
predicate: (node: Node) => boolean,
excludeSibling?: boolean,
): boolean {
if (!startNode) {
return false;
}
if (predicate(startNode as Text)) {
return true;
}
for (let i = 0, len = startNode.childNodes.length; i < len; i++) {
if (searchNode(startNode, startNode.childNodes[i], predicate, true)) {
return true;
}
}
if (!excludeSibling) {
let parentNode = startNode;
while (parentNode && parentNode !== container) {
let nextSibling = parentNode.nextSibling;
while (nextSibling) {
if (searchNode(container, nextSibling, predicate, true)) {
return true;
}
nextSibling = nextSibling.nextSibling;
}
parentNode = parentNode.parentNode;
}
}
return false;
}
export const setLastNodeRange = (editElement: Element, range: Range, setStart = true) => {
if (!editElement) {
return range;
}
let lastNode = editElement.lastChild;
while (lastNode && lastNode.nodeType !== 3) {
// 最后一个为多种行内元素嵌套
lastNode = lastNode.lastChild;
}
if (!lastNode) {
range.selectNodeContents(editElement);
return range;
}
if (setStart) {
range.setStart(lastNode, lastNode.textContent.length);
} else {
range.setEnd(lastNode, lastNode.textContent.length);
}
return range;
};
export const setFirstNodeRange = (editElement: Element, range: Range) => {
if (!editElement) {
return range;
}
let firstChild = editElement.firstChild;
while (firstChild && firstChild.nodeType !== 3) {
firstChild = firstChild.firstChild;
}
if (!firstChild) {
range.selectNodeContents(editElement);
return range;
}
range.setStart(firstChild, 0);
return range;
};
export const focusByOffset = (container: Node, start: number, end: number) => {
if (!container) {
return;
}
let startNode;
searchNode(container, container.firstChild, node => {
if (node.nodeType === Node.TEXT_NODE) {
const dataLength = (node as Text).data.length;
if (start <= dataLength) {
startNode = node;
return true;
}
start -= dataLength;
end -= dataLength;
return false;
}
});
let endNode;
if (startNode) {
searchNode(container, startNode, node => {
if (node.nodeType === Node.TEXT_NODE) {
const dataLength = (node as Text).data.length;
if (end <= dataLength) {
endNode = node;
return true;
}
end -= dataLength;
return false;
}
});
}
const range = document.createRange();
if (startNode) {
if (start < (startNode as Text).data.length) {
range.setStart(startNode, start);
} else {
range.setStartAfter(startNode);
}
} else {
if (start === 0) {
range.setStart(container, 0);
} else {
setLastNodeRange(getContenteditableElement(container as Element), range);
}
}
if (endNode) {
if (end < (endNode as Text).data.length) {
range.setEnd(endNode, end);
} else {
range.setEndAfter(endNode);
}
} else {
if (end === 0) {
range.setEnd(container, 0);
} else {
setLastNodeRange(getContenteditableElement(container as Element), range, false);
}
}
focusByRange(range);
};
export const focusByWbr = (element: Element, range: Range) => {
const wbrElements = element.querySelectorAll("wbr");
if (wbrElements.length === 0) {
return;
}
// 没找到 wbr 产生多个的地方,先顶顶
wbrElements.forEach((item, index) => {
if (index !== 0) {
item.remove();
}
});
const wbrElement = wbrElements[0];
if (!wbrElement.previousElementSibling) {
if (wbrElement.previousSibling) {
// text