2022-05-26 15:18:53 +08:00
|
|
|
|
import {Constants} from "../../constants";
|
|
|
|
|
|
import {uploadFiles, uploadLocalFiles} from "../upload";
|
|
|
|
|
|
import {processPasteCode, processRender} from "./processCode";
|
2023-08-04 14:16:47 +08:00
|
|
|
|
import {readText, writeText} from "./compatibility";
|
2022-08-23 20:57:39 +08:00
|
|
|
|
/// #if !BROWSER
|
|
|
|
|
|
import {clipboard} from "electron";
|
|
|
|
|
|
/// #endif
|
2022-05-26 15:18:53 +08:00
|
|
|
|
import {hasClosestBlock} from "./hasClosest";
|
2023-04-22 19:22:53 +08:00
|
|
|
|
import {getEditorRange} from "./selection";
|
2023-06-07 10:36:20 +08:00
|
|
|
|
import {blockRender} from "../render/blockRender";
|
|
|
|
|
|
import {highlightRender} from "../render/highlightRender";
|
2022-05-26 15:18:53 +08:00
|
|
|
|
import {fetchPost, fetchSyncPost} from "../../util/fetch";
|
|
|
|
|
|
import {isDynamicRef, isFileAnnotation} from "../../util/functions";
|
|
|
|
|
|
import {insertHTML} from "./insertHTML";
|
|
|
|
|
|
import {scrollCenter} from "../../util/highlightById";
|
2022-10-22 11:31:41 +08:00
|
|
|
|
import {hideElements} from "../ui/hideElements";
|
2023-06-08 22:55:31 +08:00
|
|
|
|
import {avRender} from "../render/av/render";
|
2024-04-01 11:51:24 +08:00
|
|
|
|
import {cellScrollIntoView, getCellText} from "../render/av/cell";
|
2024-04-01 17:33:18 +08:00
|
|
|
|
import {getContenteditableElement} from "../wysiwyg/getBlock";
|
|
|
|
|
|
|
|
|
|
|
|
export const getTextStar = (blockElement: HTMLElement) => {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
const dataType = blockElement.dataset.type;
|
|
|
|
|
|
let refText = "";
|
2024-04-01 17:33:18 +08:00
|
|
|
|
if (["NodeHeading", "NodeParagraph"].includes(dataType)) {
|
|
|
|
|
|
refText = getContenteditableElement(blockElement).innerHTML;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
if ("NodeHTMLBlock" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = "HTML";
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeAttributeView" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = blockElement.querySelector(".av__title").textContent || window.siyuan.languages.database;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeThematicBreak" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = window.siyuan.languages.line;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeIFrame" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = "IFrame";
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeWidget" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = window.siyuan.languages.widget;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeVideo" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = window.siyuan.languages.video;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if ("NodeAudio" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
refText = window.siyuan.languages.audio;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
} else if (["NodeCodeBlock", "NodeTable"].includes(dataType)) {
|
|
|
|
|
|
refText = getPlainText(blockElement);
|
|
|
|
|
|
} else if (blockElement.classList.contains("render-node")) {
|
|
|
|
|
|
// 需在嵌入块后,代码块前
|
|
|
|
|
|
refText += blockElement.dataset.subtype || Lute.UnEscapeHTMLStr(blockElement.getAttribute("data-content"));
|
2024-04-24 22:35:23 +08:00
|
|
|
|
} else if (["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(dataType)) {
|
2024-04-01 17:33:18 +08:00
|
|
|
|
Array.from(blockElement.querySelectorAll("[data-node-id]")).find((item: HTMLElement) => {
|
|
|
|
|
|
if (!["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(item.getAttribute("data-type"))) {
|
|
|
|
|
|
refText = getTextStar(blockElement.querySelector("[data-node-id]"));
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2024-04-02 09:16:12 +08:00
|
|
|
|
});
|
2024-04-01 17:33:18 +08:00
|
|
|
|
if (refText) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
return refText;
|
2024-04-01 17:33:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return refText + ` <span data-type="block-ref" data-subtype="s" data-id="${blockElement.getAttribute("data-node-id")}">*</span>`;
|
2024-04-02 09:16:12 +08:00
|
|
|
|
};
|
2024-04-01 11:51:24 +08:00
|
|
|
|
|
|
|
|
|
|
export const getPlainText = (blockElement: HTMLElement, isNested = false) => {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
let text = "";
|
|
|
|
|
|
const dataType = blockElement.dataset.type;
|
2024-04-01 11:51:24 +08:00
|
|
|
|
if ("NodeHTMLBlock" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
text += Lute.UnEscapeHTMLStr(blockElement.querySelector("protyle-html").getAttribute("data-content"));
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if ("NodeAttributeView" === dataType) {
|
|
|
|
|
|
blockElement.querySelectorAll(".av__row").forEach(rowElement => {
|
|
|
|
|
|
rowElement.querySelectorAll(".av__cell").forEach((cellElement: HTMLElement) => {
|
|
|
|
|
|
text += getCellText(cellElement) + " ";
|
2024-04-02 09:16:12 +08:00
|
|
|
|
});
|
2024-04-01 11:51:24 +08:00
|
|
|
|
text += "\n";
|
2024-04-02 09:16:12 +08:00
|
|
|
|
});
|
|
|
|
|
|
text = text.trimEnd();
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if ("NodeThematicBreak" === dataType) {
|
|
|
|
|
|
text += "---";
|
|
|
|
|
|
} else if ("NodeIFrame" === dataType || "NodeWidget" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
text += blockElement.querySelector("iframe").getAttribute("src");
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if ("NodeVideo" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
text += blockElement.querySelector("video").getAttribute("src");
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if ("NodeAudio" === dataType) {
|
2024-04-02 09:16:12 +08:00
|
|
|
|
text += blockElement.querySelector("audio").getAttribute("src");
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if (blockElement.classList.contains("render-node")) {
|
|
|
|
|
|
// 需在嵌入块后,代码块前
|
2024-04-02 09:16:12 +08:00
|
|
|
|
text += Lute.UnEscapeHTMLStr(blockElement.getAttribute("data-content"));
|
2024-04-01 11:51:24 +08:00
|
|
|
|
} else if (["NodeHeading", "NodeParagraph", "NodeCodeBlock", "NodeTable"].includes(dataType)) {
|
|
|
|
|
|
text += blockElement.querySelector("[spellcheck]").textContent;
|
|
|
|
|
|
} else if (!isNested && ["NodeBlockquote", "NodeList", "NodeSuperBlock", "NodeListItem"].includes(dataType)) {
|
|
|
|
|
|
blockElement.querySelectorAll("[data-node-id]").forEach((item: HTMLElement) => {
|
|
|
|
|
|
const nestedText = getPlainText(item, true);
|
|
|
|
|
|
text += nestedText ? nestedText + "\n" : "";
|
2024-04-02 09:16:12 +08:00
|
|
|
|
});
|
2024-04-01 11:51:24 +08:00
|
|
|
|
}
|
|
|
|
|
|
return text;
|
2024-04-02 09:16:12 +08:00
|
|
|
|
};
|
2022-05-26 15:18:53 +08:00
|
|
|
|
|
2023-09-08 23:12:54 +08:00
|
|
|
|
export const pasteEscaped = async (protyle: IProtyle, nodeElement: Element) => {
|
2023-08-04 14:16:47 +08:00
|
|
|
|
try {
|
2024-03-24 09:25:05 +08:00
|
|
|
|
// * _ [ ] ! \ ` < > & ~ { } ( ) = # $ ^ | .
|
2023-08-04 14:16:47 +08:00
|
|
|
|
let clipText = await readText();
|
|
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/5446
|
|
|
|
|
|
// A\B\C\D\
|
|
|
|
|
|
// E
|
|
|
|
|
|
// task-blog-2~default~baiduj 无法原义粘贴含有 `~foo~` 的文本 https://github.com/siyuan-note/siyuan/issues/5523
|
|
|
|
|
|
|
|
|
|
|
|
// 这里必须多加一个反斜杆,因为 Lute 在进行 Markdown 嵌套节点转换平铺标记节点时会剔除 Backslash 节点,
|
2023-11-30 10:51:27 +08:00
|
|
|
|
// 多加入的一个反斜杆会作为文本节点保留下来,后续 Spin 时刚好用于转义标记符
|
|
|
|
|
|
clipText = clipText.replace(/\\/g, "\\\\")
|
|
|
|
|
|
.replace(/\*/g, "\\*")
|
|
|
|
|
|
.replace(/_/g, "\\_")
|
|
|
|
|
|
.replace(/\[/g, "\\[")
|
|
|
|
|
|
.replace(/]/g, "\\]")
|
|
|
|
|
|
.replace(/!/g, "\\!")
|
|
|
|
|
|
.replace(/`/g, "\\`")
|
|
|
|
|
|
.replace(/</g, "\\<")
|
|
|
|
|
|
.replace(/>/g, "\\>")
|
|
|
|
|
|
.replace(/&/g, "\\&")
|
|
|
|
|
|
.replace(/~/g, "\\~")
|
|
|
|
|
|
.replace(/\{/g, "\\{")
|
|
|
|
|
|
.replace(/}/g, "\\}")
|
|
|
|
|
|
.replace(/\(/g, "\\(")
|
|
|
|
|
|
.replace(/\)/g, "\\)")
|
|
|
|
|
|
.replace(/=/g, "\\=")
|
|
|
|
|
|
.replace(/#/g, "\\#")
|
|
|
|
|
|
.replace(/\$/g, "\\$")
|
|
|
|
|
|
.replace(/\^/g, "\\^")
|
2024-03-24 09:25:05 +08:00
|
|
|
|
.replace(/\|/g, "\\|")
|
|
|
|
|
|
.replace(/\./g, "\\.");
|
2023-08-04 14:16:47 +08:00
|
|
|
|
pasteText(protyle, clipText, nodeElement);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.log(e);
|
|
|
|
|
|
}
|
2023-08-07 12:38:00 +08:00
|
|
|
|
};
|
2023-08-04 14:16:47 +08:00
|
|
|
|
|
2022-07-08 09:24:54 +08:00
|
|
|
|
const filterClipboardHint = (protyle: IProtyle, textPlain: string) => {
|
|
|
|
|
|
let needRender = true;
|
|
|
|
|
|
protyle.options.hint.extend.find(item => {
|
|
|
|
|
|
if (item.key === textPlain) {
|
|
|
|
|
|
needRender = false;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2022-07-08 10:38:11 +08:00
|
|
|
|
});
|
2022-07-08 09:24:54 +08:00
|
|
|
|
if (needRender) {
|
|
|
|
|
|
protyle.hint.render(protyle);
|
|
|
|
|
|
}
|
2022-07-08 10:38:11 +08:00
|
|
|
|
};
|
2022-07-08 09:24:54 +08:00
|
|
|
|
|
2022-11-08 18:53:03 +08:00
|
|
|
|
export const pasteAsPlainText = async (protyle: IProtyle) => {
|
|
|
|
|
|
let localFiles: string[] = [];
|
2023-04-17 17:51:45 +08:00
|
|
|
|
/// #if !BROWSER
|
2022-11-08 18:53:03 +08:00
|
|
|
|
if ("darwin" === window.siyuan.config.system.os) {
|
|
|
|
|
|
const xmlString = clipboard.read("NSFilenamesPboardType");
|
|
|
|
|
|
const domParser = new DOMParser();
|
|
|
|
|
|
const xmlDom = domParser.parseFromString(xmlString, "application/xml");
|
|
|
|
|
|
Array.from(xmlDom.getElementsByTagName("string")).forEach(item => {
|
|
|
|
|
|
localFiles.push(item.childNodes[0].nodeValue);
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
const xmlString = await fetchSyncPost("/api/clipboard/readFilePaths", {});
|
|
|
|
|
|
if (xmlString.data.length > 0) {
|
|
|
|
|
|
localFiles = xmlString.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (localFiles.length > 0) {
|
|
|
|
|
|
uploadLocalFiles(localFiles, protyle, false);
|
|
|
|
|
|
writeText("");
|
|
|
|
|
|
}
|
|
|
|
|
|
/// #endif
|
2023-04-17 17:51:45 +08:00
|
|
|
|
if (localFiles.length === 0) {
|
2023-05-17 21:48:01 +08:00
|
|
|
|
// Inline-level elements support pasted as plain text https://github.com/siyuan-note/siyuan/issues/8010
|
2023-04-18 18:18:10 +08:00
|
|
|
|
navigator.clipboard.readText().then(textPlain => {
|
2024-04-05 22:03:25 +08:00
|
|
|
|
// 对 HTML 标签进行内部转义,避免被 Lute 解析以后变为小写 https://github.com/siyuan-note/siyuan/issues/10620
|
2024-03-15 20:48:08 +08:00
|
|
|
|
textPlain = textPlain.replace(/</g, ";;;lt;;;").replace(/>/g, ";;;gt;;;");
|
|
|
|
|
|
const content = protyle.lute.BlockDOM2EscapeMarkerContent(protyle.lute.Md2BlockDOM(textPlain));
|
2024-04-05 22:03:25 +08:00
|
|
|
|
// insertHTML 会进行内部反转义
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(content, protyle, false, false, true);
|
2023-06-09 22:29:15 +08:00
|
|
|
|
filterClipboardHint(protyle, textPlain);
|
2023-04-18 18:18:10 +08:00
|
|
|
|
});
|
2023-04-17 17:51:45 +08:00
|
|
|
|
}
|
2022-11-08 18:53:03 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
export const pasteText = (protyle: IProtyle, textPlain: string, nodeElement: Element) => {
|
|
|
|
|
|
const range = getEditorRange(protyle.wysiwyg.element);
|
|
|
|
|
|
if (nodeElement.getAttribute("data-type") === "NodeCodeBlock") {
|
|
|
|
|
|
// 粘贴在代码位置
|
2022-10-24 21:49:28 +08:00
|
|
|
|
insertHTML(textPlain, protyle);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (range.toString() !== "") {
|
|
|
|
|
|
if (isDynamicRef(textPlain)) {
|
|
|
|
|
|
textPlain = textPlain.replace(/'.+'\)\)$/, ` "${range.toString()}"))`);
|
|
|
|
|
|
} else if (isFileAnnotation(textPlain)) {
|
|
|
|
|
|
textPlain = textPlain.replace(/".+">>$/, `"${range.toString()}">>`);
|
2024-04-24 22:35:23 +08:00
|
|
|
|
} else {
|
2024-04-25 15:56:06 +08:00
|
|
|
|
const linkDest = protyle.lute.GetLinkDest(textPlain);
|
2024-04-24 22:35:23 +08:00
|
|
|
|
if (linkDest) {
|
|
|
|
|
|
textPlain = `[${range.toString()}](${linkDest})`;
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(protyle.lute.Md2BlockDOM(textPlain), protyle, false, false, true);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
|
|
|
|
|
|
blockRender(protyle, protyle.wysiwyg.element);
|
|
|
|
|
|
processRender(protyle.wysiwyg.element);
|
|
|
|
|
|
highlightRender(protyle.wysiwyg.element);
|
2023-09-09 23:21:46 +08:00
|
|
|
|
avRender(protyle.wysiwyg.element, protyle);
|
2022-07-08 10:38:11 +08:00
|
|
|
|
filterClipboardHint(protyle, textPlain);
|
2023-03-20 21:02:37 +08:00
|
|
|
|
scrollCenter(protyle, undefined, false, "smooth");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
export const paste = async (protyle: IProtyle, event: (ClipboardEvent | DragEvent) & { target: HTMLElement }) => {
|
|
|
|
|
|
event.stopPropagation();
|
|
|
|
|
|
event.preventDefault();
|
2023-10-20 11:45:39 +08:00
|
|
|
|
let textHTML: string;
|
|
|
|
|
|
let textPlain: string;
|
|
|
|
|
|
let siyuanHTML: string;
|
|
|
|
|
|
let files: FileList | DataTransferItemList;
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if ("clipboardData" in event) {
|
|
|
|
|
|
textHTML = event.clipboardData.getData("text/html");
|
|
|
|
|
|
textPlain = event.clipboardData.getData("text/plain");
|
2023-02-02 21:51:30 +08:00
|
|
|
|
siyuanHTML = event.clipboardData.getData("text/siyuan");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
files = event.clipboardData.files;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
textHTML = event.dataTransfer.getData("text/html");
|
|
|
|
|
|
textPlain = event.dataTransfer.getData("text/plain");
|
2023-02-02 21:51:30 +08:00
|
|
|
|
siyuanHTML = event.dataTransfer.getData("text/siyuan");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (event.dataTransfer.types[0] === "Files") {
|
|
|
|
|
|
files = event.dataTransfer.items;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-31 21:05:45 +08:00
|
|
|
|
/// #if !BROWSER
|
|
|
|
|
|
// 不再支持 PC 浏览器 https://github.com/siyuan-note/siyuan/issues/7206
|
2023-02-02 21:51:30 +08:00
|
|
|
|
if (!siyuanHTML && !textHTML && !textPlain && ("clipboardData" in event)) {
|
2022-08-23 20:57:39 +08:00
|
|
|
|
if ("darwin" === window.siyuan.config.system.os) {
|
|
|
|
|
|
const xmlString = clipboard.read("NSFilenamesPboardType");
|
|
|
|
|
|
const domParser = new DOMParser();
|
|
|
|
|
|
const xmlDom = domParser.parseFromString(xmlString, "application/xml");
|
|
|
|
|
|
const localFiles: string[] = [];
|
|
|
|
|
|
Array.from(xmlDom.getElementsByTagName("string")).forEach(item => {
|
|
|
|
|
|
localFiles.push(item.childNodes[0].nodeValue);
|
|
|
|
|
|
});
|
2022-08-26 23:49:07 +08:00
|
|
|
|
if (localFiles.length > 0) {
|
2022-09-27 14:57:25 +08:00
|
|
|
|
uploadLocalFiles(localFiles, protyle, true);
|
2022-08-26 23:49:07 +08:00
|
|
|
|
writeText("");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-08-23 20:57:39 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
const xmlString = await fetchSyncPost("/api/clipboard/readFilePaths", {});
|
|
|
|
|
|
if (xmlString.data.length > 0) {
|
2022-09-27 14:57:25 +08:00
|
|
|
|
uploadLocalFiles(xmlString.data, protyle, true);
|
2022-08-23 20:57:39 +08:00
|
|
|
|
writeText("");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/// #endif
|
|
|
|
|
|
|
|
|
|
|
|
// 浏览器地址栏拷贝处理
|
|
|
|
|
|
if (textHTML.replace(/&/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
|
|
|
|
|
|
`<a href="${textPlain}">${textPlain}</a>` ||
|
|
|
|
|
|
textHTML.replace(/&/g, "&").replace(/<(|\/)(html|body|meta)[^>]*?>/ig, "").trim() ===
|
|
|
|
|
|
`<!--StartFragment--><a href="${textPlain}">${textPlain}</a><!--EndFragment-->`) {
|
|
|
|
|
|
textHTML = "";
|
|
|
|
|
|
}
|
2023-02-02 21:51:30 +08:00
|
|
|
|
// 复制标题及其下方块使用 writeText,需将 textPlain 转换为 textHTML
|
|
|
|
|
|
if (textPlain.endsWith(Constants.ZWSP) && !textHTML && !siyuanHTML) {
|
|
|
|
|
|
siyuanHTML = textPlain.substr(0, textPlain.length - 1);
|
|
|
|
|
|
}
|
2022-08-18 12:36:24 +08:00
|
|
|
|
// 剪切复制中首位包含空格或仅有空格 https://github.com/siyuan-note/siyuan/issues/5667
|
2023-02-02 21:51:30 +08:00
|
|
|
|
if (!siyuanHTML) {
|
2022-08-18 12:32:49 +08:00
|
|
|
|
// process word
|
|
|
|
|
|
const doc = new DOMParser().parseFromString(textHTML, "text/html");
|
|
|
|
|
|
if (doc.body && doc.body.innerHTML) {
|
|
|
|
|
|
textHTML = doc.body.innerHTML;
|
|
|
|
|
|
}
|
2022-08-18 19:39:53 +08:00
|
|
|
|
// windows 剪切板
|
|
|
|
|
|
if (textHTML.startsWith("\n<!--StartFragment-->") && textHTML.endsWith("<!--EndFragment-->\n\n")) {
|
|
|
|
|
|
textHTML = doc.body.innerHTML.trim().replace("<!--StartFragment-->", "").replace("<!--EndFragment-->", "");
|
|
|
|
|
|
}
|
2023-02-02 21:51:30 +08:00
|
|
|
|
textHTML = Lute.Sanitize(textHTML);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
2022-08-18 12:32:49 +08:00
|
|
|
|
|
2023-10-20 11:45:39 +08:00
|
|
|
|
if (protyle && protyle.app && protyle.app.plugins) {
|
|
|
|
|
|
for (let i = 0; i < protyle.app.plugins.length; i++) {
|
|
|
|
|
|
const response: IObject & { files: FileList } = await new Promise((resolve) => {
|
|
|
|
|
|
const emitResult = protyle.app.plugins[i].eventBus.emit("paste", {
|
|
|
|
|
|
protyle,
|
|
|
|
|
|
resolve,
|
|
|
|
|
|
textHTML,
|
|
|
|
|
|
textPlain,
|
|
|
|
|
|
siyuanHTML,
|
|
|
|
|
|
files
|
|
|
|
|
|
});
|
|
|
|
|
|
if (emitResult) {
|
|
|
|
|
|
resolve(undefined);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (response?.textHTML) {
|
|
|
|
|
|
textHTML = response.textHTML;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (response?.textPlain) {
|
|
|
|
|
|
textPlain = response.textPlain;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (response?.siyuanHTML) {
|
|
|
|
|
|
siyuanHTML = response.siyuanHTML;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (response?.files) {
|
|
|
|
|
|
files = response.files as FileList;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-26 15:18:53 +08:00
|
|
|
|
const nodeElement = hasClosestBlock(event.target);
|
|
|
|
|
|
if (!nodeElement) {
|
2022-06-03 11:42:54 +08:00
|
|
|
|
if (files && files.length > 0) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
uploadFiles(protyle, files);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-10-22 11:31:41 +08:00
|
|
|
|
hideElements(["select"], protyle);
|
|
|
|
|
|
protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--hl").forEach(item => {
|
|
|
|
|
|
item.classList.remove("protyle-wysiwyg--hl");
|
2022-05-26 15:18:53 +08:00
|
|
|
|
});
|
|
|
|
|
|
const code = processPasteCode(textHTML, textPlain);
|
|
|
|
|
|
const range = getEditorRange(protyle.wysiwyg.element);
|
2023-04-22 17:34:27 +08:00
|
|
|
|
if (nodeElement.getAttribute("data-type") === "NodeCodeBlock" ||
|
|
|
|
|
|
protyle.toolbar.getCurrentType(range).includes("code")) {
|
2023-02-08 14:28:15 +08:00
|
|
|
|
// 粘贴在代码位置
|
2023-09-08 23:12:54 +08:00
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/9142
|
2023-09-30 20:30:06 +08:00
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/9323
|
2023-10-09 10:07:54 +08:00
|
|
|
|
// 需排除行内代码 https://github.com/siyuan-note/siyuan/issues/9369
|
|
|
|
|
|
if (nodeElement.querySelector(".protyle-action")?.contains(range.startContainer)) {
|
2023-09-08 23:12:54 +08:00
|
|
|
|
range.setStart(nodeElement.querySelector(".hljs").firstChild, 0);
|
|
|
|
|
|
}
|
2023-02-08 14:28:15 +08:00
|
|
|
|
insertHTML(textPlain, protyle);
|
|
|
|
|
|
return;
|
|
|
|
|
|
} else if (siyuanHTML) {
|
2023-02-02 21:51:30 +08:00
|
|
|
|
// 编辑器内部粘贴
|
2023-02-02 23:16:18 +08:00
|
|
|
|
const tempElement = document.createElement("div");
|
|
|
|
|
|
tempElement.innerHTML = siyuanHTML;
|
2023-02-02 21:51:30 +08:00
|
|
|
|
let isBlock = false;
|
|
|
|
|
|
tempElement.querySelectorAll("[data-node-id]").forEach((e) => {
|
|
|
|
|
|
const newId = Lute.NewNodeID();
|
|
|
|
|
|
e.setAttribute("data-node-id", newId);
|
2023-09-09 14:54:05 +08:00
|
|
|
|
e.removeAttribute(Constants.CUSTOM_RIFF_DECKS);
|
2023-02-02 21:51:30 +08:00
|
|
|
|
e.classList.remove("protyle-wysiwyg--select", "protyle-wysiwyg--hl");
|
|
|
|
|
|
e.setAttribute("updated", newId.split("-")[0]);
|
|
|
|
|
|
isBlock = true;
|
|
|
|
|
|
});
|
|
|
|
|
|
if (nodeElement.classList.contains("table")) {
|
|
|
|
|
|
isBlock = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 从历史中复制后粘贴
|
|
|
|
|
|
tempElement.querySelectorAll('[contenteditable="false"][spellcheck]').forEach((e) => {
|
|
|
|
|
|
e.setAttribute("contenteditable", "true");
|
|
|
|
|
|
});
|
|
|
|
|
|
const tempInnerHTML = tempElement.innerHTML;
|
2024-06-10 17:14:22 +08:00
|
|
|
|
if (!nodeElement.classList.contains("av") && tempInnerHTML.startsWith("[[{") && tempInnerHTML.endsWith("}]]")) {
|
2024-01-31 11:02:32 +08:00
|
|
|
|
try {
|
|
|
|
|
|
const json = JSON.parse(tempInnerHTML);
|
2024-06-10 17:14:22 +08:00
|
|
|
|
if (json.length > 0 && json[0].length > 0 && json[0][0].id && json[0][0].type) {
|
2024-01-31 11:02:32 +08:00
|
|
|
|
insertHTML(textPlain, protyle, isBlock);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
insertHTML(tempInnerHTML, protyle, isBlock);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
insertHTML(tempInnerHTML, protyle, isBlock);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(tempInnerHTML, protyle, isBlock, false, true);
|
2024-01-31 11:02:32 +08:00
|
|
|
|
}
|
2023-02-14 09:38:41 +08:00
|
|
|
|
filterClipboardHint(protyle, protyle.lute.BlockDOM2StdMd(tempInnerHTML));
|
2023-02-08 14:58:53 +08:00
|
|
|
|
blockRender(protyle, protyle.wysiwyg.element);
|
|
|
|
|
|
processRender(protyle.wysiwyg.element);
|
|
|
|
|
|
highlightRender(protyle.wysiwyg.element);
|
2023-09-09 23:21:46 +08:00
|
|
|
|
avRender(protyle.wysiwyg.element, protyle);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
} else if (code) {
|
|
|
|
|
|
if (!code.startsWith('<div data-type="NodeCodeBlock" class="code-block" data-node-id="')) {
|
2023-04-22 17:34:27 +08:00
|
|
|
|
// 原有代码在行内元素中粘贴会嵌套
|
|
|
|
|
|
insertHTML(code, protyle);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
} else {
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(code, protyle, true, false, true);
|
2022-08-31 21:57:12 +08:00
|
|
|
|
highlightRender(protyle.wysiwyg.element);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
2023-02-17 15:46:46 +08:00
|
|
|
|
hideElements(["hint"], protyle);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
let isHTML = false;
|
2023-02-08 14:58:53 +08:00
|
|
|
|
if (textHTML.replace("<!--StartFragment--><!--EndFragment-->", "").trim() !== "") {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
textHTML = textHTML.replace("<!--StartFragment-->", "").replace("<!--EndFragment-->", "").trim();
|
2023-01-09 21:03:41 +08:00
|
|
|
|
if (files && files.length === 1 && (
|
|
|
|
|
|
textHTML.startsWith("<img") || // 浏览器上复制单个图片
|
2023-01-09 22:00:40 +08:00
|
|
|
|
(textHTML.startsWith("<table") && textHTML.indexOf("<img") > -1) // Excel 或者浏览器中复制带有图片的表格
|
2023-01-09 21:03:41 +08:00
|
|
|
|
)) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
isHTML = false;
|
|
|
|
|
|
} else {
|
2023-01-09 21:03:41 +08:00
|
|
|
|
// 需注意 Edge 中的画选不应识别为图片 https://github.com/siyuan-note/siyuan/issues/7021
|
2022-05-26 15:18:53 +08:00
|
|
|
|
isHTML = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (isHTML) {
|
|
|
|
|
|
const tempElement = document.createElement("div");
|
2023-02-02 21:51:30 +08:00
|
|
|
|
tempElement.innerHTML = textHTML;
|
|
|
|
|
|
tempElement.querySelectorAll("[style]").forEach((e) => {
|
|
|
|
|
|
e.removeAttribute("style");
|
|
|
|
|
|
});
|
|
|
|
|
|
// 移除空的 A 标签
|
|
|
|
|
|
tempElement.querySelectorAll("a").forEach((e) => {
|
|
|
|
|
|
if (e.innerHTML.trim() === "") {
|
|
|
|
|
|
e.remove();
|
2022-11-14 23:14:44 +08:00
|
|
|
|
}
|
2023-02-02 21:51:30 +08:00
|
|
|
|
});
|
|
|
|
|
|
fetchPost("/api/lute/html2BlockDOM", {
|
|
|
|
|
|
dom: tempElement.innerHTML
|
|
|
|
|
|
}, (response) => {
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(response.data, protyle, false, false, true);
|
2023-02-02 21:51:30 +08:00
|
|
|
|
protyle.wysiwyg.element.querySelectorAll('[data-type~="block-ref"]').forEach(item => {
|
|
|
|
|
|
if (item.textContent === "") {
|
|
|
|
|
|
fetchPost("/api/block/getRefText", {id: item.getAttribute("data-id")}, (response) => {
|
|
|
|
|
|
item.innerHTML = response.data;
|
|
|
|
|
|
});
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
2023-02-02 21:51:30 +08:00
|
|
|
|
blockRender(protyle, protyle.wysiwyg.element);
|
|
|
|
|
|
processRender(protyle.wysiwyg.element);
|
|
|
|
|
|
highlightRender(protyle.wysiwyg.element);
|
2023-09-09 23:21:46 +08:00
|
|
|
|
avRender(protyle.wysiwyg.element, protyle);
|
2023-02-02 21:51:30 +08:00
|
|
|
|
filterClipboardHint(protyle, response.data);
|
2023-03-20 21:02:37 +08:00
|
|
|
|
scrollCenter(protyle, undefined, false, "smooth");
|
2023-02-02 21:51:30 +08:00
|
|
|
|
});
|
|
|
|
|
|
return;
|
2022-06-03 11:42:54 +08:00
|
|
|
|
} else if (files && files.length > 0) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
uploadFiles(protyle, files);
|
2022-06-03 11:42:54 +08:00
|
|
|
|
} else if (textPlain.trim() !== "" && files && files.length === 0) {
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (range.toString() !== "") {
|
2023-09-19 10:05:10 +08:00
|
|
|
|
const firstLine = textPlain.split("\n")[0];
|
2022-05-26 15:18:53 +08:00
|
|
|
|
if (isDynamicRef(textPlain)) {
|
2023-05-06 19:36:54 +08:00
|
|
|
|
protyle.toolbar.setInlineMark(protyle, "block-ref", "range", {
|
|
|
|
|
|
type: "id",
|
2023-05-26 22:44:33 +08:00
|
|
|
|
// range 不能 escape,否则 https://github.com/siyuan-note/siyuan/issues/8359
|
|
|
|
|
|
color: `${textPlain.substring(2, 22 + 2)}${Constants.ZWSP}s${Constants.ZWSP}${range.toString()}`
|
2023-05-06 19:36:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
return;
|
2023-09-19 10:05:10 +08:00
|
|
|
|
} else if (isFileAnnotation(firstLine)) {
|
2023-05-06 19:36:54 +08:00
|
|
|
|
protyle.toolbar.setInlineMark(protyle, "file-annotation-ref", "range", {
|
|
|
|
|
|
type: "file-annotation-ref",
|
2023-09-19 10:05:10 +08:00
|
|
|
|
color: firstLine.substring(2).replace(/ ".+">>$/, "")
|
2023-05-06 19:36:54 +08:00
|
|
|
|
});
|
|
|
|
|
|
return;
|
2024-04-24 22:35:23 +08:00
|
|
|
|
} else {
|
2023-06-07 10:19:38 +08:00
|
|
|
|
// https://github.com/siyuan-note/siyuan/issues/8475
|
2024-04-25 15:56:06 +08:00
|
|
|
|
const linkDest = protyle.lute.GetLinkDest(textPlain);
|
2024-04-24 22:35:23 +08:00
|
|
|
|
if (linkDest) {
|
|
|
|
|
|
protyle.toolbar.setInlineMark(protyle, "a", "range", {
|
|
|
|
|
|
type: "a",
|
|
|
|
|
|
color: linkDest
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
const textPlainDom = protyle.lute.Md2BlockDOM(textPlain);
|
2024-06-10 17:14:22 +08:00
|
|
|
|
insertHTML(textPlainDom, protyle, false, false, true);
|
2023-02-14 09:38:41 +08:00
|
|
|
|
filterClipboardHint(protyle, textPlain);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
|
|
|
|
|
blockRender(protyle, protyle.wysiwyg.element);
|
|
|
|
|
|
processRender(protyle.wysiwyg.element);
|
|
|
|
|
|
highlightRender(protyle.wysiwyg.element);
|
2023-09-09 23:21:46 +08:00
|
|
|
|
avRender(protyle.wysiwyg.element, protyle);
|
2022-05-26 15:18:53 +08:00
|
|
|
|
}
|
2024-01-04 17:05:24 +08:00
|
|
|
|
const selectCellElement = nodeElement.querySelector(".av__cell--select");
|
|
|
|
|
|
if (nodeElement.classList.contains("av") && selectCellElement) {
|
|
|
|
|
|
cellScrollIntoView(nodeElement, selectCellElement);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
scrollCenter(protyle, undefined, false, "smooth");
|
|
|
|
|
|
}
|
2022-05-26 15:18:53 +08:00
|
|
|
|
};
|