UNPKG

vditor

Version:

♏ 易于使用的 Markdown 编辑器,为适配不同的应用场景而生

276 lines (255 loc) 10.6 kB
import {Constants} from "../constants"; import {isChrome} from "./compatibility"; import {hasClosestBlock, hasClosestByClassName} from "./hasClosest"; export const getEditorRange = (vditor: IVditor) => { let range: Range; const element = vditor[vditor.currentMode].element; if (getSelection().rangeCount > 0) { range = getSelection().getRangeAt(0); if (element.isEqualNode(range.startContainer) || element.contains(range.startContainer)) { return range; } } if (vditor[vditor.currentMode].range) { return vditor[vditor.currentMode].range; } element.focus(); range = element.ownerDocument.createRange(); range.setStart(element, 0); range.collapse(true); return range; }; export const getCursorPosition = (editor: HTMLElement) => { const range = window.getSelection().getRangeAt(0); if (!editor.contains(range.startContainer) && !hasClosestByClassName(range.startContainer, "vditor-panel--none")) { return { left: 0, top: 0, }; } const parentRect = editor.parentElement.getBoundingClientRect(); let cursorRect; if (range.getClientRects().length === 0) { if (range.startContainer.nodeType === 3) { // 空行时,会出现没有 br 的情况,需要根据父元素 <p> 获取位置信息 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; while (!parentElement.getClientRects || (parentElement.getClientRects && parentElement.getClientRects().length === 0)) { parentElement = parentElement.parentElement; } cursorRect = parentElement.getClientRects()[0]; } } } else { cursorRect = range.getClientRects()[0]; } return { left: cursorRect.left - parentRect.left, top: cursorRect.top - parentRect.top, }; }; export const selectIsEditor = (editor: HTMLElement, 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 setSelectionFocus = (range: Range) => { const selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); }; export const getSelectPosition = (selectElement: HTMLElement, editorElement: HTMLElement, range?: Range) => { const position = { end: 0, start: 0, }; if (!range) { if (getSelection().rangeCount === 0) { return position; } range = window.getSelection().getRangeAt(0); } if (selectIsEditor(editorElement, range)) { 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; }; export const setSelectionByPosition = (start: number, end: number, editor: HTMLElement) => { let charIndex = 0; let line = 0; let pNode = editor.childNodes[line]; let foundStart = false; let stop = false; start = Math.max(0, start); end = Math.max(0, end); const range = editor.ownerDocument.createRange(); range.setStart(pNode || editor, 0); range.collapse(true); while (!stop && pNode) { const nextCharIndex = charIndex + pNode.textContent.length; if (!foundStart && start >= charIndex && start <= nextCharIndex) { if (start === 0) { range.setStart(pNode, 0); } else { if (pNode.childNodes[0].nodeType === 3) { range.setStart(pNode.childNodes[0], start - charIndex); } else if (pNode.nextSibling) { range.setStartBefore(pNode.nextSibling); } else { range.setStartAfter(pNode); } } foundStart = true; if (start === end) { stop = true; break; } } if (foundStart && end >= charIndex && end <= nextCharIndex) { if (end === 0) { range.setEnd(pNode, 0); } else { if (pNode.childNodes[0].nodeType === 3) { range.setEnd(pNode.childNodes[0], end - charIndex); } else if (pNode.nextSibling) { range.setEndBefore(pNode.nextSibling); } else { range.setEndAfter(pNode); } } stop = true; } charIndex = nextCharIndex; pNode = editor.childNodes[++line]; } if (!stop && editor.childNodes[line - 1]) { range.setStartBefore(editor.childNodes[line - 1]); } setSelectionFocus(range); return range; }; export const setRangeByWbr = (element: HTMLElement, range: Range) => { const wbrElement = element.querySelector("wbr"); if (!wbrElement) { return; } if (!wbrElement.previousElementSibling) { if (wbrElement.previousSibling) { // text<wbr> range.setStart(wbrElement.previousSibling, wbrElement.previousSibling.textContent.length); } else if (wbrElement.nextSibling) { if (wbrElement.nextSibling.nodeType === 3) { // <wbr>text range.setStart(wbrElement.nextSibling, 0); } else { // <wbr><br> https://github.com/Vanessa219/vditor/issues/400 range.setStartBefore(wbrElement.nextSibling); } } else { // 内容为空 range.setStart(wbrElement.parentElement, 0); } } else { if (wbrElement.previousElementSibling.isSameNode(wbrElement.previousSibling)) { if (wbrElement.previousElementSibling.lastChild) { // <em>text</em><wbr> range.setStartBefore(wbrElement); range.collapse(true); setSelectionFocus(range); // fix Chrome set range bug: **c** if (isChrome() && (wbrElement.previousElementSibling.tagName === "EM" || wbrElement.previousElementSibling.tagName === "STRONG" || wbrElement.previousElementSibling.tagName === "S")) { range.insertNode(document.createTextNode(Constants.ZWSP)); range.collapse(false); } wbrElement.remove(); return; } else { // <br><wbr> range.setStartAfter(wbrElement.previousElementSibling); } } else { // <em>text</em>text<wbr> range.setStart(wbrElement.previousSibling, wbrElement.previousSibling.textContent.length); } } range.collapse(true); wbrElement.remove(); setSelectionFocus(range); }; export const insertHTML = (html: string, vditor: IVditor) => { // 使用 lute 方法会添加 p 元素,只有一个 p 元素的时候进行删除 const tempElement = document.createElement("div"); tempElement.innerHTML = html; const tempBlockElement = tempElement.querySelectorAll("p"); if (tempBlockElement.length === 1 && !tempBlockElement[0].previousSibling && !tempBlockElement[0].nextSibling && vditor[vditor.currentMode].element.children.length > 0 && tempElement.firstElementChild.tagName === "P") { html = tempBlockElement[0].innerHTML.trim(); } const pasteElement = document.createElement("div"); pasteElement.innerHTML = html; const range = getEditorRange(vditor); if (range.toString() !== "") { vditor[vditor.currentMode].preventInput = true; document.execCommand("delete", false, ""); } if (pasteElement.firstElementChild && pasteElement.firstElementChild.getAttribute("data-block") === "0") { // 粘贴内容为块元素时,应在下一段落中插入 pasteElement.lastElementChild.insertAdjacentHTML("beforeend", "<wbr>"); const blockElement = hasClosestBlock(range.startContainer); if (!blockElement) { vditor[vditor.currentMode].element.insertAdjacentHTML("beforeend", pasteElement.innerHTML); } else { blockElement.insertAdjacentHTML("afterend", pasteElement.innerHTML); } setRangeByWbr(vditor[vditor.currentMode].element, range); } else { const pasteTemplate = document.createElement("template"); pasteTemplate.innerHTML = html; range.insertNode(pasteTemplate.content.cloneNode(true)); range.collapse(false); setSelectionFocus(range); } };