UNPKG

vditor

Version:

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

334 lines (310 loc) 14.9 kB
import {Constants} from "../constants"; import {removeCurrentToolbar} from "../toolbar/setToolbar"; import {listToggle} from "../util/fixBrowserBehavior"; import {hasClosestBlock, hasClosestByMatchTag} from "../util/hasClosest"; import {processCodeRender} from "../util/processCode"; import {getEditorRange, setRangeByWbr, setSelectionFocus} from "../util/selection"; import {afterRenderEvent} from "./afterRenderEvent"; import {genAPopover, highlightToolbarWYSIWYG} from "./highlightToolbarWYSIWYG"; import {getNextHTML, getPreviousHTML, splitElement} from "./inlineTag"; const cancelBES = (range: Range, vditor: IVditor, commandName: string) => { let element = range.startContainer.parentElement; let jump = false; let lastTagName = ""; let lastEndTagName = ""; const splitHTML = splitElement(range); let lastBeforeHTML = splitHTML.beforeHTML; let lastAfterHTML = splitHTML.afterHTML; while (element && !jump) { let tagName = element.tagName; if (tagName === "STRIKE") { tagName = "S"; } if (tagName === "I") { tagName = "EM"; } if (tagName === "B") { tagName = "STRONG"; } if (tagName === "S" || tagName === "STRONG" || tagName === "EM") { let insertHTML = ""; let previousHTML = ""; let nextHTML = ""; if (element.parentElement.getAttribute("data-block") !== "0") { previousHTML = getPreviousHTML(element); nextHTML = getNextHTML(element); } if (lastBeforeHTML || previousHTML) { insertHTML = `${previousHTML}<${tagName}>${lastBeforeHTML}</${tagName}>`; lastBeforeHTML = insertHTML; } if ((commandName === "bold" && tagName === "STRONG") || (commandName === "italic" && tagName === "EM") || (commandName === "strikeThrough" && tagName === "S")) { // 取消 insertHTML += `${lastTagName}${Constants.ZWSP}<wbr>${lastEndTagName}`; jump = true; } if (lastAfterHTML || nextHTML) { lastAfterHTML = `<${tagName}>${lastAfterHTML}</${tagName}>${nextHTML}`; insertHTML += lastAfterHTML; } if (element.parentElement.getAttribute("data-block") !== "0") { element = element.parentElement; element.innerHTML = insertHTML; } else { element.outerHTML = insertHTML; element = element.parentElement; } lastTagName = `<${tagName}>` + lastTagName; lastEndTagName = `</${tagName}>` + lastEndTagName; } else { jump = true; } } setRangeByWbr(vditor.wysiwyg.element, range); }; export const toolbarEvent = (vditor: IVditor, actionBtn: Element, event: Event) => { if (vditor.wysiwyg.composingLock // Mac Chrome 中韩文结束会出发此事件,导致重复末尾字符 https://github.com/Vanessa219/vditor/issues/188 && event instanceof CustomEvent // 点击按钮应忽略输入法 https://github.com/Vanessa219/vditor/issues/473 ) { return; } let useHighlight = true; let useRender = true; if (vditor.wysiwyg.element.querySelector("wbr")) { vditor.wysiwyg.element.querySelector("wbr").remove(); } const range = getEditorRange(vditor); let commandName = actionBtn.getAttribute("data-type"); // 移除 if (actionBtn.classList.contains("vditor-menu--current")) { if (commandName === "strike") { commandName = "strikeThrough"; } if (commandName === "quote") { let quoteElement = hasClosestByMatchTag(range.startContainer, "BLOCKQUOTE"); if (!quoteElement) { quoteElement = range.startContainer.childNodes[range.startOffset] as HTMLElement; } if (quoteElement) { useHighlight = false; actionBtn.classList.remove("vditor-menu--current"); range.insertNode(document.createElement("wbr")); quoteElement.outerHTML = quoteElement.innerHTML.trim() === "" ? `<p data-block="0">${quoteElement.innerHTML}</p>` : quoteElement.innerHTML; setRangeByWbr(vditor.wysiwyg.element, range); } } else if (commandName === "inline-code") { let inlineCodeElement = hasClosestByMatchTag(range.startContainer, "CODE"); if (!inlineCodeElement) { inlineCodeElement = range.startContainer.childNodes[range.startOffset] as HTMLElement; } if (inlineCodeElement) { inlineCodeElement.outerHTML = inlineCodeElement.innerHTML.replace(Constants.ZWSP, "") + "<wbr>"; setRangeByWbr(vditor.wysiwyg.element, range); } } else if (commandName === "link") { if (!range.collapsed) { document.execCommand("unlink", false, ""); } else { range.selectNode(range.startContainer.parentElement); document.execCommand("unlink", false, ""); } } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") { listToggle(vditor, range, commandName); setRangeByWbr(vditor.wysiwyg.element, range); useHighlight = false; actionBtn.classList.remove("vditor-menu--current"); } else { // bold, italic, strike useHighlight = false; actionBtn.classList.remove("vditor-menu--current"); if (range.toString() === "") { cancelBES(range, vditor, commandName); } else { document.execCommand(commandName, false, ""); } } } else { // 添加 if (vditor.wysiwyg.element.childNodes.length === 0) { vditor.wysiwyg.element.innerHTML = '<p data-block="0"><wbr></p>'; setRangeByWbr(vditor.wysiwyg.element, range); } let blockElement = hasClosestBlock(range.startContainer); if (commandName === "quote") { if (!blockElement) { blockElement = range.startContainer.childNodes[range.startOffset] as HTMLElement; } if (blockElement) { useHighlight = false; actionBtn.classList.add("vditor-menu--current"); range.insertNode(document.createElement("wbr")); const liElement = hasClosestByMatchTag(range.startContainer, "LI"); // li 中软换行 if (liElement && blockElement.contains(liElement)) { liElement.innerHTML = `<blockquote data-block="0">${liElement.innerHTML}</blockquote>`; } else { blockElement.outerHTML = `<blockquote data-block="0">${blockElement.outerHTML}</blockquote>`; } setRangeByWbr(vditor.wysiwyg.element, range); } } else if (commandName === "check" || commandName === "list" || commandName === "ordered-list") { listToggle(vditor, range, commandName, false); setRangeByWbr(vditor.wysiwyg.element, range); useHighlight = false; removeCurrentToolbar(vditor.toolbar.elements, ["check", "list", "ordered-list"]); actionBtn.classList.add("vditor-menu--current"); } else if (commandName === "inline-code") { if (range.toString() === "") { const node = document.createElement("code"); node.textContent = Constants.ZWSP; range.insertNode(node); range.setStart(node.firstChild, 1); range.collapse(true); setSelectionFocus(range); } else if (range.startContainer.nodeType === 3) { const node = document.createElement("code"); range.surroundContents(node); range.insertNode(node); setSelectionFocus(range); } actionBtn.classList.add("vditor-menu--current"); } else if (commandName === "code") { const node = document.createElement("div"); node.className = "vditor-wysiwyg__block"; node.setAttribute("data-type", "code-block"); node.setAttribute("data-block", "0"); node.setAttribute("data-marker", "```"); if (range.toString() === "") { node.innerHTML = "<pre><code><wbr>\n</code></pre>"; } else { node.innerHTML = `<pre><code>${range.toString()}<wbr></code></pre>`; range.deleteContents(); } range.insertNode(node); if (blockElement) { blockElement.outerHTML = vditor.lute.SpinVditorDOM(blockElement.outerHTML); } setRangeByWbr(vditor.wysiwyg.element, range); vditor.wysiwyg.element.querySelectorAll(".vditor-wysiwyg__preview[data-render='2']").forEach( (item: HTMLElement) => { processCodeRender(item, vditor); }); actionBtn.classList.add("vditor-menu--disabled"); } else if (commandName === "link") { if (range.toString() === "") { const aElement = document.createElement("a"); aElement.innerText = Constants.ZWSP; range.insertNode(aElement); range.setStart(aElement.firstChild, 1); range.collapse(true); genAPopover(vditor, aElement, range); const textInputElement = vditor.wysiwyg.popover.querySelector("input"); textInputElement.value = ""; textInputElement.focus(); useRender = false; } else { const node = document.createElement("a"); node.setAttribute("href", ""); node.innerHTML = range.toString(); range.surroundContents(node); range.insertNode(node); setSelectionFocus(range); genAPopover(vditor, node, range); const textInputElements = vditor.wysiwyg.popover.querySelectorAll("input"); textInputElements[0].value = node.innerText; textInputElements[1].focus(); } useHighlight = false; actionBtn.classList.add("vditor-menu--current"); } else if (commandName === "table") { let tableHTML = `<table data-block="0"><thead><tr><th>col1<wbr></th><th>col2</th><th>col3</th></tr></thead><tbody><tr><td> </td><td> </td><td> </td></tr><tr><td> </td><td> </td><td> </td></tr></tbody></table>`; if (range.toString().trim() === "") { if (blockElement && blockElement.innerHTML.trim().replace(Constants.ZWSP, "") === "") { blockElement.outerHTML = tableHTML; } else { document.execCommand("insertHTML", false, tableHTML); } range.selectNode(vditor.wysiwyg.element.querySelector("wbr").previousSibling); vditor.wysiwyg.element.querySelector("wbr").remove(); setSelectionFocus(range); } else { tableHTML = `<table data-block="0"><thead><tr>`; const tableText = range.toString().split("\n"); const delimiter = tableText[0].split(",").length > tableText[0].split("\t").length ? "," : "\t"; tableText.forEach((rows, index) => { if (index === 0) { rows.split(delimiter).forEach((header, subIndex) => { if (subIndex === 0) { tableHTML += `<th>${header}<wbr></th>`; } else { tableHTML += `<th>${header}</th>`; } }); tableHTML += "</tr></thead>"; } else { if (index === 1) { tableHTML += "<tbody><tr>"; } else { tableHTML += "<tr>"; } rows.split(delimiter).forEach((cell) => { tableHTML += `<td>${cell}</td>`; }); tableHTML += `</tr>`; } }); tableHTML += "</tbody></table>"; document.execCommand("insertHTML", false, tableHTML); setRangeByWbr(vditor.wysiwyg.element, range); } useHighlight = false; actionBtn.classList.add("vditor-menu--disabled"); } else if (commandName === "line") { if (blockElement) { const hrHTML = '<hr data-block="0"><p data-block="0"><wbr>\n</p>'; if (blockElement.innerHTML.trim() === "") { blockElement.outerHTML = hrHTML; } else { blockElement.insertAdjacentHTML("afterend", hrHTML); } setRangeByWbr(vditor.wysiwyg.element, range); } } else { // bold, italic, strike useHighlight = false; actionBtn.classList.add("vditor-menu--current"); if (commandName === "strike") { commandName = "strikeThrough"; } if (range.toString() === "" && (commandName === "bold" || commandName === "italic" || commandName === "strikeThrough")) { let tagName = "strong"; if (commandName === "italic") { tagName = "em"; } else if (commandName === "strikeThrough") { tagName = "s"; } const node = document.createElement(tagName); node.textContent = Constants.ZWSP; range.insertNode(node); if (node.previousSibling && node.previousSibling.textContent === Constants.ZWSP) { // 移除多层嵌套中的 zwsp node.previousSibling.textContent = ""; } range.setStart(node.firstChild, 1); range.collapse(true); setSelectionFocus(range); } else { document.execCommand(commandName, false, ""); } } } if (useHighlight) { highlightToolbarWYSIWYG(vditor); } if (useRender) { afterRenderEvent(vditor); } };