siyuan/app/src/protyle/wysiwyg/index.ts

2838 lines
161 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import {enableLuteMarkdownSyntax, getTextStar, paste, restoreLuteMarkdownSyntax} from "../util/paste";
import {
hasClosestBlock,
hasClosestByAttribute,
hasClosestByClassName,
hasClosestByTag,
hasTopClosestByClassName,
isInEmbedBlock,
} from "../util/hasClosest";
import {
focusBlock,
focusByRange,
focusByWbr,
focusSideBlock,
getEditorRange,
getSelectionOffset,
setFirstNodeRange,
setInsertWbrHTML,
setLastNodeRange,
} from "../util/selection";
import {Constants} from "../../constants";
import {isMobile} from "../../util/functions";
import {previewDocImage} from "../preview/image";
import {
contentMenu,
enterBack,
fileAnnotationRefMenu,
imgMenu,
inlineMathMenu,
linkMenu,
refMenu,
setFold,
tagMenu,
zoomOut
} from "../../menus/protyle";
import * as dayjs from "dayjs";
import {dropEvent} from "../util/editorCommonEvent";
import {input} from "./input";
import {
getContenteditableElement,
getNextBlock,
getTopAloneElement,
hasNextSibling,
hasPreviousSibling,
isEndOfBlock,
isNotEditBlock
} from "./getBlock";
import {transaction, updateTransaction} from "./transaction";
import {hideElements} from "../ui/hideElements";
/// #if !BROWSER
import {ipcRenderer} from "electron";
/// #endif
import {getEnableHTML, removeEmbed} from "./removeEmbed";
import {keydown} from "./keydown";
import {openMobileFileById} from "../../mobile/editor";
import {removeBlock} from "./remove";
import {highlightRender} from "../render/highlightRender";
import {openAttr} from "../../menus/commonMenuItem";
import {blockRender} from "../render/blockRender";
/// #if !MOBILE
import {getAllModels} from "../../layout/getAll";
import {pushBack} from "../../util/backForward";
import {openFileById} from "../../editor/util";
import {openGlobalSearch} from "../../search/util";
/// #else
import {popSearch} from "../../mobile/menu/search";
/// #endif
import {BlockPanel} from "../../block/Panel";
import {copyPlainText, isInIOS, isMac, isOnlyMeta, readClipboard} from "../util/compatibility";
import {MenuItem} from "../../menus/Menu";
import {fetchPost} from "../../util/fetch";
import {onGet} from "../util/onGet";
import {clearTableCell, setTableAlign} from "../util/table";
import {countBlockWord, countSelectWord} from "../../layout/status";
import {showMessage} from "../../dialog/message";
import {getBacklinkHeadingMore, loadBreadcrumb} from "./renderBacklink";
import {removeSearchMark} from "../toolbar/util";
import {activeBlur} from "../../mobile/util/keyboardToolbar";
import {commonClick} from "./commonClick";
import {avClick, avContextmenu, updateAVName} from "../render/av/action";
import {selectRow, stickyRow, updateHeader} from "../render/av/row";
import {showColMenu} from "../render/av/col";
import {openViewMenu} from "../render/av/view";
import {avRender} from "../render/av/render";
import {checkFold} from "../../util/noRelyPCFunction";
import {
addDragFill,
dragFillCellsValue,
genCellValueByElement,
getCellText,
getPositionByCellElement,
getTypeByCellElement,
updateCellsValue
} from "../render/av/cell";
import {openEmojiPanel, unicode2Emoji} from "../../emoji";
import {openLink} from "../../editor/openLink";
import {mathRender} from "../render/mathRender";
import {editAssetItem} from "../render/av/asset";
import {img3115} from "../../boot/compatibleVersion";
import {globalClickHideMenu} from "../../boot/globalEvent/click";
import {hideTooltip} from "../../dialog/tooltip";
export class WYSIWYG {
public lastHTMLs: { [key: string]: string } = {};
public element: HTMLDivElement;
public preventKeyup: boolean;
private shiftStartElement: HTMLElement;
private preventClick: boolean;
constructor(protyle: IProtyle) {
this.element = document.createElement("div");
this.element.className = "protyle-wysiwyg";
this.element.setAttribute("spellcheck", "false");
if (isMobile()) {
// iPhoneiPad 端输入 contenteditable 为 true 时会在块中间插入 span
// Android 端空块输入法弹出会收起 https://ld246.com/article/1689713888289
this.element.setAttribute("contenteditable", "false");
} else {
this.element.setAttribute("contenteditable", "true");
}
if (window.siyuan.config.editor.displayBookmarkIcon) {
this.element.classList.add("protyle-wysiwyg--attr");
}
this.bindCommonEvent(protyle);
if (protyle.options.action.includes(Constants.CB_GET_HISTORY)) {
return;
}
this.bindEvent(protyle);
keydown(protyle, this.element);
dropEvent(protyle, this.element);
}
public renderCustom(ial: IObject) {
let isFullWidth = ial[Constants.CUSTOM_SY_FULLWIDTH];
if (!isFullWidth) {
isFullWidth = window.siyuan.config.editor.fullWidth ? "true" : "false";
}
if (isFullWidth === "true") {
this.element.parentElement.setAttribute("data-fullwidth", "true");
} else {
this.element.parentElement.removeAttribute("data-fullwidth");
}
const ialKeys = Object.keys(ial);
for (let i = 0; i < this.element.attributes.length; i++) {
const oldKey = this.element.attributes[i].nodeName;
if (!["type", "class", "spellcheck", "contenteditable", "data-doc-type", "style", "data-realwidth", "data-readonly"].includes(oldKey) &&
!ialKeys.includes(oldKey)) {
this.element.removeAttribute(oldKey);
i--;
}
}
ialKeys.forEach((key: string) => {
if (!["title-img", "title", "updated", "icon", "id", "type", "class", "spellcheck", "contenteditable", "data-doc-type", "style", "data-realwidth", "data-readonly"].includes(key)) {
this.element.setAttribute(key, ial[key]);
}
});
}
// text block-ref file-annotation-ref a 结尾处打字应为普通文本
private escapeInline(protyle: IProtyle, range: Range, event: InputEvent) {
if (!event.data && event.inputType !== "insertLineBreak") {
return;
}
const inputData = event.data;
protyle.toolbar.range = range;
const inlineElement = range.startContainer.parentElement;
const currentTypes = protyle.toolbar.getCurrentType();
// https://github.com/siyuan-note/siyuan/issues/11766
if (event.inputType === "insertLineBreak") {
if (currentTypes.length > 0 && range.toString() === "" && inlineElement.tagName === "SPAN" &&
inlineElement.textContent.startsWith("\n") &&
range.startContainer.previousSibling && range.startContainer.previousSibling.textContent === "\n") {
inlineElement.before(range.startContainer.previousSibling);
}
return;
}
let dataLength = inputData.length;
if (inputData === "<" || inputData === ">") {
// 使用 inlineElement.innerHTML 会出现 https://ld246.com/article/1627185027423 中的第2个问题
dataLength = 4;
} else if (inputData === "&") {
// https://github.com/siyuan-note/siyuan/issues/12239
dataLength = 5;
}
// https://github.com/siyuan-note/siyuan/issues/5924
if (currentTypes.length > 0 && range.toString() === "" && range.startOffset === inputData.length &&
inlineElement.tagName === "SPAN" &&
inlineElement.textContent.replace(Constants.ZWSP, "") !== inputData &&
inlineElement.textContent.replace(Constants.ZWSP, "").length >= inputData.length &&
!hasPreviousSibling(range.startContainer) && !hasPreviousSibling(inlineElement)) {
const html = inlineElement.innerHTML.replace(Constants.ZWSP, "");
inlineElement.innerHTML = html.substr(dataLength);
const textNode = document.createTextNode(inputData);
inlineElement.before(textNode);
range.selectNodeContents(textNode);
range.collapse(false);
return;
}
if (// 表格行内公式之前无法插入文字 https://github.com/siyuan-note/siyuan/issues/3908
inlineElement.tagName === "SPAN" &&
inlineElement.textContent !== inputData &&
!currentTypes.includes("search-mark") && // https://github.com/siyuan-note/siyuan/issues/7586
!currentTypes.includes("code") && // https://github.com/siyuan-note/siyuan/issues/13871
!currentTypes.includes("kbd") &&
!currentTypes.includes("tag") &&
range.toString() === "" && range.startContainer.nodeType === 3 &&
(currentTypes.includes("inline-memo") || currentTypes.includes("block-ref") || currentTypes.includes("file-annotation-ref") || currentTypes.includes("a")) &&
!hasNextSibling(range.startContainer) && range.startContainer.textContent.length === range.startOffset &&
inlineElement.textContent.length > inputData.length
) {
const position = getSelectionOffset(inlineElement, protyle.wysiwyg.element, range);
const html = inlineElement.innerHTML;
if (position.start === inlineElement.textContent.length) {
// 使用 inlineElement.textContent **$a$b** 中数学公式消失
inlineElement.innerHTML = html.substr(0, html.length - dataLength);
const textNode = document.createTextNode(inputData);
inlineElement.after(textNode);
range.selectNodeContents(textNode);
range.collapse(false);
}
}
}
private setEmptyOutline(protyle: IProtyle, element: HTMLElement) {
// 图片移除选择状态应放在前面,否则 https://github.com/siyuan-note/siyuan/issues/4173
protyle.wysiwyg.element.querySelectorAll(".img--select, .av__cell--select, .av__cell--active, .av__row--select").forEach((item: HTMLElement) => {
if (item.classList.contains("av__row--select") && !hasClosestByClassName(element, "av")) {
item.classList.remove("av__row--select");
item.querySelector(".av__firstcol use").setAttribute("xlink:href", "#iconUncheck");
updateHeader(item);
} else {
item.classList.remove("img--select", "av__cell--select", "av__cell--active");
item.querySelector(".av__drag-fill")?.remove();
}
});
let nodeElement = element;
if (!element.getAttribute("data-node-id")) {
const tempElement = hasClosestBlock(element);
if (!tempElement) {
return;
}
nodeElement = tempElement;
}
/// #if !MOBILE
if (protyle.model) {
getAllModels().outline.forEach(item => {
if (item.blockId === protyle.block.rootID) {
item.setCurrent(nodeElement);
}
});
}
/// #else
if (protyle.disabled) {
protyle.toolbar.range = getEditorRange(nodeElement);
}
/// #endif
}
private emojiToMd(element: HTMLElement) {
element.querySelectorAll(".emoji").forEach((item: HTMLElement) => {
item.outerHTML = `:${item.getAttribute("alt")}:`;
});
}
private bindCommonEvent(protyle: IProtyle) {
this.element.addEventListener("copy", (event: ClipboardEvent & { target: HTMLElement }) => {
window.siyuan.ctrlIsPressed = false; // https://github.com/siyuan-note/siyuan/issues/6373
// https://github.com/siyuan-note/siyuan/issues/4600
if (event.target.tagName === "PROTYLE-HTML" || event.target.localName === "input") {
event.stopPropagation();
return;
}
event.stopPropagation();
event.preventDefault();
const range = getEditorRange(protyle.wysiwyg.element);
const nodeElement = hasClosestBlock(range.startContainer);
if (!nodeElement) {
return;
}
const selectImgElement = nodeElement.querySelector(".img--select");
const selectAVElement = nodeElement.querySelector(".av__row--select, .av__cell--select");
const selectTableElement = nodeElement.querySelector(".table__select")?.clientWidth > 0;
let selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
if (selectElements.length === 0 && range.toString() === "" && !range.cloneContents().querySelector("img") &&
!selectImgElement && !selectAVElement && !selectTableElement) {
nodeElement.classList.add("protyle-wysiwyg--select");
countBlockWord([nodeElement.getAttribute("data-node-id")]);
selectElements = [nodeElement];
}
let html = "";
let textPlain = "";
if (selectElements.length > 0) {
const isRefText = selectElements[0].getAttribute("data-reftext") === "true";
if (selectElements[0].getAttribute("data-type") === "NodeListItem" &&
selectElements[0].parentElement.classList.contains("list") && // 反链复制列表项 https://github.com/siyuan-note/siyuan/issues/6555
selectElements[0].parentElement.childElementCount - 1 === selectElements.length) {
if (isRefText) {
html = getTextStar(selectElements[0].parentElement);
} else {
html = selectElements[0].parentElement.outerHTML;
}
} else {
selectElements.forEach((item: HTMLElement, index) => {
// 复制列表项中的块会变为复制列表项,因此不能使用 getTopAloneElement https://github.com/siyuan-note/siyuan/issues/8925
if (isRefText && index === 0) {
html += getTextStar(item) + "\n\n";
} else {
html += removeEmbed(item);
}
});
}
if (isRefText) {
selectElements[0].removeAttribute("data-reftext");
}
} else if (selectAVElement) {
const cellElements: Element[] = Array.from(nodeElement.querySelectorAll(".av__cell--active, .av__cell--select")) || [];
if (cellElements.length === 0) {
nodeElement.querySelectorAll(".av__row--select:not(.av__row--header)").forEach(rowElement => {
rowElement.querySelectorAll(".av__cell").forEach(cellElement => {
cellElements.push(cellElement);
});
});
}
if (cellElements.length > 0) {
html = "[";
cellElements.forEach((item: HTMLElement, index) => {
const cellText = getCellText(item);
if (index === 0 || !cellElements[index - 1].isSameNode(item.previousElementSibling)) {
html += "[";
}
html += JSON.stringify(genCellValueByElement(getTypeByCellElement(item), item)) + ",";
if (index === cellElements.length - 1 || !cellElements[index + 1].isSameNode(item.nextElementSibling)) {
html = html.substring(0, html.length - 1) + "],";
}
textPlain += cellText + ((cellElements[index + 1] && item.nextElementSibling && item.nextElementSibling.isSameNode(cellElements[index + 1])) ? "\t" : "\n\n");
});
textPlain = textPlain.substring(0, textPlain.length - 2);
html = html.substring(0, html.length - 1) + "]";
}
} else if (selectTableElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = nodeElement.firstElementChild.scrollLeft;
const tableSelectElement = nodeElement.querySelector(".table__select") as HTMLElement;
html = "<table>";
nodeElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) {
selectCellElements.push(item);
}
});
selectCellElements.forEach((item, index) => {
if (index === 0 || !item.previousElementSibling ||
!item.previousElementSibling.isSameNode(selectCellElements[index - 1])) {
html += "<tr>";
}
html += item.outerHTML;
if (!item.nextElementSibling || !selectCellElements[index + 1] ||
!item.nextElementSibling.isSameNode(selectCellElements[index + 1])) {
html += "</tr>";
}
});
html += "</table>";
textPlain = protyle.lute.HTML2Md(html);
} else {
const tempElement = document.createElement("div");
// https://github.com/siyuan-note/siyuan/issues/5540
const selectTypes = protyle.toolbar.getCurrentType(range);
const spanElement = hasClosestByTag(range.startContainer, "SPAN");
const headingElement = hasClosestByAttribute(range.startContainer, "data-type", "NodeHeading");
if ((selectTypes.length > 0 && spanElement && spanElement.textContent.replace(Constants.ZWSP, "") === range.toString()) ||
(headingElement && headingElement.textContent.replace(Constants.ZWSP, "") === range.toString())) {
if (headingElement) {
// 复制标题 https://github.com/siyuan-note/insider/issues/297
tempElement.append(headingElement.cloneNode(true));
} else if (!["DIV", "TD", "TH", "TR"].includes(range.startContainer.parentElement.tagName)) {
// 复制行内元素 https://github.com/siyuan-note/insider/issues/191
tempElement.append(range.startContainer.parentElement.cloneNode(true));
this.emojiToMd(tempElement);
} else {
// 直接复制块 https://github.com/siyuan-note/insider/issues/318
tempElement.append(range.cloneContents());
this.emojiToMd(tempElement);
}
html = tempElement.innerHTML;
textPlain = range.toString();
} else if (selectImgElement) {
html = selectImgElement.outerHTML;
textPlain = selectImgElement.querySelector("img").getAttribute("data-src");
} else if (selectTypes.length > 0 && range.startContainer.nodeType === 3 &&
range.startContainer.parentElement.tagName === "SPAN" &&
range.startContainer.parentElement.isSameNode(range.endContainer.parentElement)) {
// 复制粗体等字体中的一部分
const attributes = range.startContainer.parentElement.attributes;
const spanElement = document.createElement("span");
for (let i = 0; i < attributes.length; i++) {
spanElement.setAttribute(attributes[i].name, attributes[i].value);
}
if (spanElement.getAttribute("data-type").indexOf("block-ref") > -1 &&
spanElement.getAttribute("data-subtype") === "d") {
// 需变为静态锚文本
spanElement.setAttribute("data-subtype", "s");
}
spanElement.textContent = range.toString();
html = spanElement.outerHTML;
textPlain = range.toString();
} else {
tempElement.append(range.cloneContents());
this.emojiToMd(tempElement);
const inlineMathElement = hasClosestByAttribute(range.commonAncestorContainer, "data-type", "inline-math");
if (inlineMathElement) {
// 表格内复制数学公式 https://ld246.com/article/1631708573504
html = inlineMathElement.outerHTML;
} else {
html = tempElement.innerHTML;
}
// 不能使用 commonAncestorContainer https://ld246.com/article/1643282894693
textPlain = tempElement.textContent;
if (hasClosestByAttribute(range.startContainer, "data-type", "NodeCodeBlock")) {
if (isEndOfBlock(range)) {
textPlain = textPlain.replace(/\n$/, "");
}
} else if (hasClosestByTag(range.startContainer, "TD") || hasClosestByTag(range.startContainer, "TH")) {
tempElement.innerHTML = tempElement.innerHTML.replace(/<br>/g, "\n").replace(/<br\/>/g, "\n");
textPlain = tempElement.textContent.endsWith("\n") ? tempElement.textContent.replace(/\n$/, "") : tempElement.textContent;
} else if (!hasClosestByTag(range.startContainer, "CODE")) {
textPlain = range.toString();
}
}
}
if (protyle.disabled) {
html = getEnableHTML(html);
}
textPlain = textPlain || protyle.lute.BlockDOM2StdMd(html).trimEnd();
textPlain = textPlain.replace(/\u00A0/g, " ") // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382
// Remove ZWSP when copying inline elements https://github.com/siyuan-note/siyuan/issues/13882
.replace(new RegExp(Constants.ZWSP, "g"), "");
event.clipboardData.setData("text/plain", textPlain);
event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
enableLuteMarkdownSyntax(protyle);
event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html);
restoreLuteMarkdownSyntax(protyle);
});
this.element.addEventListener("mousedown", (event: MouseEvent) => {
protyle.wysiwyg.element.classList.remove("protyle-wysiwyg--hiderange");
if (event.button === 2 || window.siyuan.ctrlIsPressed) {
// 右键
return;
}
// Electron 更新后 shift 向上点击获取的 range 不为上一个位置的 https://github.com/siyuan-note/siyuan/issues/9334
if (window.siyuan.shiftIsPressed && getSelection().rangeCount > 0) {
this.shiftStartElement = hasClosestBlock(getSelection().getRangeAt(0).startContainer) as HTMLElement;
}
if (!window.siyuan.shiftIsPressed) {
// https://github.com/siyuan-note/siyuan/issues/3026
hideElements(["select"], protyle);
}
let target = event.target as HTMLElement;
if (hasClosestByClassName(target, "protyle-action") ||
(hasClosestByClassName(target, "av__cell--header") && !hasClosestByClassName(target, "av__widthdrag"))) {
return;
}
const documentSelf = document;
const rect = protyle.element.getBoundingClientRect();
const mostLeft = rect.left + (parseInt(protyle.wysiwyg.element.style.paddingLeft) || 24) + 1;
// 不能用 firstElement否则 https://ld246.com/article/1668758661338
const mostRight = mostLeft + (protyle.wysiwyg.element.clientWidth - (parseInt(protyle.wysiwyg.element.style.paddingLeft) || 24) - (parseInt(protyle.wysiwyg.element.style.paddingRight) || 16)) - 2;
const mostBottom = rect.bottom;
const y = event.clientY;
const contentRect = protyle.contentElement.getBoundingClientRect();
// av col resize
if (!protyle.disabled && target.classList.contains("av__widthdrag")) {
const nodeElement = hasClosestBlock(target);
if (!nodeElement) {
return;
}
const avId = nodeElement.getAttribute("data-av-id");
const blockID = nodeElement.dataset.nodeId;
const dragElement = target.parentElement;
const oldWidth = dragElement.clientWidth;
const dragColId = dragElement.getAttribute("data-col-id");
let newWidth: number;
const scrollElement = nodeElement.querySelector(".av__scroll");
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
newWidth = Math.max(oldWidth + (moveEvent.clientX - event.clientX), 25);
scrollElement.querySelectorAll(".av__row, .av__row--footer").forEach(item => {
(item.querySelector(`[data-col-id="${dragColId}"]`) as HTMLElement).style.width = newWidth + "px";
});
stickyRow(nodeElement, contentRect, "bottom");
};
documentSelf.onmouseup = () => {
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
if (!newWidth || newWidth === oldWidth) {
return;
}
const viewId = nodeElement.getAttribute("custom-sy-av-view");
transaction(protyle, [{
action: "setAttrViewColWidth",
id: dragColId,
avID: avId,
data: newWidth + "px",
blockID,
keyID: viewId // 仅前端使用,用于推送时不影响其他视图 https://github.com/siyuan-note/siyuan/issues/11019
}], [{
action: "setAttrViewColWidth",
id: dragColId,
avID: avId,
data: oldWidth + "px",
blockID,
keyID: viewId
}]);
};
this.preventClick = true;
event.preventDefault();
return;
}
// av drag fill
const avDragFillElement = hasClosestByClassName(target, "av__drag-fill");
if (!protyle.disabled && avDragFillElement) {
const nodeElement = hasClosestBlock(avDragFillElement);
if (!nodeElement) {
return;
}
const originData: { [key: string]: IAVCellValue[] } = {};
let lastOriginCellElement;
const originCellIds: string[] = [];
nodeElement.querySelectorAll(".av__cell--active").forEach((item: HTMLElement) => {
const rowElement = hasClosestByClassName(item, "av__row");
if (rowElement) {
if (!originData[rowElement.dataset.id]) {
originData[rowElement.dataset.id] = [];
}
originData[rowElement.dataset.id].push(genCellValueByElement(getTypeByCellElement(item), item));
lastOriginCellElement = item;
originCellIds.push(item.dataset.id);
}
});
const dragFillCellIndex = getPositionByCellElement(lastOriginCellElement);
const firstCellIndex = getPositionByCellElement(nodeElement.querySelector(".av__cell--active"));
let moveAVCellElement: HTMLElement;
let lastCellElement: HTMLElement;
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
const tempCellElement = hasClosestByClassName(moveEvent.target as HTMLElement, "av__cell") as HTMLElement;
if (moveAVCellElement && tempCellElement && tempCellElement.isSameNode(moveAVCellElement)) {
return;
}
moveAVCellElement = tempCellElement;
if (moveAVCellElement && moveAVCellElement.dataset.id) {
const newIndex = getPositionByCellElement(moveAVCellElement);
nodeElement.querySelectorAll(".av__cell--active").forEach((item: HTMLElement) => {
if (!originCellIds.includes(item.dataset.id)) {
item.classList.remove("av__cell--active");
}
});
if (newIndex.celIndex !== dragFillCellIndex.celIndex) {
lastCellElement = undefined;
return;
}
nodeElement.querySelectorAll(".av__row").forEach((rowElement: HTMLElement, index: number) => {
if ((newIndex.rowIndex < firstCellIndex.rowIndex && index >= newIndex.rowIndex && index < firstCellIndex.rowIndex) ||
(newIndex.rowIndex > dragFillCellIndex.rowIndex && index <= newIndex.rowIndex && index > dragFillCellIndex.rowIndex)) {
rowElement.querySelectorAll(".av__cell").forEach((cellElement: HTMLElement, cellIndex: number) => {
if (cellIndex >= firstCellIndex.celIndex && cellIndex <= newIndex.celIndex) {
cellElement.classList.add("av__cell--active");
lastCellElement = cellElement;
}
});
}
});
}
};
documentSelf.onmouseup = () => {
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
if (lastCellElement) {
dragFillCellsValue(protyle, nodeElement, originData, originCellIds);
const allActiveCellsElement = nodeElement.querySelectorAll(".av__cell--active");
addDragFill(allActiveCellsElement[allActiveCellsElement.length - 1]);
}
return false;
};
this.preventClick = true;
return false;
}
// av cell select
const avCellElement = hasClosestByClassName(target, "av__cell");
if (!protyle.disabled && avCellElement && avCellElement.dataset.id && !isInEmbedBlock(avCellElement)) {
const nodeElement = hasClosestBlock(avCellElement);
if (!nodeElement) {
return;
}
nodeElement.querySelectorAll(".av__cell--select").forEach(item => {
item.classList.remove("av__cell--select");
});
nodeElement.querySelectorAll(".av__drag-fill").forEach(item => {
item.remove();
});
avCellElement.classList.add("av__cell--select");
const originIndex = getPositionByCellElement(avCellElement);
let moveSelectCellElement: HTMLElement;
let lastCellElement: HTMLElement;
const nodeRect = nodeElement.getBoundingClientRect();
const scrollElement = nodeElement.querySelector(".av__scroll");
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
const tempCellElement = hasClosestByClassName(moveEvent.target as HTMLElement, "av__cell") as HTMLElement;
if (scrollElement.scrollWidth > scrollElement.clientWidth + 2) {
if (moveEvent.clientX > nodeRect.right - 10) {
scrollElement.scrollLeft += 10;
} else if (moveEvent.clientX < nodeRect.left + 34) {
scrollElement.scrollLeft -= 10;
}
if (moveEvent.clientY < contentRect.top + 48) {
protyle.contentElement.scrollTop -= 5;
} else if (moveEvent.clientY > contentRect.bottom - 48) {
protyle.contentElement.scrollTop += 5;
}
}
if (moveSelectCellElement && tempCellElement && tempCellElement.isSameNode(moveSelectCellElement)) {
return;
}
if (tempCellElement && tempCellElement.dataset.id && (event.clientX !== moveEvent.clientX || event.clientY !== moveEvent.clientY)) {
const newIndex = getPositionByCellElement(tempCellElement);
nodeElement.querySelectorAll(".av__cell--active").forEach((item: HTMLElement) => {
item.classList.remove("av__cell--active");
});
nodeElement.querySelectorAll(".av__row").forEach((rowElement: HTMLElement, index: number) => {
if (index >= Math.min(originIndex.rowIndex, newIndex.rowIndex) && index <= Math.max(originIndex.rowIndex, newIndex.rowIndex)) {
rowElement.querySelectorAll(".av__cell").forEach((cellElement: HTMLElement, cellIndex: number) => {
if (cellIndex >= Math.min(originIndex.celIndex, newIndex.celIndex) && cellIndex <= Math.max(originIndex.celIndex, newIndex.celIndex)) {
cellElement.classList.add("av__cell--active");
lastCellElement = cellElement;
}
});
}
});
moveSelectCellElement = tempCellElement;
}
};
documentSelf.onmouseup = () => {
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
if (lastCellElement) {
selectRow(nodeElement.querySelector(".av__firstcol"), "unselectAll");
focusBlock(nodeElement);
addDragFill(lastCellElement);
this.preventClick = true;
}
return false;
};
return false;
}
// 图片、iframe、video 缩放
if (!protyle.disabled && target.classList.contains("protyle-action__drag")) {
const nodeElement = hasClosestBlock(target);
if (!nodeElement) {
return;
}
let isCenter = true;
if (["NodeIFrame", "NodeWidget", "NodeVideo"].includes(nodeElement.getAttribute("data-type"))) {
nodeElement.classList.add("iframe--drag");
if (nodeElement.style.textAlign === "left" || nodeElement.style.textAlign === "right") {
isCenter = false;
}
} else if (target.parentElement.parentElement.getAttribute("data-type") === "img") {
target.parentElement.parentElement.classList.add("img--drag");
}
const id = nodeElement.getAttribute("data-node-id");
const html = nodeElement.outerHTML;
const x = event.clientX;
const dragElement = target.previousElementSibling as HTMLElement;
const dragWidth = dragElement.clientWidth;
const dragHeight = dragElement.clientHeight;
const imgElement = dragElement.parentElement.parentElement;
if (dragElement.tagName === "IMG") {
img3115(imgElement);
}
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
if (dragElement.tagName === "IMG") {
dragElement.style.height = "";
}
if (moveEvent.clientX > x - dragWidth + 8 && moveEvent.clientX < mostRight) {
const multiple = ((dragElement.tagName === "IMG" && !imgElement.style.minWidth && nodeElement.style.textAlign !== "center") || !isCenter) ? 1 : 2;
if (dragElement.tagName === "IMG") {
dragElement.parentElement.style.width = Math.max(17, dragWidth + (moveEvent.clientX - x) * multiple) + "px";
} else {
dragElement.style.width = Math.max(17, dragWidth + (moveEvent.clientX - x) * multiple) + "px";
}
}
if (dragElement.tagName !== "IMG") {
if (moveEvent.clientY > y - dragHeight + 8 && moveEvent.clientY < mostBottom) {
dragElement.style.height = (dragHeight + (moveEvent.clientY - y)) + "px";
}
}
};
documentSelf.onmouseup = () => {
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
if (target.classList.contains("protyle-action__drag") && nodeElement) {
updateTransaction(protyle, id, nodeElement.outerHTML, html);
}
nodeElement.classList.remove("iframe--drag");
target.parentElement.parentElement.classList.remove("img--drag");
};
return;
}
// table cell select
let tableBlockElement: HTMLElement | false;
const targetCellElement = hasClosestByTag(target, "TH") || hasClosestByTag(target, "TD");
if (targetCellElement) {
target = targetCellElement;
}
if (target.tagName === "TH" || target.tagName === "TD" || target.firstElementChild?.tagName === "TABLE" || target.classList.contains("table__resize") || target.classList.contains("table__select")) {
tableBlockElement = hasClosestBlock(target);
if (tableBlockElement) {
tableBlockElement.querySelector(".table__select").removeAttribute("style");
window.siyuan.menus.menu.remove();
hideElements(["toolbar"], protyle);
if (target.classList.contains("table__select")) {
target = document.elementFromPoint(event.clientX, event.clientY) as HTMLElement;
}
event.stopPropagation();
}
// 后续拖拽操作写在多选节点中
}
// table col resize
if (!protyle.disabled && target.classList.contains("table__resize")) {
const nodeElement = hasClosestBlock(target);
if (!nodeElement) {
return;
}
const html = nodeElement.outerHTML;
// https://github.com/siyuan-note/siyuan/issues/4455
if (getSelection().rangeCount > 0) {
getSelection().getRangeAt(0).collapse(false);
}
// @ts-ignore
nodeElement.firstElementChild.style.webkitUserModify = "read-only";
nodeElement.style.cursor = "col-resize";
target.removeAttribute("style");
const id = nodeElement.getAttribute("data-node-id");
const x = event.clientX;
const colIndex = parseInt(target.getAttribute("data-col-index"));
const colElement = nodeElement.querySelectorAll("table col")[colIndex] as HTMLElement;
// 清空初始化 table 时的最小宽度
if (colElement.style.minWidth) {
colElement.style.width = (nodeElement.querySelectorAll("table td, table th")[colIndex] as HTMLElement).offsetWidth + "px";
colElement.style.minWidth = "";
}
// 移除 cell 上的宽度限制 https://github.com/siyuan-note/siyuan/issues/7795
nodeElement.querySelectorAll("tr").forEach((trItem: HTMLTableRowElement) => {
trItem.cells[colIndex].style.width = "";
});
const oldWidth = colElement.clientWidth;
const hasScroll = nodeElement.firstElementChild.clientWidth < nodeElement.firstElementChild.scrollWidth;
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
if (nodeElement.style.textAlign === "center" && !hasScroll) {
colElement.style.width = (oldWidth + (moveEvent.clientX - x) * 2) + "px";
} else {
colElement.style.width = (oldWidth + (moveEvent.clientX - x)) + "px";
}
};
documentSelf.onmouseup = () => {
// @ts-ignore
nodeElement.firstElementChild.style.webkitUserModify = "";
nodeElement.style.cursor = "";
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
if (nodeElement) {
updateTransaction(protyle, id, nodeElement.outerHTML, html);
}
};
return;
}
// 多选节点
let clentX = event.clientX;
if (event.clientX > mostRight) {
clentX = mostRight;
} else if (event.clientX < mostLeft) {
clentX = mostLeft;
}
const mostTop = rect.top + (protyle.options.render.breadcrumb ? protyle.breadcrumb.element.parentElement.clientHeight : 0);
let mouseElement: Element;
let moveCellElement: HTMLElement;
let startFirstElement: Element;
let endLastElement: Element;
this.element.querySelectorAll("iframe").forEach(item => {
item.style.pointerEvents = "none";
});
const needScroll = ["IMG", "VIDEO", "AUDIO"].includes(target.tagName) || target.classList.contains("img");
documentSelf.onmousemove = (moveEvent: MouseEvent) => {
let moveTarget: boolean | HTMLElement = moveEvent.target as HTMLElement;
// table cell select
if (tableBlockElement && tableBlockElement.contains(moveTarget) &&
!hasClosestByClassName(tableBlockElement, "protyle-wysiwyg__embed")) {
if (moveTarget.classList.contains("table__select")) {
moveTarget.classList.add("fn__none");
const pointElement = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY);
moveTarget.classList.remove("fn__none");
moveTarget = hasClosestByTag(pointElement, "TH") || hasClosestByTag(pointElement, "TD");
}
if (moveTarget && moveTarget.isSameNode(target)) {
tableBlockElement.querySelector(".table__select").removeAttribute("style");
protyle.wysiwyg.element.classList.remove("protyle-wysiwyg--hiderange");
moveCellElement = moveTarget;
return false;
}
if (moveTarget && (moveTarget.tagName === "TH" || moveTarget.tagName === "TD") &&
(!moveCellElement || !moveCellElement.isSameNode(moveTarget))) {
// @ts-ignore
tableBlockElement.firstElementChild.style.webkitUserModify = "read-only";
let width = target.offsetLeft + target.clientWidth - moveTarget.offsetLeft;
let left = moveTarget.offsetLeft;
if (target.offsetLeft === moveTarget.offsetLeft) {
width = Math.max(target.clientWidth, moveTarget.clientWidth);
} else if (target.offsetLeft < moveTarget.offsetLeft) {
width = moveTarget.offsetLeft + moveTarget.clientWidth - target.offsetLeft;
left = target.offsetLeft;
}
let height = target.offsetTop + target.clientHeight - moveTarget.offsetTop;
let top = moveTarget.offsetTop;
if (target.offsetTop === moveTarget.offsetTop) {
height = Math.max(target.clientHeight, moveTarget.clientHeight);
} else if (target.offsetTop < moveTarget.offsetTop) {
height = moveTarget.offsetTop + moveTarget.clientHeight - target.offsetTop;
top = target.offsetTop;
}
// https://github.com/siyuan-note/insider/issues/1015
Array.from(tableBlockElement.querySelectorAll("th, td")).find((item: HTMLElement) => {
const updateWidth = item.offsetLeft < left + width && item.offsetLeft + item.clientWidth > left + width;
const updateWidth2 = item.offsetLeft < left && item.offsetLeft + item.clientWidth > left;
if (item.offsetTop < top && item.offsetTop + item.clientHeight > top) {
if ((item.offsetLeft + 6 > left && item.offsetLeft + item.clientWidth - 6 < left + width) || updateWidth || updateWidth2) {
height = top + height - item.offsetTop;
top = item.offsetTop;
}
if (updateWidth) {
width = item.offsetLeft + item.clientWidth - left;
}
if (updateWidth2) {
width = left + width - item.offsetLeft;
left = item.offsetLeft;
}
} else if (item.offsetTop < top + height && item.offsetTop + item.clientHeight > top + height) {
if ((item.offsetLeft + 6 > left && item.offsetLeft + item.clientWidth - 6 < left + width) || updateWidth || updateWidth2) {
height = item.clientHeight + item.offsetTop - top;
}
if (updateWidth) {
width = item.offsetLeft + item.clientWidth - left;
}
if (updateWidth2) {
width = left + width - item.offsetLeft;
left = item.offsetLeft;
}
} else if (updateWidth2 && item.offsetTop + 6 > top && item.offsetTop + item.clientHeight - 6 < top + height) {
width = left + width - item.offsetLeft;
left = item.offsetLeft;
} else if (updateWidth && item.offsetTop + 6 > top && item.offsetTop + item.clientHeight - 6 < top + height) {
width = item.offsetLeft + item.clientWidth - left;
}
});
protyle.wysiwyg.element.classList.add("protyle-wysiwyg--hiderange");
tableBlockElement.querySelector(".table__select").setAttribute("style", `left:${left - tableBlockElement.firstElementChild.scrollLeft}px;top:${top}px;height:${height}px;width:${width + 1}px;`);
moveCellElement = moveTarget;
}
return;
}
// 在包含 img video audio 的元素上划选后无法上下滚动 https://ld246.com/article/1681778773806
// 在包含 img video audio 的元素上拖拽无法划选 https://github.com/siyuan-note/siyuan/issues/11763
if (needScroll) {
if (moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB || moveEvent.clientY > contentRect.bottom - Constants.SIZE_SCROLL_TB) {
protyle.contentElement.scroll({
top: protyle.contentElement.scrollTop + (moveEvent.clientY < contentRect.top + Constants.SIZE_SCROLL_TB ? -Constants.SIZE_SCROLL_STEP : Constants.SIZE_SCROLL_STEP),
behavior: "smooth"
});
}
}
protyle.selectElement.classList.remove("fn__none");
// 向左选择,遇到 gutter 就不会弹出 toolbar
hideElements(["gutter"], protyle);
let newTop = 0;
let newLeft = 0;
let newWidth = 0;
let newHeight = 0;
if (moveEvent.clientX < clentX) {
if (moveEvent.clientX < mostLeft) {
// 向左越界
newLeft = mostLeft;
} else {
// 向左
newLeft = moveEvent.clientX;
}
newWidth = clentX - newLeft;
} else {
if (moveEvent.clientX > mostRight) {
// 向右越界
newLeft = clentX;
newWidth = mostRight - newLeft;
} else {
// 向右
newLeft = clentX;
newWidth = moveEvent.clientX - clentX;
}
}
if (moveEvent.clientY > y) {
if (moveEvent.clientY > mostBottom) {
// 向下越界
newTop = y;
newHeight = mostBottom - y;
} else {
// 向下
newTop = y;
newHeight = moveEvent.clientY - y;
}
} else {
if (moveEvent.clientY < mostTop) {
// 向上越界
newTop = mostTop;
} else {
// 向上
newTop = moveEvent.clientY;
}
newHeight = y - newTop;
}
if (newHeight < 4) {
return;
}
protyle.selectElement.setAttribute("style", `background-color: ${protyle.selectElement.style.backgroundColor};top:${newTop}px;height:${newHeight}px;left:${newLeft + 2}px;width:${newWidth - 2}px;`);
const newMouseElement = document.elementFromPoint(moveEvent.clientX, moveEvent.clientY);
if (mouseElement && mouseElement.isSameNode(newMouseElement) && !mouseElement.classList.contains("protyle-wysiwyg") &&
!mouseElement.classList.contains("list") && !mouseElement.classList.contains("bq") && !mouseElement.classList.contains("sb")) {
// 性能优化同一个p元素不进行选中计算
return;
} else {
mouseElement = newMouseElement;
}
hideElements(["select"], protyle);
let firstElement;
if (moveEvent.clientY > y) {
firstElement = startFirstElement || document.elementFromPoint(newLeft, newTop);
endLastElement = undefined;
} else {
firstElement = document.elementFromPoint(newLeft, newTop);
startFirstElement = undefined;
}
if (!firstElement) {
return;
}
if (firstElement.classList.contains("protyle-wysiwyg") || firstElement.classList.contains("list") ||
firstElement.classList.contains("li") || firstElement.classList.contains("sb") ||
firstElement.classList.contains("bq")) {
firstElement = document.elementFromPoint(newLeft, newTop + 16);
}
if (!firstElement) {
return;
}
let firstBlockElement = hasClosestBlock(firstElement);
if (!firstBlockElement && firstElement.classList.contains("protyle-breadcrumb__bar")) {
firstBlockElement = firstElement.nextElementSibling as HTMLElement;
}
if (moveEvent.clientY > y) {
if (!startFirstElement) {
// 向上选择导致滚动条滚动到顶部再向下选择至 > y 时firstBlockElement 为 undefined https://ld246.com/article/1705233964097
if (!firstBlockElement) {
firstBlockElement = protyle.wysiwyg.element.firstElementChild as HTMLElement;
if (firstBlockElement.classList.contains("protyle-breadcrumb__bar")) {
firstBlockElement = firstBlockElement.nextElementSibling as HTMLElement;
}
}
startFirstElement = firstBlockElement;
}
} else if (!firstBlockElement &&
// https://github.com/siyuan-note/siyuan/issues/7580
moveEvent.clientY < protyle.wysiwyg.element.lastElementChild.getBoundingClientRect().bottom) {
firstBlockElement = protyle.wysiwyg.element.firstElementChild as HTMLElement;
if (firstBlockElement.classList.contains("protyle-breadcrumb__bar")) {
firstBlockElement = firstBlockElement.nextElementSibling as HTMLElement;
}
}
let selectElements: Element[] = [];
let currentElement: Element | boolean = firstBlockElement;
if (currentElement) {
// 从下往上划选遇到嵌入块时,选中整个嵌入块
const embedElement = isInEmbedBlock(currentElement);
if (embedElement) {
currentElement = embedElement;
}
}
let hasJump = false;
const selectBottom = endLastElement ? endLastElement.getBoundingClientRect().bottom : (newTop + newHeight);
while (currentElement) {
if (currentElement && !currentElement.classList.contains("protyle-attr")) {
const currentRect = currentElement.getBoundingClientRect();
if (currentRect.height > 0 && currentRect.top < selectBottom && currentRect.left < newLeft + newWidth) {
if (hasJump) {
// 父节点的下个节点在选中范围内才可使用父节点作为选中节点
if (currentElement.nextElementSibling && !currentElement.nextElementSibling.classList.contains("protyle-attr")) {
const nextRect = currentElement.nextElementSibling.getBoundingClientRect();
if (nextRect.top < selectBottom && nextRect.left < newLeft + newWidth) {
selectElements = [currentElement];
currentElement = currentElement.nextElementSibling;
hasJump = false;
} else if (currentElement.parentElement.classList.contains("sb")) {
currentElement = hasClosestBlock(currentElement.parentElement);
hasJump = true;
} else {
break;
}
} else {
currentElement = hasClosestBlock(currentElement.parentElement);
hasJump = true;
}
} else {
if (!currentElement.classList.contains("protyle-breadcrumb__bar")) {
selectElements.push(currentElement);
}
currentElement = currentElement.nextElementSibling;
}
} else if (currentElement.parentElement.classList.contains("sb")) {
// 跳出超级块横向排版中的未选中元素
currentElement = hasClosestBlock(currentElement.parentElement);
hasJump = true;
} else if (currentRect.height === 0 && currentRect.width === 0 && currentElement.parentElement.getAttribute("fold") === "1") {
currentElement = currentElement.parentElement;
selectElements = [];
} else {
break;
}
} else {
currentElement = hasClosestBlock(currentElement.parentElement);
hasJump = true;
}
}
if (moveEvent.clientY <= y && !endLastElement) {
endLastElement = selectElements[selectElements.length - 1];
}
if (selectElements.length === 1 && !selectElements[0].classList.contains("list") &&
!selectElements[0].classList.contains("bq") && !selectElements[0].classList.contains("sb")) {
// 只有一个 p 时不选中
protyle.selectElement.style.backgroundColor = "transparent";
protyle.wysiwyg.element.classList.remove("protyle-wysiwyg--hiderange");
} else {
protyle.wysiwyg.element.classList.add("protyle-wysiwyg--hiderange");
selectElements.forEach(item => {
if (!hasClosestByClassName(item, "protyle-wysiwyg__embed")) {
item.classList.add("protyle-wysiwyg--select");
}
});
protyle.selectElement.style.backgroundColor = "";
}
};
documentSelf.onmouseup = (mouseUpEvent) => {
documentSelf.onmousemove = null;
documentSelf.onmouseup = null;
documentSelf.ondragstart = null;
documentSelf.onselectstart = null;
documentSelf.onselect = null;
startFirstElement = undefined;
endLastElement = undefined;
// 多选表格单元格后,选择菜单中的居左,然后 shift+左 选中的文字无法显示选中背景,因此需移除
// 多选块后 shift+左 选中的文字无法显示选中背景,因此需移除
protyle.wysiwyg.element.classList.remove("protyle-wysiwyg--hiderange");
this.element.querySelectorAll("iframe").forEach(item => {
item.style.pointerEvents = "";
});
protyle.selectElement.classList.add("fn__none");
protyle.selectElement.removeAttribute("style");
if (tableBlockElement) {
// @ts-ignore
tableBlockElement.firstElementChild.style.webkitUserModify = "";
const tableSelectElement = tableBlockElement.querySelector(".table__select") as HTMLElement;
if (tableSelectElement.getAttribute("style")) {
if (getSelection().rangeCount > 0) {
getSelection().getRangeAt(0).collapse(false);
}
window.siyuan.menus.menu.remove();
if (!protyle.disabled) {
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.mergeCell,
click: () => {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const colIndexList: number[] = [];
const colCount = tableBlockElement.querySelectorAll("th").length;
let fnNoneMax = 0;
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
let isTHead = false;
let isTBody = false;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement, index: number) => {
if (item.classList.contains("fn__none")) {
// 合并的元素中间有 fn__none 的元素
if (item.previousElementSibling && item.previousElementSibling.isSameNode(selectCellElements[selectCellElements.length - 1])) {
selectCellElements.push(item);
if (!isTHead && item.parentElement.parentElement.tagName === "THEAD") {
isTHead = true;
} else if (!isTBody && item.parentElement.parentElement.tagName === "TBODY") {
isTBody = true;
}
} else {
if (index < fnNoneMax && colIndexList.includes((index + 1) % colCount)) {
selectCellElements.push(item);
if (!isTHead && item.parentElement.parentElement.tagName === "THEAD") {
isTHead = true;
} else if (!isTBody && item.parentElement.parentElement.tagName === "TBODY") {
isTBody = true;
}
}
}
} else {
if (item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) {
selectCellElements.push(item);
if (!isTHead && item.parentElement.parentElement.tagName === "THEAD") {
isTHead = true;
} else if (!isTBody && item.parentElement.parentElement.tagName === "TBODY") {
isTBody = true;
}
colIndexList.push((index + 1) % colCount);
// https://github.com/siyuan-note/insider/issues/1014
fnNoneMax = Math.max((item.rowSpan - 1) * colCount + index + 1, fnNoneMax);
}
}
});
tableSelectElement.removeAttribute("style");
const oldHTML = tableBlockElement.outerHTML;
let cellElement = selectCellElements[0];
let colSpan = cellElement.colSpan;
let index = 1;
while (cellElement.nextElementSibling && cellElement.nextElementSibling.isSameNode(selectCellElements[index])) {
cellElement = cellElement.nextElementSibling as HTMLTableCellElement;
if (!cellElement.classList.contains("fn__none")) { // https://github.com/siyuan-note/insider/issues/1007#issuecomment-1046195608
colSpan += cellElement.colSpan;
}
index++;
}
let html = "";
let rowElement: Element = selectCellElements[0].parentElement;
let rowSpan = selectCellElements[0].rowSpan;
selectCellElements.forEach((item, index) => {
let cellHTML = item.innerHTML.trim();
if (cellHTML.endsWith("<br>")) {
cellHTML = cellHTML.substr(0, cellHTML.length - 4);
}
html += cellHTML + ((!cellHTML || index === selectCellElements.length - 1) ? "" : "<br>");
if (index !== 0) {
if (!rowElement.isSameNode(item.parentElement)) {
if (!item.classList.contains("fn__none")) { // https://github.com/siyuan-note/insider/issues/1011
rowSpan += item.rowSpan;
}
rowElement = item.parentElement;
if (selectCellElements[0].parentElement.parentElement.tagName === "THEAD" && item.parentElement.parentElement.tagName !== "THEAD") {
selectCellElements[0].parentElement.parentElement.insertAdjacentElement("beforeend", item.parentElement);
}
}
item.classList.add("fn__none");
item.innerHTML = "";
}
});
// https://github.com/siyuan-note/insider/issues/1017
if (isTHead && isTBody) {
rowElement = rowElement.parentElement.nextElementSibling.firstElementChild;
while (rowElement && rowElement.parentElement.tagName !== "THEAD") {
let colSpanCount = 0;
let noneCount = 0;
Array.from(rowElement.children).forEach((item: HTMLTableCellElement) => {
colSpanCount += item.colSpan - 1;
if (item.classList.contains("fn__none")) {
noneCount++;
}
});
if (colSpanCount !== noneCount) {
selectCellElements[0].parentElement.parentElement.insertAdjacentElement("beforeend", rowElement);
rowElement = rowElement.parentElement.nextElementSibling.firstElementChild;
} else {
break;
}
}
}
// 合并背景色不会修改,需要等计算完毕
setTimeout(() => {
if (tableBlockElement) {
selectCellElements[0].innerHTML = html + "<wbr>";
selectCellElements[0].colSpan = colSpan;
selectCellElements[0].rowSpan = rowSpan;
focusByWbr(selectCellElements[0], document.createRange());
updateTransaction(protyle, tableBlockElement.getAttribute("data-node-id"), tableBlockElement.outerHTML, oldHTML);
}
});
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconAlignLeft",
accelerator: window.siyuan.config.keymap.editor.general.alignLeft.custom,
label: window.siyuan.languages.alignLeft,
click: () => {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight &&
(selectCellElements.length === 0 || (selectCellElements.length > 0 && item.offsetTop === selectCellElements[0].offsetTop))) {
selectCellElements.push(item);
}
});
tableSelectElement.removeAttribute("style");
setTableAlign(protyle, selectCellElements, tableBlockElement, "left", getEditorRange(tableBlockElement));
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconAlignCenter",
accelerator: window.siyuan.config.keymap.editor.general.alignCenter.custom,
label: window.siyuan.languages.alignCenter,
click: () => {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight &&
(selectCellElements.length === 0 || (selectCellElements.length > 0 && item.offsetTop === selectCellElements[0].offsetTop))) {
selectCellElements.push(item);
}
});
tableSelectElement.removeAttribute("style");
setTableAlign(protyle, selectCellElements, tableBlockElement, "center", getEditorRange(tableBlockElement));
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconAlignRight",
accelerator: window.siyuan.config.keymap.editor.general.alignRight.custom,
label: window.siyuan.languages.alignRight,
click: () => {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight &&
(selectCellElements.length === 0 || (selectCellElements.length > 0 && item.offsetTop === selectCellElements[0].offsetTop))) {
selectCellElements.push(item);
}
});
tableSelectElement.removeAttribute("style");
setTableAlign(protyle, selectCellElements, tableBlockElement, "right", getEditorRange(tableBlockElement));
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
icon: "",
label: window.siyuan.languages.useDefaultAlign,
click: () => {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight &&
(selectCellElements.length === 0 || (selectCellElements.length > 0 && item.offsetTop === selectCellElements[0].offsetTop))) {
selectCellElements.push(item);
}
});
tableSelectElement.removeAttribute("style");
setTableAlign(protyle, selectCellElements, tableBlockElement, "", getEditorRange(tableBlockElement));
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({type: "separator"}).element);
}
window.siyuan.menus.menu.append(new MenuItem({
id: "copyPlainText",
label: window.siyuan.languages.copyPlainText,
click() {
if (tableBlockElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = tableBlockElement.firstElementChild.scrollLeft;
const tableSelectElement = tableBlockElement.querySelector(".table__select") as HTMLElement;
tableBlockElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) {
selectCellElements.push(item);
}
});
let textPlain = "";
selectCellElements.forEach((item, index) => {
textPlain += item.textContent.trim() + "\t";
if (!item.nextElementSibling || !selectCellElements[index + 1] ||
!item.nextElementSibling.isSameNode(selectCellElements[index + 1])) {
textPlain = textPlain.slice(0, -1) + "\n";
}
});
copyPlainText(textPlain.slice(0, -1));
focusBlock(tableBlockElement);
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconCopy",
accelerator: "⌘C",
label: window.siyuan.languages.copy,
click() {
if (tableBlockElement) {
focusByRange(getEditorRange(tableBlockElement));
document.execCommand("copy");
}
}
}).element);
if (!protyle.disabled) {
window.siyuan.menus.menu.append(new MenuItem({
icon: "iconCut",
accelerator: "⌘X",
label: window.siyuan.languages.cut,
click() {
if (tableBlockElement) {
focusByRange(getEditorRange(tableBlockElement));
document.execCommand("cut");
}
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.clear,
icon: "iconTrashcan",
accelerator: "⌦",
click() {
clearTableCell(protyle, tableBlockElement as HTMLElement);
}
}).element);
window.siyuan.menus.menu.append(new MenuItem({
label: window.siyuan.languages.paste,
icon: "iconPaste",
accelerator: "⌘V",
async click() {
if (document.queryCommandSupported("paste")) {
document.execCommand("paste");
} else if (tableBlockElement) {
try {
const text = await readClipboard();
paste(protyle, Object.assign(text, {target: tableBlockElement as HTMLElement}));
} catch (e) {
console.log(e);
}
}
}
}).element);
}
window.siyuan.menus.menu.popup({x: mouseUpEvent.clientX - 8, y: mouseUpEvent.clientY - 16});
}
}
const ids: string[] = [];
const selectElement = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
selectElement.forEach(item => {
ids.push(item.getAttribute("data-node-id"));
});
countBlockWord(ids);
// 划选后不能存在跨块的 range https://github.com/siyuan-note/siyuan/issues/4473
if (getSelection().rangeCount > 0) {
const range = getSelection().getRangeAt(0);
if (range.toString() === "" ||
window.siyuan.shiftIsPressed // https://ld246.com/article/1650096678723
) {
if (event.detail > 2) {
// table 前或最后一个 cell 三击状态不对
let cursorElement = hasClosestBlock(range.startContainer) as Element;
if (cursorElement) {
if (cursorElement.nextElementSibling?.classList.contains("table")) {
setLastNodeRange(getContenteditableElement(cursorElement), range, false);
} else if (cursorElement.classList.contains("table")) {
const cellElements = cursorElement.querySelectorAll("th, td");
cursorElement = cellElements[cellElements.length - 1];
if (cursorElement.contains(range.startContainer)) {
setLastNodeRange(cursorElement, range, false);
}
}
}
return;
}
}
if (selectElement.length > 0) {
range.collapse(true);
return;
}
const startBlockElement = hasClosestBlock(range.startContainer);
let endBlockElement: false | HTMLElement;
if (mouseUpEvent.detail > 2 && range.endContainer.nodeType !== 3 && ["DIV", "TD", "TH"].includes((range.endContainer as HTMLElement).tagName) && range.endOffset === 0) {
// 三击选中段落块时rangeEnd 会在下一个块
if ((range.endContainer as HTMLElement).classList.contains("protyle-attr") && startBlockElement) {
// 三击在悬浮层中会选择到 attr https://github.com/siyuan-note/siyuan/issues/4636
// 需要获取可编辑元素,使用 previousElementSibling 的话会 https://github.com/siyuan-note/siyuan/issues/9714
setLastNodeRange(getContenteditableElement(startBlockElement), range, false);
} else if (["TD", "TH"].includes((range.endContainer as HTMLElement).tagName)) {
const cellElement = hasClosestByTag(range.startContainer, "TH") || hasClosestByTag(range.startContainer, "TD");
if (cellElement) {
setLastNodeRange(cellElement, range, false);
}
}
} else {
endBlockElement = hasClosestBlock(range.endContainer);
}
if (startBlockElement && endBlockElement && !endBlockElement.isSameNode(startBlockElement)) {
if ((range.startContainer.nodeType === 1 && (range.startContainer as HTMLElement).tagName === "DIV" && (range.startContainer as HTMLElement).classList.contains("protyle-attr")) ||
event.clientY > mouseUpEvent.clientY) {
setFirstNodeRange(getContenteditableElement(endBlockElement), range);
} else if (range.endOffset === 0 && range.endContainer.nodeType === 1 && (range.endContainer as HTMLElement).tagName === "DIV") {
setLastNodeRange(getContenteditableElement(startBlockElement), range, false);
} else {
range.collapse(true);
}
}
}
};
});
}
private bindEvent(protyle: IProtyle) {
// 删除块时av 头尾需重新计算位置
protyle.observer = new ResizeObserver(() => {
const contentRect = protyle.contentElement.getBoundingClientRect();
protyle.wysiwyg.element.querySelectorAll(".av").forEach((item: HTMLElement) => {
if (item.querySelector(".av__title")) {
stickyRow(item, contentRect, "all");
}
});
});
this.element.addEventListener("focusout", () => {
if (getSelection().rangeCount === 0) {
return;
}
const range = getSelection().getRangeAt(0);
if (this.element.isSameNode(range.startContainer) || this.element.contains(range.startContainer)) {
protyle.toolbar.range = range;
}
});
this.element.addEventListener("cut", (event: ClipboardEvent & { target: HTMLElement }) => {
window.siyuan.ctrlIsPressed = false; // https://github.com/siyuan-note/siyuan/issues/6373
if (protyle.disabled) {
return;
}
if (event.target.tagName === "PROTYLE-HTML" || event.target.localName === "input") {
event.stopPropagation();
return;
}
if (protyle.options.render.breadcrumb) {
protyle.breadcrumb.hide();
}
const range = getEditorRange(protyle.wysiwyg.element);
let nodeElement = hasClosestBlock(range.startContainer);
if (!nodeElement) {
event.stopPropagation();
event.preventDefault();
return;
}
// https://github.com/siyuan-note/siyuan/issues/11793
const embedElement = isInEmbedBlock(nodeElement);
if (embedElement) {
nodeElement = embedElement;
}
event.stopPropagation();
event.preventDefault();
const selectImgElement = nodeElement.querySelector(".img--select");
const selectAVElement = nodeElement.querySelector(".av__row--select, .av__cell--select");
const selectTableElement = nodeElement.querySelector(".table__select")?.clientWidth > 0;
let selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
if (selectElements.length === 0 && range.toString() === "" && !range.cloneContents().querySelector("img") &&
!selectImgElement && !selectAVElement && !selectTableElement) {
nodeElement.classList.add("protyle-wysiwyg--select");
selectElements = [nodeElement];
}
let html = "";
let textPlain = "";
if (selectElements.length > 0) {
if (selectElements[0].getAttribute("data-type") === "NodeListItem" &&
selectElements[0].parentElement.classList.contains("list") && // 反链复制列表项 https://github.com/siyuan-note/siyuan/issues/6555
selectElements[0].parentElement.childElementCount - 1 === selectElements.length) {
html = selectElements[0].parentElement.outerHTML;
} else {
selectElements.forEach(item => {
const topElement = getTopAloneElement(item);
if (item.getAttribute("data-type") === "NodeHeading" && item.getAttribute("fold") === "1") {
html += removeEmbed(topElement).replace('fold="1"', "");
} else {
html += removeEmbed(topElement);
}
});
if (selectElements[0].getAttribute("data-type") === "NodeListItem") {
html = `<div data-subtype="${selectElements[0].getAttribute("data-subtype")}" data-node-id="${Lute.NewNodeID()}" data-type="NodeList" class="list">${html}<div class="protyle-attr" contenteditable="false">${Constants.ZWSP}</div></div>`;
}
}
const nextElement = getNextBlock(selectElements[selectElements.length - 1]);
removeBlock(protyle, nodeElement, range, "remove");
if (nextElement) {
// Ctrl+X 剪切后光标应跳到下一行行首 https://github.com/siyuan-note/siyuan/issues/5485
focusBlock(nextElement);
}
} else if (selectAVElement) {
const cellsValue = updateCellsValue(protyle, nodeElement);
html = JSON.stringify(cellsValue.json);
textPlain = cellsValue.text;
} else if (selectTableElement) {
const selectCellElements: HTMLTableCellElement[] = [];
const scrollLeft = nodeElement.firstElementChild.scrollLeft;
const tableSelectElement = nodeElement.querySelector(".table__select") as HTMLElement;
html = "<table>";
nodeElement.querySelectorAll("th, td").forEach((item: HTMLTableCellElement) => {
if (!item.classList.contains("fn__none") &&
item.offsetLeft + 6 > tableSelectElement.offsetLeft + scrollLeft && item.offsetLeft + item.clientWidth - 6 < tableSelectElement.offsetLeft + scrollLeft + tableSelectElement.clientWidth &&
item.offsetTop + 6 > tableSelectElement.offsetTop && item.offsetTop + item.clientHeight - 6 < tableSelectElement.offsetTop + tableSelectElement.clientHeight) {
selectCellElements.push(item);
}
});
tableSelectElement.removeAttribute("style");
if (getSelection().rangeCount > 0) {
const range = getSelection().getRangeAt(0);
if (nodeElement.contains(range.startContainer)) {
range.insertNode(document.createElement("wbr"));
}
}
const oldHTML = nodeElement.outerHTML;
nodeElement.querySelector("wbr")?.remove();
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
selectCellElements.forEach((item, index) => {
if (index === 0 || !item.previousElementSibling ||
!item.previousElementSibling.isSameNode(selectCellElements[index - 1])) {
html += "<tr>";
}
html += item.outerHTML;
if (!item.nextElementSibling || !selectCellElements[index + 1] ||
!item.nextElementSibling.isSameNode(selectCellElements[index + 1])) {
html += "</tr>";
}
item.innerHTML = "";
});
html += "</table>";
textPlain = protyle.lute.HTML2Md(html);
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, oldHTML);
} else {
const id = nodeElement.getAttribute("data-node-id");
setInsertWbrHTML(nodeElement, range, protyle);
const oldHTML = protyle.wysiwyg.lastHTMLs[id] || nodeElement.outerHTML;
const tempElement = document.createElement("div");
// 首次选中标题时range.startContainer 会为空
let startContainer = range.startContainer;
if (startContainer.nodeType === 3 && startContainer.textContent === "") {
const nextSibling = hasNextSibling(range.startContainer);
if (nextSibling) {
startContainer = nextSibling;
}
}
const headElement = hasClosestByAttribute(startContainer, "data-type", "NodeHeading");
if (headElement && range.toString() === headElement.firstElementChild.textContent) {
tempElement.insertAdjacentHTML("afterbegin", headElement.firstElementChild.innerHTML);
headElement.firstElementChild.innerHTML = "";
} else if (range.toString() !== "" && startContainer.isSameNode(range.endContainer) &&
range.startContainer.nodeType === 3 &&
// 需使用 wholeText https://github.com/siyuan-note/siyuan/issues/14339
range.endOffset === (range.endContainer as Text).wholeText.length &&
range.startOffset === 0 &&
!["DIV", "TD", "TH", "TR"].includes(range.startContainer.parentElement.tagName)) {
// 选中整个内联元素
tempElement.append(range.startContainer.parentElement);
} else if (selectImgElement) {
tempElement.append(selectImgElement);
} else if (range.startContainer.nodeType === 3 && range.startContainer.parentElement.tagName === "SPAN" &&
range.startContainer.parentElement.getAttribute("data-type") &&
range.startContainer.parentElement.isSameNode(range.endContainer.parentElement)) {
// 剪切粗体等字体中的一部分
const spanElement = range.startContainer.parentElement;
const attributes = spanElement.attributes;
const newSpanElement = document.createElement("span");
for (let i = 0; i < attributes.length; i++) {
newSpanElement.setAttribute(attributes[i].name, attributes[i].value);
}
if (spanElement.getAttribute("data-type").indexOf("block-ref") > -1 &&
spanElement.getAttribute("data-subtype") === "d") {
// 引用被剪切后需变为静态锚文本
newSpanElement.setAttribute("data-subtype", "s");
spanElement.setAttribute("data-subtype", "s");
}
newSpanElement.textContent = range.toString();
range.deleteContents();
tempElement.append(newSpanElement);
} else {
if (range.cloneContents().querySelectorAll("td, th").length > 0) {
// 表格内多格子 cut https://github.com/siyuan-note/insider/issues/564
const wbrElement = document.createElement("wbr");
range.insertNode(wbrElement);
range.setStartAfter(wbrElement);
tempElement.append(range.extractContents());
nodeElement.outerHTML = protyle.lute.SpinBlockDOM(nodeElement.outerHTML);
nodeElement = protyle.wysiwyg.element.querySelector(`[data-node-id="${id}"]`) as HTMLElement;
mathRender(nodeElement);
focusByWbr(nodeElement, range);
} else {
const inlineMathElement = hasClosestByAttribute(range.commonAncestorContainer, "data-type", "inline-math");
if (inlineMathElement) {
// 表格内剪切数学公式 https://ld246.com/article/1631708573504
tempElement.append(inlineMathElement);
} else {
tempElement.append(range.extractContents());
let parentElement: Element | false;
// https://ld246.com/article/1647689760545
if (nodeElement.classList.contains("av")) {
updateAVName(protyle, nodeElement);
} else if (nodeElement.classList.contains("table")) {
parentElement = hasClosestByTag(range.startContainer, "TD") || hasClosestByTag(range.startContainer, "TH");
} else {
parentElement = getContenteditableElement(nodeElement);
}
if (parentElement) {
// 引用文本剪切 https://ld246.com/article/1647689760545
// 表格多行剪切 https://ld246.com/article/1652603836350
// 自定义表情的段落剪切后表情丢失 https://ld246.com/article/1668781478724
Array.from(parentElement.children).forEach(item => {
if (item.textContent === "" && (item.nodeType === 1 && !["BR", "IMG"].includes(item.tagName))) {
item.remove();
}
});
}
}
}
}
this.emojiToMd(tempElement);
html = tempElement.innerHTML;
// https://github.com/siyuan-note/siyuan/issues/10722
if (hasClosestByAttribute(range.startContainer, "data-type", "NodeCodeBlock") ||
hasClosestByTag(range.startContainer, "CODE")) {
textPlain = tempElement.textContent.replace(Constants.ZWSP, "");
}
// https://github.com/siyuan-note/siyuan/issues/4321
if (!nodeElement.classList.contains("table")) {
const editableElement = getContenteditableElement(nodeElement);
if (editableElement && editableElement.textContent === "") {
editableElement.innerHTML = "";
}
}
nodeElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
if (nodeElement.getAttribute("data-type") === "NodeCodeBlock") {
range.insertNode(document.createElement("wbr"));
nodeElement.querySelector('[data-render="true"]')?.removeAttribute("data-render");
highlightRender(nodeElement);
}
if (nodeElement.parentElement.parentElement && !nodeElement.classList.contains("av")) {
// 选中 heading 时,使用删除的 transaction
setInsertWbrHTML(nodeElement, range, protyle);
updateTransaction(protyle, id, protyle.wysiwyg.lastHTMLs[id] || nodeElement.outerHTML, oldHTML);
}
}
protyle.hint.render(protyle);
if (!selectAVElement) {
textPlain = textPlain || protyle.lute.BlockDOM2StdMd(html).trimEnd(); // 需要 trimEnd否则 \n 会导致 https://github.com/siyuan-note/siyuan/issues/6218
if (nodeElement.classList.contains("table")) {
textPlain = textPlain.replace(/<br>/g, "\n").replace(/<br\/>/g, "\n");
textPlain = textPlain.endsWith("\n") ? textPlain.replace(/\n$/, "") : textPlain;
}
}
textPlain = textPlain.replace(/\u00A0/g, " "); // Replace non-breaking spaces with normal spaces when copying https://github.com/siyuan-note/siyuan/issues/9382
event.clipboardData.setData("text/plain", textPlain);
event.clipboardData.setData("text/html", selectTableElement ? html : protyle.lute.BlockDOM2HTML(selectAVElement ? textPlain : html));
enableLuteMarkdownSyntax(protyle);
event.clipboardData.setData("text/siyuan", selectTableElement ? protyle.lute.HTML2BlockDOM(html) : html);
restoreLuteMarkdownSyntax(protyle);
});
let beforeContextmenuRange: Range;
this.element.addEventListener("contextmenu", (event: MouseEvent & { detail: any }) => {
if (event.shiftKey) {
return;
}
event.stopPropagation();
event.preventDefault();
const x = event.clientX || event.detail.x;
const y = event.clientY || event.detail.y;
const selectElements = protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select");
if (selectElements.length > 1) {
// 多选块
hideElements(["util"], protyle);
protyle.gutter.renderMenu(protyle, selectElements[0]);
window.siyuan.menus.menu.popup({x, y});
return;
}
const target = event.detail.target || event.target as HTMLElement;
const embedElement = isInEmbedBlock(target);
if (embedElement) {
if (getSelection().rangeCount === 0) {
focusSideBlock(embedElement);
}
protyle.gutter.renderMenu(protyle, embedElement);
/// #if MOBILE
window.siyuan.menus.menu.fullscreen();
/// #else
window.siyuan.menus.menu.popup({x, y});
/// #endif
return false;
}
const nodeElement = hasClosestBlock(target);
if (!nodeElement) {
return false;
}
const avCellElement = hasClosestByClassName(target, "av__cell");
if (avCellElement) {
if (avCellElement.classList.contains("av__cell--header")) {
if (!protyle.disabled) {
showColMenu(protyle, nodeElement, avCellElement);
}
event.stopPropagation();
event.preventDefault();
return;
}
if (getTypeByCellElement(avCellElement) === "mAsset") {
const assetImgElement = hasClosestByClassName(target, "av__cellassetimg") || hasClosestByClassName(target, "av__celltext--url");
if (assetImgElement) {
let index = 0;
Array.from(avCellElement.children).find((item, i) => {
if (item === assetImgElement) {
index = i;
return true;
}
});
editAssetItem({
protyle,
cellElements: [avCellElement],
blockElement: hasClosestBlock(assetImgElement) as HTMLElement,
content: target.tagName === "IMG" ? target.getAttribute("src") : target.getAttribute("data-url"),
type: target.tagName === "IMG" ? "image" : "file",
name: target.tagName === "IMG" ? "" : target.getAttribute("data-name"),
index,
rect: target.getBoundingClientRect()
});
event.stopPropagation();
event.preventDefault();
return;
}
}
}
// 在 span 前面,防止单元格哪 block-ref 被修改
const avRowElement = hasClosestByClassName(target, "av__row");
if (avRowElement && avContextmenu(protyle, avRowElement, {
x: event.clientX,
y: avRowElement.getBoundingClientRect().bottom,
h: avRowElement.clientHeight
})) {
event.stopPropagation();
event.preventDefault();
return;
}
const avTabHeaderElement = hasClosestByClassName(target, "item");
if (nodeElement.classList.contains("av") && avTabHeaderElement) {
if (avTabHeaderElement.classList.contains("item--focus")) {
openViewMenu({protyle, blockElement: nodeElement, element: avTabHeaderElement});
} else {
nodeElement.removeAttribute("data-render");
avRender(nodeElement, protyle, () => {
openViewMenu({
protyle,
blockElement: nodeElement,
element: nodeElement.querySelector(".item.item--focus")
});
}, avTabHeaderElement.dataset.id);
}
event.stopPropagation();
event.preventDefault();
return;
}
protyle.toolbar.range = getEditorRange(protyle.element);
if (target.tagName === "SPAN" && !isNotEditBlock(nodeElement)) { // https://ld246.com/article/1665141518103
let types = target.getAttribute("data-type")?.split(" ") || [];
if (types.length === 0) {
// https://github.com/siyuan-note/siyuan/issues/8960
types = (target.dataset.type || "").split(" ");
}
if (types.length > 0) {
removeSearchMark(target);
}
if (types.includes("block-ref")) {
refMenu(protyle, target);
// 阻止 popover
target.setAttribute("prevent-popover", "true");
setTimeout(() => {
target.removeAttribute("prevent-popover");
}, 620);
return false;
} else if (types.includes("file-annotation-ref") && !protyle.disabled) {
fileAnnotationRefMenu(protyle, target);
return false;
} else if (types.includes("tag") && !protyle.disabled) {
tagMenu(protyle, target);
return false;
} else if (types.includes("inline-memo")) {
protyle.toolbar.showRender(protyle, target);
return false;
} else if (types.includes("a")) {
linkMenu(protyle, target);
if (window.siyuan.config.editor.floatWindowMode === 0 &&
target.getAttribute("data-href")?.startsWith("siyuan://blocks")) {
// 阻止 popover
target.setAttribute("prevent-popover", "true");
setTimeout(() => {
target.removeAttribute("prevent-popover");
}, 620);
}
return false;
}
}
const inlineMathElement = hasClosestByAttribute(target, "data-type", "inline-math");
if (inlineMathElement) {
inlineMathMenu(protyle, inlineMathElement);
return false;
}
if (target.tagName === "IMG" && hasClosestByClassName(target, "img")) {
imgMenu(protyle, protyle.toolbar.range, target.parentElement.parentElement, {
clientX: x + 4,
clientY: y
});
return false;
}
if (!isNotEditBlock(nodeElement) && !nodeElement.classList.contains("protyle-wysiwyg--select") &&
!hasClosestByClassName(target, "protyle-action") && // https://github.com/siyuan-note/siyuan/issues/8983
(isMobile() || event.detail.target || (beforeContextmenuRange && nodeElement.contains(beforeContextmenuRange.startContainer)))
) {
if ((!isMobile() || protyle.toolbar?.element.classList.contains("fn__none")) && !nodeElement.classList.contains("av")) {
contentMenu(protyle, nodeElement);
window.siyuan.menus.menu.popup({x, y: y + 13, h: 26});
protyle.toolbar?.element.classList.add("fn__none");
if (nodeElement.classList.contains("table")) {
nodeElement.querySelector(".table__select").removeAttribute("style");
}
}
} else if (protyle.toolbar.range.toString() === "") {
hideElements(["util"], protyle);
if (protyle.gutter) {
protyle.gutter.renderMenu(protyle, nodeElement);
}
/// #if MOBILE
window.siyuan.menus.menu.fullscreen();
/// #else
window.siyuan.menus.menu.popup({x, y});
/// #endif
protyle.toolbar?.element.classList.add("fn__none");
}
});
this.element.addEventListener("pointerdown", () => {
if (getSelection().rangeCount > 0) {
beforeContextmenuRange = getSelection().getRangeAt(0);
} else {
beforeContextmenuRange = undefined;
}
/// #if BROWSER && !MOBILE
if (protyle.breadcrumb) {
const indentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]');
if (indentElement && getSelection().rangeCount > 0) {
setTimeout(() => {
const newRange = getSelection().getRangeAt(0);
const blockElement = hasClosestBlock(newRange.startContainer);
if (!blockElement) {
return;
}
const outdentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]');
if (blockElement.parentElement.classList.contains("li")) {
indentElement.removeAttribute("disabled");
outdentElement.removeAttribute("disabled");
} else {
indentElement.setAttribute("disabled", "true");
outdentElement.setAttribute("disabled", "true");
}
}, 520);
}
}
/// #endif
});
let preventGetTopHTML = false;
this.element.addEventListener("mousewheel", (event: WheelEvent) => {
hideTooltip();
// https://ld246.com/article/1648865235549
// 不能使用上一版本的 timeStamp否则一直滚动将导致间隔不够 https://ld246.com/article/1662852664926
if (!preventGetTopHTML &&
event.deltaY < 0 && !protyle.scroll.element.classList.contains("fn__none") &&
protyle.contentElement.clientHeight === protyle.contentElement.scrollHeight &&
protyle.wysiwyg.element.firstElementChild.getAttribute("data-eof") !== "1") {
fetchPost("/api/filetree/getDoc", {
id: protyle.wysiwyg.element.firstElementChild.getAttribute("data-node-id"),
mode: 1,
size: window.siyuan.config.editor.dynamicLoadBlocks,
}, getResponse => {
preventGetTopHTML = false;
onGet({
data: getResponse,
protyle,
action: [Constants.CB_GET_BEFORE, Constants.CB_GET_UNCHANGEID],
});
});
preventGetTopHTML = true;
}
if (event.deltaX === 0) {
return;
}
// https://github.com/siyuan-note/siyuan/issues/4099
const tableElement = hasClosestByClassName(event.target as HTMLElement, "table");
if (tableElement) {
const tableSelectElement = tableElement.querySelector(".table__select") as HTMLElement;
if (tableSelectElement?.style.width) {
tableSelectElement.removeAttribute("style");
window.siyuan.menus.menu.remove();
}
}
}, {passive: true});
this.element.addEventListener("paste", (event: ClipboardEvent & { target: HTMLElement }) => {
// https://github.com/siyuan-note/siyuan/issues/11241
if (event.target.localName === "input" && event.target.getAttribute("data-type") === "av-search") {
return;
}
if (protyle.disabled) {
event.stopPropagation();
event.preventDefault();
return;
}
window.siyuan.ctrlIsPressed = false; // https://github.com/siyuan-note/siyuan/issues/6373
// https://github.com/siyuan-note/siyuan/issues/4600
if (event.target.tagName === "PROTYLE-HTML" || event.target.localName === "input") {
event.stopPropagation();
return;
}
if (!hasClosestByAttribute(event.target, "contenteditable", "true")) {
event.stopPropagation();
event.preventDefault();
return;
}
const blockElement = hasClosestBlock(event.target);
if (blockElement && !getContenteditableElement(blockElement)) {
event.stopPropagation();
event.preventDefault();
return;
}
if (!blockElement) {
return;
}
// 链接备注样式引用pdf标注粘贴 https://github.com/siyuan-note/siyuan/issues/11572
const range = getSelection().getRangeAt(0);
const inlineElement = range.startContainer.parentElement;
if (range.toString() === "" && inlineElement.tagName === "SPAN") {
const currentTypes = (inlineElement.getAttribute("data-type") || "").split(" ");
if (currentTypes.includes("inline-memo") || currentTypes.includes("text") ||
currentTypes.includes("block-ref") || currentTypes.includes("file-annotation-ref") ||
currentTypes.includes("a")) {
const offset = getSelectionOffset(inlineElement, blockElement, range);
if (offset.start === 0) {
range.setStartBefore(inlineElement);
range.collapse(true);
} else if (offset.start === inlineElement.textContent.length) {
range.setEndAfter(inlineElement);
range.collapse(false);
}
}
}
paste(protyle, event);
});
// 输入法测试点 https://github.com/siyuan-note/siyuan/issues/3027
let isComposition = false; // for iPhone
this.element.addEventListener("compositionstart", (event) => {
isComposition = true;
// 微软双拼由于 focusByRange 导致无法输入文字,因此不再 keydown 中记录了,但 keyup 会记录拼音字符,因此使用 isComposition 阻止 keyup 记录。
// 但搜狗输入法选中后继续输入不走 keydownisComposition 阻止了 keyup 记录,因此需在此记录。
const range = getEditorRange(protyle.wysiwyg.element);
const nodeElement = hasClosestBlock(range.startContainer);
if (!isMac() && nodeElement) {
setInsertWbrHTML(nodeElement, range, protyle);
}
event.stopPropagation();
});
this.element.addEventListener("compositionend", (event: InputEvent) => {
event.stopPropagation();
isComposition = false;
const range = getEditorRange(this.element);
const blockElement = hasClosestBlock(range.startContainer);
if (!blockElement) {
return;
}
if ("" !== event.data) {
this.escapeInline(protyle, range, event);
// 小鹤音形 ;k 不能使用 setTimeout;
// wysiwyg.element contenteditable 为 false 时,连拼 needRender 必须为 false
// hr 渲染;任务列表、粗体、数学公示结尾 needRender 必须为 true
input(protyle, blockElement, range, true);
} else {
const id = blockElement.getAttribute("data-node-id");
if (protyle.wysiwyg.lastHTMLs[id]) {
updateTransaction(protyle, id, blockElement.outerHTML, protyle.wysiwyg.lastHTMLs[id]);
}
}
});
let timeout: number;
this.element.addEventListener("input", (event: InputEvent) => {
const target = event.target as HTMLElement;
if (target.tagName === "VIDEO" || target.tagName === "AUDIO" || event.inputType === "historyRedo") {
return;
}
if (event.inputType === "historyUndo") {
/// #if !BROWSER
ipcRenderer.send(Constants.SIYUAN_CMD, "redo");
/// #endif
window.siyuan.menus.menu.remove();
return;
}
const range = getEditorRange(this.element);
const blockElement = hasClosestBlock(range.startContainer);
if (!blockElement) {
return;
}
if ([":", "(", "【", "", "[", "{", "「", "『", "#", "/", "、"].includes(event.data)) {
protyle.hint.enableExtend = true;
}
if (event.isComposing || isComposition ||
// https://github.com/siyuan-note/siyuan/issues/337 编辑器内容拖拽问题
event.inputType === "deleteByDrag" || event.inputType === "insertFromDrop"
) {
return;
}
this.escapeInline(protyle, range, event);
if ((/^\d{1}$/.test(event.data) || event.data === "" || event.data === "“" ||
// 百度输入法中文反双引号 https://github.com/siyuan-note/siyuan/issues/9686
event.data === "”" ||
event.data === "「")) {
clearTimeout(timeout); // https://github.com/siyuan-note/siyuan/issues/9179
timeout = window.setTimeout(() => {
input(protyle, blockElement, range, true); // 搜狗拼音数字后面句号变为点Mac 反向双引号无法输入
});
} else {
input(protyle, blockElement, range, true, event);
}
event.stopPropagation();
});
this.element.addEventListener("keyup", (event) => {
const range = getEditorRange(this.element).cloneRange();
const nodeElement = hasClosestBlock(range.startContainer);
if (event.key !== "PageUp" && event.key !== "PageDown" && event.key !== "Home" && event.key !== "End" &&
event.key.indexOf("Arrow") === -1 && event.key !== "Escape" && event.key !== "Shift" &&
event.key !== "Meta" && event.key !== "Alt" && event.key !== "Control" && event.key !== "CapsLock" &&
!event.ctrlKey && !event.shiftKey && !event.metaKey && !event.altKey &&
!/^F\d{1,2}$/.test(event.key)) {
// 搜狗输入法不走 keydown没有选中字符后不走 compositionstart需重新记录历史状态
if (!isMac() && nodeElement &&
// 微软双拼 keyup 会记录拼音字符,因此在 compositionstart 记录
!isComposition &&
(typeof protyle.wysiwyg.lastHTMLs[nodeElement.getAttribute("data-node-id")] === "undefined" || range.toString() !== "" || !this.preventKeyup)) {
setInsertWbrHTML(nodeElement, range, protyle);
}
this.preventKeyup = false;
return;
}
// 需放在 lastHTMLs 后,否则 https://github.com/siyuan-note/siyuan/issues/4388
if (this.preventKeyup) {
this.preventKeyup = false;
return;
}
if ((event.shiftKey || isOnlyMeta(event)) && !event.isComposing && range.toString() !== "") {
// 工具栏
protyle.toolbar.render(protyle, range, event);
countSelectWord(range);
}
if (event.eventPhase !== 3 && !event.shiftKey && (event.key.indexOf("Arrow") > -1 || event.key === "Home" || event.key === "End" || event.key === "PageUp" || event.key === "PageDown") && !event.isComposing) {
if (nodeElement) {
this.setEmptyOutline(protyle, nodeElement);
if (range.toString() === "" && !nodeElement.classList.contains("protyle-wysiwyg--select")) {
countSelectWord(range, protyle.block.rootID);
}
if (protyle.breadcrumb) {
const indentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="indent"]');
if (indentElement) {
const outdentElement = protyle.breadcrumb.element.parentElement.querySelector('[data-type="outdent"]');
if (nodeElement.parentElement.classList.contains("li")) {
indentElement.removeAttribute("disabled");
outdentElement.removeAttribute("disabled");
} else {
indentElement.setAttribute("disabled", "true");
outdentElement.setAttribute("disabled", "true");
}
}
}
}
event.stopPropagation();
}
// 按下方向键后块高亮跟随光标移动 https://github.com/siyuan-note/siyuan/issues/8918
if ((event.key === "ArrowLeft" || event.key === "ArrowRight") &&
nodeElement && !nodeElement.classList.contains("protyle-wysiwyg--select")) {
const selectElements = Array.from(protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select"));
let containRange = false;
selectElements.find(item => {
if (item.contains(range.startContainer)) {
containRange = true;
return true;
}
});
if (!containRange && selectElements.length > 0) {
selectElements.forEach(item => {
item.classList.remove("protyle-wysiwyg--select");
});
nodeElement.classList.add("protyle-wysiwyg--select");
}
}
});
this.element.addEventListener("dblclick", (event: MouseEvent & { target: HTMLElement }) => {
if (event.target.tagName === "IMG" && !event.target.classList.contains("emoji")) {
previewDocImage((event.target as HTMLElement).getAttribute("src"), protyle.block.rootID);
return;
}
});
let mobileBlur = false;
this.element.addEventListener("click", (event: MouseEvent & { target: HTMLElement }) => {
if (this.preventClick) {
this.preventClick = false;
return;
}
protyle.app.plugins.forEach(item => {
item.eventBus.emit("click-editorcontent", {
protyle,
event
});
});
hideElements(["hint", "util"], protyle);
const ctrlIsPressed = isOnlyMeta(event);
const backlinkBreadcrumbItemElement = hasClosestByClassName(event.target, "protyle-breadcrumb__item");
if (backlinkBreadcrumbItemElement) {
const breadcrumbId = backlinkBreadcrumbItemElement.getAttribute("data-id");
/// #if !MOBILE
if (breadcrumbId) {
if (ctrlIsPressed && !event.shiftKey && !event.altKey) {
checkFold(breadcrumbId, (zoomIn) => {
openFileById({
app: protyle.app,
id: breadcrumbId,
action: zoomIn ? [Constants.CB_GET_FOCUS, Constants.CB_GET_ALL] : [Constants.CB_GET_FOCUS, Constants.CB_GET_CONTEXT],
zoomIn
});
});
} else {
loadBreadcrumb(protyle, backlinkBreadcrumbItemElement);
}
} else {
// 引用标题时的更多加载
getBacklinkHeadingMore(backlinkBreadcrumbItemElement);
}
/// #else
if (breadcrumbId) {
loadBreadcrumb(protyle, backlinkBreadcrumbItemElement);
}
/// #endif
event.stopPropagation();
return;
}
if (!event.shiftKey) {
this.shiftStartElement = undefined;
}
this.setEmptyOutline(protyle, event.target);
const tableElement = hasClosestByClassName(event.target, "table");
this.element.querySelectorAll(".table").forEach(item => {
if (item.tagName !== "DIV") {
return;
}
if (!tableElement || !item.isSameNode(tableElement)) {
item.querySelector(".table__select").removeAttribute("style");
}
if (tableElement && tableElement.isSameNode(item) && item.querySelector(".table__select").getAttribute("style")) {
// 防止合并单元格的菜单消失
event.stopPropagation();
}
});
// 面包屑定位,需至于前,否则 return 的元素就无法进行面包屑定位
if (protyle.options.render.breadcrumb) {
protyle.breadcrumb.render(protyle, false, hasClosestBlock(event.target));
}
const range = getEditorRange(this.element);
// https://github.com/siyuan-note/siyuan/issues/12317
if (range.startContainer.nodeType !== 3 &&
(range.startContainer as Element).classList.contains("protyle-action") &&
range.startContainer.parentElement.classList.contains("code-block")) {
setFirstNodeRange(range.startContainer.parentElement.querySelector(".hljs").lastElementChild, range);
}
// 需放在嵌入块之前否则嵌入块内的引用、链接、pdf 双链无法点击打开 https://ld246.com/article/1630479789513
const aElement = hasClosestByAttribute(event.target, "data-type", "a") ||
hasClosestByClassName(event.target, "av__celltext--url"); // 数据库中资源文件、链接、电话、邮箱单元格
let aLink = aElement ? (aElement.getAttribute("data-href") || "") : "";
if (aElement && !aLink && aElement.classList.contains("av__celltext--url")) {
aLink = aElement.textContent.trim();
if (aElement.dataset.type === "phone") {
aLink = "tel:" + aLink;
} else if (aElement.dataset.type === "email") {
aLink = "mailto:" + aLink;
} else if (aElement.classList.contains("b3-chip")) {
aLink = aElement.dataset.url;
}
}
const blockRefElement = hasClosestByAttribute(event.target, "data-type", "block-ref");
if (blockRefElement || aLink.startsWith("siyuan://blocks/")) {
event.stopPropagation();
event.preventDefault();
hideElements(["dialog", "toolbar"], protyle);
if (range.toString() === "" || event.shiftKey) {
let refBlockId: string;
if (blockRefElement) {
refBlockId = blockRefElement.getAttribute("data-id");
} else if (aElement) {
refBlockId = aLink.substring(16, 38);
}
checkFold(refBlockId, (zoomIn, action, isRoot) => {
// 块引用跳转后需要短暂高亮目标块 https://github.com/siyuan-note/siyuan/issues/11542
if (!isRoot) {
action.push(Constants.CB_GET_HL);
}
/// #if MOBILE
mobileBlur = true;
activeBlur();
openMobileFileById(protyle.app, refBlockId, zoomIn ? [Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
/// #else
if (event.shiftKey) {
openFileById({
app: protyle.app,
id: refBlockId,
position: "bottom",
action,
zoomIn
});
window.dispatchEvent(new KeyboardEvent("keydown", {key: "Escape"}));
} else if (event.altKey) {
openFileById({
app: protyle.app,
id: refBlockId,
position: "right",
action,
zoomIn
});
} else if (ctrlIsPressed) {
openFileById({
app: protyle.app,
id: refBlockId,
keepCursor: true,
action: zoomIn ? [Constants.CB_GET_HL, Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL],
zoomIn
});
} else {
openFileById({
app: protyle.app,
id: refBlockId,
action,
zoomIn
});
}
/// #endif
});
/// #if !MOBILE
if (protyle.model) {
// 打开双链需记录到后退中 https://github.com/siyuan-note/insider/issues/801
let blockElement: HTMLElement | false;
if (blockRefElement) {
blockElement = hasClosestBlock(blockRefElement);
} else if (aElement) {
blockElement = hasClosestBlock(aElement);
}
if (blockElement) {
pushBack(protyle, getEditorRange(this.element), blockElement);
}
}
/// #endif
return;
}
}
/// #if MOBILE
// https://github.com/siyuan-note/siyuan/issues/10513
const virtualRefElement = hasClosestByAttribute(event.target, "data-type", "virtual-block-ref");
if (virtualRefElement && range.toString() === "") {
event.stopPropagation();
event.preventDefault();
const blockElement = hasClosestBlock(virtualRefElement);
if (blockElement) {
fetchPost("/api/block/getBlockDefIDsByRefText", {
anchor: virtualRefElement.textContent,
excludeIDs: [blockElement.getAttribute("data-node-id")]
}, (response) => {
checkFold(response.data.refDefs[0].refID, (zoomIn) => {
mobileBlur = true;
activeBlur();
openMobileFileById(protyle.app, response.data.refDefs[0].refID, zoomIn ? [Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
});
});
}
return;
}
/// #endif
const fileElement = hasClosestByAttribute(event.target, "data-type", "file-annotation-ref");
if (fileElement && range.toString() === "") {
event.stopPropagation();
event.preventDefault();
openLink(protyle, fileElement.getAttribute("data-id"), event, ctrlIsPressed);
return;
}
if (aElement &&
// https://github.com/siyuan-note/siyuan/issues/11980
(event.shiftKey || range.toString() === "") &&
// 如果aLink 为空时,当 data-type="a inline-math" 可继续后续操作
aLink) {
event.stopPropagation();
event.preventDefault();
openLink(protyle, aLink, event, ctrlIsPressed);
return;
}
if (aElement && aElement.classList.contains("av__celltext--url") && !aLink) {
let index = 0;
Array.from(aElement.parentElement.children).find((item, i) => {
if (item === aElement) {
index = i;
return true;
}
});
editAssetItem({
protyle,
cellElements: [aElement.parentElement],
blockElement: hasClosestBlock(aElement) as HTMLElement,
content: aElement.getAttribute("data-url"),
type: "file",
name: aElement.getAttribute("data-name"),
index,
rect: aElement.getBoundingClientRect()
});
return;
}
const tagElement = hasClosestByAttribute(event.target, "data-type", "tag");
if (tagElement && !event.altKey && !event.shiftKey && range.toString() === "") {
/// #if !MOBILE
openGlobalSearch(protyle.app, `#${tagElement.textContent}#`, !ctrlIsPressed, {method: 0});
hideElements(["dialog"]);
/// #else
popSearch(protyle.app, {
hasReplace: false,
method: 0,
hPath: "",
idPath: [],
k: `#${tagElement.textContent}#`,
r: "",
page: 1,
});
/// #endif
return;
}
const embedItemElement = hasClosestByClassName(event.target, "protyle-wysiwyg__embed");
if (embedItemElement) {
const embedId = embedItemElement.getAttribute("data-id");
checkFold(embedId, (zoomIn, action) => {
/// #if MOBILE
mobileBlur = true;
activeBlur();
openMobileFileById(protyle.app, embedId, zoomIn ? [Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT, Constants.CB_GET_ROOTSCROLL]);
/// #else
if (event.shiftKey) {
openFileById({
app: protyle.app,
id: embedId,
position: "bottom",
action,
zoomIn
});
} else if (event.altKey) {
openFileById({
app: protyle.app,
id: embedId,
position: "right",
action,
zoomIn
});
} else if (ctrlIsPressed) {
openFileById({
app: protyle.app,
id: embedId,
action: zoomIn ? [Constants.CB_GET_HL, Constants.CB_GET_ALL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT],
zoomIn,
keepCursor: true,
});
} else if (!protyle.disabled) {
window.siyuan.blockPanels.push(new BlockPanel({
app: protyle.app,
targetElement: embedItemElement,
isBacklink: false,
refDefs: [{refID: embedId}]
}));
}
/// #endif
});
// https://github.com/siyuan-note/siyuan/issues/12585
if (!ctrlIsPressed) {
event.stopPropagation();
return;
}
}
if (commonClick(event, protyle)) {
return;
}
if (hasTopClosestByClassName(event.target, "protyle-action__copy")) {
return;
}
const editElement = hasClosestByClassName(event.target, "protyle-action__edit");
if (editElement && !protyle.disabled) {
protyle.toolbar.showRender(protyle, editElement.parentElement.parentElement);
event.stopPropagation();
event.preventDefault();
return;
}
const menuElement = hasClosestByClassName(event.target, "protyle-action__menu");
if (menuElement) {
protyle.gutter.renderMenu(protyle, menuElement.parentElement.parentElement);
/// #if MOBILE
window.siyuan.menus.menu.fullscreen();
/// #else
const rect = menuElement.getBoundingClientRect();
window.siyuan.menus.menu.popup({
x: rect.left,
y: rect.top,
isLeft: true
});
/// #endif
event.stopPropagation();
event.preventDefault();
return;
}
const reloadElement = hasClosestByClassName(event.target, "protyle-action__reload");
if (reloadElement) {
const embedReloadElement = isInEmbedBlock(reloadElement);
if (embedReloadElement) {
embedReloadElement.removeAttribute("data-render");
blockRender(protyle, embedReloadElement);
}
event.stopPropagation();
event.preventDefault();
return;
}
const languageElement = hasClosestByClassName(event.target, "protyle-action__language");
if (languageElement && !protyle.disabled && !ctrlIsPressed) {
protyle.toolbar.showCodeLanguage(protyle, [languageElement]);
event.stopPropagation();
event.preventDefault();
return;
}
// 需放在属性后,否则数学公式无法点击属性;需放在 action 后,否则嵌入块的的 action 无法打开;需放在嵌入块后,否则嵌入块中的数学公式会被打开
const mathElement = hasClosestByAttribute(event.target, "data-subtype", "math");
if (!event.shiftKey && !ctrlIsPressed && mathElement && !protyle.disabled) {
protyle.toolbar.showRender(protyle, mathElement);
event.stopPropagation();
return;
}
const actionElement = hasClosestByClassName(event.target, "protyle-action");
if (actionElement) {
const type = actionElement.parentElement.parentElement.getAttribute("data-type");
if (type === "img" && !protyle.disabled) {
imgMenu(protyle, range, actionElement.parentElement.parentElement, {
clientX: event.clientX + 4,
clientY: event.clientY
});
event.stopPropagation();
return;
} else if (actionElement.parentElement.classList.contains("li")) {
const actionId = actionElement.parentElement.getAttribute("data-node-id");
if (event.altKey && !protyle.disabled) {
// 展开/折叠当前层级的所有列表项
if (actionElement.parentElement.parentElement.classList.contains("protyle-wysiwyg")) {
// 缩放列表项 https://ld246.com/article/1653123034794
setFold(protyle, actionElement.parentElement);
} else {
let hasFold = true;
const oldHTML = actionElement.parentElement.parentElement.outerHTML;
Array.from(actionElement.parentElement.parentElement.children).find((listItemElement) => {
if (listItemElement.classList.contains("li")) {
if (listItemElement.getAttribute("fold") !== "1" && listItemElement.childElementCount > 3) {
hasFold = false;
return true;
}
}
});
Array.from(actionElement.parentElement.parentElement.children).find((listItemElement) => {
if (listItemElement.classList.contains("li")) {
if (hasFold) {
listItemElement.removeAttribute("fold");
} else if (listItemElement.childElementCount > 3) {
listItemElement.setAttribute("fold", "1");
}
}
});
updateTransaction(protyle, actionElement.parentElement.parentElement.getAttribute("data-node-id"), actionElement.parentElement.parentElement.outerHTML, oldHTML);
}
hideElements(["gutter"], protyle);
} else if (event.shiftKey && !protyle.disabled) {
openAttr(actionElement.parentElement, "bookmark", protyle);
} else if (ctrlIsPressed) {
zoomOut({protyle, id: actionId});
} else {
if (actionElement.classList.contains("protyle-action--task")) {
if (!protyle.disabled) {
const html = actionElement.parentElement.outerHTML;
if (actionElement.parentElement.classList.contains("protyle-task--done")) {
actionElement.querySelector("use").setAttribute("xlink:href", "#iconUncheck");
actionElement.parentElement.classList.remove("protyle-task--done");
} else {
actionElement.querySelector("use").setAttribute("xlink:href", "#iconCheck");
actionElement.parentElement.classList.add("protyle-task--done");
}
actionElement.parentElement.setAttribute("updated", dayjs().format("YYYYMMDDHHmmss"));
updateTransaction(protyle, actionId, actionElement.parentElement.outerHTML, html);
}
} else if (window.siyuan.config.editor.listItemDotNumberClickFocus) {
if (protyle.block.showAll && protyle.block.id === actionId) {
enterBack(protyle, actionId);
} else {
zoomOut({protyle, id: actionId});
}
}
}
event.stopPropagation();
return;
}
}
const selectElement = hasClosestByClassName(event.target, "hr") ||
hasClosestByClassName(event.target, "iframe");
if (!event.shiftKey && !ctrlIsPressed && selectElement) {
selectElement.classList.add("protyle-wysiwyg--select");
globalClickHideMenu(event.target);
event.stopPropagation();
return;
}
const imgElement = hasTopClosestByClassName(event.target, "img");
if (!event.shiftKey && !ctrlIsPressed && imgElement) {
imgElement.classList.add("img--select");
const nextSibling = hasNextSibling(imgElement);
if (nextSibling) {
if (nextSibling.textContent.startsWith(Constants.ZWSP)) {
range.setStart(nextSibling, 1);
} else {
range.setStart(nextSibling, 0);
}
range.collapse(true);
focusByRange(range);
// 需等待 range 更新再次进行渲染
if (protyle.options.render.breadcrumb) {
protyle.breadcrumb.render(protyle);
}
}
return;
}
const emojiElement = hasTopClosestByClassName(event.target, "emoji");
if (!protyle.disabled && !event.shiftKey && !ctrlIsPressed && emojiElement) {
const nodeElement = hasClosestBlock(emojiElement);
if (nodeElement) {
const emojiRect = emojiElement.getBoundingClientRect();
openEmojiPanel("", "av", {
x: emojiRect.left,
y: emojiRect.bottom,
h: emojiRect.height,
w: emojiRect.width
}, (unicode) => {
emojiElement.insertAdjacentHTML("afterend", "<wbr>");
const oldHTML = nodeElement.outerHTML;
let emojiHTML;
if (unicode.startsWith("api/icon/getDynamicIcon")) {
emojiHTML = `<img class="emoji" src="${unicode}"/>`;
} else if (unicode.indexOf(".") > -1) {
const emojiList = unicode.split(".");
emojiHTML = `<img alt="${emojiList[0]}" class="emoji" src="/emojis/${unicode}" title="${emojiList[0]}">`;
} else {
emojiHTML = unicode2Emoji(unicode);
}
emojiElement.outerHTML = emojiHTML;
hideElements(["dialog"]);
updateTransaction(protyle, nodeElement.getAttribute("data-node-id"), nodeElement.outerHTML, oldHTML);
focusByWbr(nodeElement, range);
}, emojiElement);
}
return;
}
if (avClick(protyle, event)) {
return;
}
setTimeout(() => {
// 选中后,在选中的文字上点击需等待 range 更新
let newRange = getEditorRange(this.element);
// https://github.com/siyuan-note/siyuan/issues/10357
const attrElement = hasClosestByClassName(newRange.endContainer, "protyle-attr");
if (attrElement) {
newRange = setLastNodeRange(attrElement.previousElementSibling, newRange, false);
}
// https://github.com/siyuan-note/siyuan/issues/14481
const inlineMathElement = hasClosestByAttribute(newRange.startContainer, "data-type", "inline-math");
if (inlineMathElement) {
newRange.setEndAfter(inlineMathElement);
newRange.collapse(false);
focusByRange(newRange);
}
/// #if !MOBILE
if (newRange.toString().replace(Constants.ZWSP, "") !== "") {
protyle.toolbar.render(protyle, newRange);
} else {
// https://github.com/siyuan-note/siyuan/issues/9785
protyle.toolbar.range = newRange;
}
/// #endif
if (!protyle.wysiwyg.element.querySelector(".protyle-wysiwyg--select")) {
countSelectWord(newRange, protyle.block.rootID);
}
if (getSelection().rangeCount === 0 && !mobileBlur) {
// TODO https://github.com/siyuan-note/siyuan/issues/14589 点击 video 也可以测试一下 https://github.com/siyuan-note/siyuan/issues/14569
// https://github.com/siyuan-note/siyuan/issues/5901
focusByRange(newRange);
}
/// #if !MOBILE
pushBack(protyle, newRange);
/// #endif
mobileBlur = false;
}, (isMobile() || isInIOS()) ? 520 : 0); // Android/iPad 双击慢了出不来
protyle.hint.enableExtend = false;
if (event.shiftKey) {
event.preventDefault();
event.stopPropagation();
// shift 多选
let startElement = this.shiftStartElement;
let endElement = hasClosestBlock(event.target);
if (this.shiftStartElement && endElement && !this.shiftStartElement.isSameNode(endElement)) {
let toDown = true;
range.collapse(true);
const startRect = startElement.getBoundingClientRect();
const endRect = endElement.getBoundingClientRect();
let startTop = startRect.top;
let endTop = endRect.top;
if (startTop === endTop) {
// 横排 https://ld246.com/article/1663036247544
startTop = startRect.left;
endTop = endRect.left;
}
if (startTop > endTop) {
const tempElement = endElement;
endElement = startElement;
startElement = tempElement;
const tempTop = endTop;
endTop = startTop;
startTop = tempTop;
toDown = false;
}
let selectElements: Element[] = [];
let currentElement: HTMLElement = startElement;
let hasJump = false;
while (currentElement) {
if (currentElement && !currentElement.classList.contains("protyle-attr")) {
const currentRect = currentElement.getBoundingClientRect();
if (startRect.top === endRect.top ? (currentRect.left <= endTop) : (currentRect.top <= endTop)) {
if (hasJump) {
// 父节点的下个节点在选中范围内才可使用父节点作为选中节点
if (currentElement.nextElementSibling && !currentElement.nextElementSibling.classList.contains("protyle-attr")) {
const currentNextRect = currentElement.nextElementSibling.getBoundingClientRect();
if (startRect.top === endRect.top ?
(currentNextRect.left <= endTop && currentNextRect.bottom <= endRect.bottom) :
(currentNextRect.top <= endTop)) {
selectElements = [currentElement];
currentElement = currentElement.nextElementSibling as HTMLElement;
hasJump = false;
} else if (currentElement.parentElement.classList.contains("sb")) {
currentElement = hasClosestBlock(currentElement.parentElement) as HTMLElement;
hasJump = true;
} else {
break;
}
} else {
currentElement = hasClosestBlock(currentElement.parentElement) as HTMLElement;
hasJump = true;
}
} else {
selectElements.push(currentElement);
currentElement = currentElement.nextElementSibling as HTMLElement;
}
} else if (currentElement.parentElement.classList.contains("sb")) {
// 跳出超级块横向排版中的未选中元素
currentElement = hasClosestBlock(currentElement.parentElement) as HTMLElement;
hasJump = true;
} else {
break;
}
} else {
currentElement = hasClosestBlock(currentElement.parentElement) as HTMLElement;
hasJump = true;
}
}
if (selectElements.length === 1 && !selectElements[0].classList.contains("list") && !selectElements[0].classList.contains("bq") && !selectElements[0].classList.contains("sb")) {
// 单个 p 不选中
this.shiftStartElement = undefined;
} else {
const ids: string[] = [];
if (!protyle.wysiwyg.element.querySelector(".protyle-wysiwyg--select") && protyle.scroll && !protyle.scroll.element.classList.contains("fn__none") && !protyle.scroll.keepLazyLoad &&
(startElement.getBoundingClientRect().top < -protyle.contentElement.clientHeight * 2 || endElement.getBoundingClientRect().bottom > protyle.contentElement.clientHeight * 2)) {
showMessage(window.siyuan.languages.crossKeepLazyLoad);
}
selectElements.forEach(item => {
item.classList.add("protyle-wysiwyg--select");
ids.push(item.getAttribute("data-node-id"));
// 清除选中的子块 https://ld246.com/article/1667826582251
item.querySelectorAll(".protyle-wysiwyg--select").forEach(subItem => {
subItem.classList.remove("protyle-wysiwyg--select");
});
});
countBlockWord(ids);
if (toDown) {
focusBlock(selectElements[selectElements.length - 1], protyle.wysiwyg.element, false);
} else {
focusBlock(selectElements[0], protyle.wysiwyg.element, false);
}
}
}
}
if (this.element.querySelector(".protyle-wysiwyg--select") && range.toString() !== "") {
// 选中块后,文字不能被选中。需在 shift click 之后防止shift点击单个块出现文字选中
range.collapse(false);
focusByRange(range);
}
if (ctrlIsPressed && range.toString() === "" && !event.shiftKey && !event.altKey) {
let ctrlElement = hasClosestBlock(event.target);
if (ctrlElement) {
const embedBlockElement = isInEmbedBlock(ctrlElement);
if (embedBlockElement) {
ctrlElement = embedBlockElement;
}
ctrlElement = getTopAloneElement(ctrlElement) as HTMLElement;
if (ctrlElement.classList.contains("protyle-wysiwyg--select")) {
ctrlElement.classList.remove("protyle-wysiwyg--select");
ctrlElement.removeAttribute("select-start");
ctrlElement.removeAttribute("select-end");
} else {
ctrlElement.classList.add("protyle-wysiwyg--select");
}
ctrlElement.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
item.classList.remove("protyle-wysiwyg--select");
item.removeAttribute("select-start");
item.removeAttribute("select-end");
});
const ctrlParentElement = hasClosestByClassName(ctrlElement.parentElement, "protyle-wysiwyg--select");
if (ctrlParentElement) {
ctrlParentElement.classList.remove("protyle-wysiwyg--select");
ctrlParentElement.removeAttribute("select-start");
ctrlParentElement.removeAttribute("select-end");
}
const ids: string[] = [];
protyle.wysiwyg.element.querySelectorAll(".protyle-wysiwyg--select").forEach(item => {
ids.push(item.getAttribute("data-node-id"));
});
countBlockWord(ids);
}
}
});
}
}