vditor
Version:
♏ 易于使用的 Markdown 编辑器,为适配不同的应用场景而生
216 lines (200 loc) • 9.39 kB
text/typescript
import {getMarkdown} from "../markdown/getMarkdown";
import {accessLocalStorage} from "../util/compatibility";
import {scrollCenter} from "../util/editorCommonEvent";
import {hasClosestBlock, hasClosestByAttribute} from "../util/hasClosest";
import {hasClosestByTag} from "../util/hasClosestByHeadings";
import {log} from "../util/log";
import {getEditorRange, setRangeByWbr} from "../util/selection";
import {inputEvent} from "./inputEvent";
import {combineFootnote} from "./combineFootnote";
export const processPaste = (vditor: IVditor, text: string) => {
const range = getEditorRange(vditor);
range.extractContents();
range.insertNode(document.createTextNode(Lute.Caret));
range.insertNode(document.createTextNode(text));
let blockElement = hasClosestByAttribute(range.startContainer, "data-block", "0");
if (!blockElement) {
blockElement = vditor.sv.element;
}
let spinHTML = vditor.lute.SpinVditorSVDOM(blockElement.textContent)
spinHTML = "<div data-block='0'>" +
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
"</div>";
if (blockElement.isEqualNode(vditor.sv.element)) {
blockElement.innerHTML = spinHTML;
} else {
blockElement.outerHTML = spinHTML;
}
combineFootnote(vditor.sv.element)
setRangeByWbr(vditor.sv.element, range);
scrollCenter(vditor);
};
export const getSideByType = (spanNode: Node, type: string, isPrevious = true) => {
let sideElement = spanNode as Element;
if (sideElement.nodeType === 3) {
sideElement = sideElement.parentElement;
}
while (sideElement) {
if (sideElement.getAttribute("data-type") === type) {
return sideElement;
}
if (isPrevious) {
sideElement = sideElement.previousElementSibling;
} else {
sideElement = sideElement.nextElementSibling;
}
}
return false;
};
export const processSpinVditorSVDOM = (html: string, vditor: IVditor) => {
log("SpinVditorSVDOM", html, "argument", vditor.options.debugger);
const spinHTML = vditor.lute.SpinVditorSVDOM(html)
html = "<div data-block='0'>" +
spinHTML.replace(/<span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span><span data-type="newline"><br \/><span style="display: none">\n<\/span><\/span></g, '<span data-type="newline"><br /><span style="display: none">\n</span></span><span data-type="newline"><br /><span style="display: none">\n</span></span></div><div data-block="0"><') +
"</div>";
log("SpinVditorSVDOM", html, "result", vditor.options.debugger);
return html;
};
export const processPreviousMarkers = (spanElement: HTMLElement) => {
const spanType = spanElement.getAttribute("data-type");
let previousElement = spanElement.previousElementSibling;
// 有内容的子列表/标题,在其 marker 后换行
let markerText = (spanType && spanType !== "text" && spanType !== "table" && spanType !== "heading-marker" &&
spanType !== "newline" && spanType !== "yaml-front-matter-open-marker" && spanType !== "yaml-front-matter-close-marker"
&& spanType !== "code-block-info" && spanType !== "code-block-close-marker" && spanType !== "code-block-open-marker") ?
spanElement.textContent : "";
let hasNL = false;
if (spanType === "newline") {
hasNL = true;
}
while (previousElement && !hasNL) {
const previousType = previousElement.getAttribute("data-type");
if (previousType === "li-marker" || previousType === "blockquote-marker" || previousType === "task-marker" ||
previousType === "padding") {
const previousText = previousElement.textContent;
if (previousType === "li-marker" &&
(spanType === "code-block-open-marker" || spanType === "code-block-info")) {
// https://github.com/Vanessa219/vditor/issues/586
markerText = previousText.replace(/\S/g, " ") + markerText;
} else if (spanType === "code-block-close-marker" &&
previousElement.nextElementSibling.isSameNode(spanElement)) {
// https://github.com/Vanessa219/vditor/issues/594
const openMarker = getSideByType(spanElement, "code-block-open-marker");
if (openMarker && openMarker.previousElementSibling) {
previousElement = openMarker.previousElementSibling;
markerText = previousText + markerText;
}
} else {
markerText = previousText + markerText;
}
} else if (previousType === "newline") {
hasNL = true;
}
previousElement = previousElement.previousElementSibling;
}
return markerText;
};
export const processAfterRender = (vditor: IVditor, options = {
enableAddUndoStack: true,
enableHint: false,
enableInput: true,
}) => {
if (options.enableHint) {
vditor.hint.render(vditor);
}
vditor.preview.render(vditor);
const text = getMarkdown(vditor);
if (typeof vditor.options.input === "function" && options.enableInput) {
vditor.options.input(text);
}
if (vditor.options.counter.enable) {
vditor.counter.render(vditor, text);
}
if (vditor.options.cache.enable && accessLocalStorage()) {
localStorage.setItem(vditor.options.cache.id, text);
if (vditor.options.cache.after) {
vditor.options.cache.after(text);
}
}
if (vditor.devtools) {
vditor.devtools.renderEchart(vditor);
}
clearTimeout(vditor.sv.processTimeoutId);
vditor.sv.processTimeoutId = window.setTimeout(() => {
if (options.enableAddUndoStack && !vditor.sv.composingLock) {
vditor.undo.addToUndoStack(vditor);
}
}, vditor.options.undoDelay);
};
export const processHeading = (vditor: IVditor, value: string) => {
const range = getEditorRange(vditor);
const headingElement = hasClosestByTag(range.startContainer, "SPAN");
if (headingElement && headingElement.textContent.trim() !== "") {
value = "\n" + value;
}
range.collapse(true);
document.execCommand("insertHTML", false, value);
};
export const processToolbar = (vditor: IVditor, actionBtn: Element, prefix: string, suffix: string) => {
const range = getEditorRange(vditor);
const commandName = actionBtn.getAttribute("data-type");
// 添加
if (vditor.sv.element.childNodes.length === 0) {
vditor.sv.element.innerHTML = `<span data-type="p" data-block="0"><span data-type="text"><wbr></span></span><span data-type="newline"><br><span style="display: none">
</span></span>`;
setRangeByWbr(vditor.sv.element, range);
}
const blockElement = hasClosestBlock(range.startContainer);
const spanElement = hasClosestByTag(range.startContainer, "SPAN");
if (!blockElement) {
return;
}
if (commandName === "link") {
let html;
if (range.toString() === "") {
html = `${prefix}${Lute.Caret}${suffix}`;
} else {
html = `${prefix}${range.toString()}${suffix.replace(")", Lute.Caret + ")")}`;
}
document.execCommand("insertHTML", false, html);
return;
} else if (commandName === "italic" || commandName === "bold" || commandName === "strike" ||
commandName === "inline-code" || commandName === "code" || commandName === "table" || commandName === "line") {
let html;
// https://github.com/Vanessa219/vditor/issues/563 代码块不需要后面的 ```
if (range.toString() === "") {
html = `${prefix}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
} else {
html = `${prefix}${range.toString()}${Lute.Caret}${commandName === "code" ? "" : suffix}`;
}
if (commandName === "table" || (commandName === "code" && spanElement && spanElement.textContent !== "")) {
html = "\n\n" + html;
} else if (commandName === "line") {
html = `\n\n${prefix}\n${Lute.Caret}`;
}
document.execCommand("insertHTML", false, html);
return;
} else if (commandName === "check" || commandName === "list" || commandName === "ordered-list" ||
commandName === "quote") {
if (spanElement) {
let marker = "* ";
if (commandName === "check") {
marker = "* [ ] ";
} else if (commandName === "ordered-list") {
marker = "1. ";
} else if (commandName === "quote") {
marker = "> ";
}
const newLine = getSideByType(spanElement, "newline");
if (newLine) {
newLine.insertAdjacentText("afterend", marker);
} else {
blockElement.insertAdjacentText("afterbegin", marker);
}
inputEvent(vditor);
return;
}
}
setRangeByWbr(vditor.sv.element, range);
processAfterRender(vditor);
};