siyuan/app/src/protyle/index.ts

246 lines
11 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 {Constants} from "../constants";
import {Hint} from "./hint";
import {setLute} from "./markdown/setLute";
import {Preview} from "./preview";
import {initUI, setPadding} from "./ui/initUI";
import {Undo} from "./undo";
import {Upload} from "./upload";
import {Options} from "./util/Options";
import {destroy} from "./util/destroy";
import {Scroll} from "./scroll";
import {Model} from "../layout/Model";
import {genUUID} from "../util/genID";
import {WYSIWYG} from "./wysiwyg";
import {Toolbar} from "./toolbar";
import {Gutter} from "./gutter";
import {Breadcrumb} from "./breadcrumb";
import {onTransaction} from "./wysiwyg/transaction";
import {fetchPost} from "../util/fetch";
/// #if !MOBILE
import {Title} from "./header/Title";
import {updatePanelByEditor} from "../editor/util";
import {setPanelFocus} from "../layout/util";
/// #endif
import {Background} from "./header/Background";
import {onGet} from "./util/onGet";
import {reloadProtyle} from "./util/reload";
import {renderBacklink} from "./wysiwyg/renderBacklink";
import {showKeyboardToolbar} from "../mobile/util/showKeyboardToolbar";
export class Protyle {
public readonly version: string;
public protyle: IProtyle;
/**
* @param id 要挂载 Protyle 的元素或者元素 ID。
* @param options Protyle 参数
*/
constructor(id: HTMLElement, options?: IOptions) {
this.version = Constants.SIYUAN_VERSION;
const getOptions = new Options(options);
const mergedOptions = getOptions.merge();
this.protyle = {
transactionTime: new Date().getTime(),
id: genUUID(),
disabled: false,
updated: false,
element: id,
options: mergedOptions,
block: {},
};
this.protyle.hint = new Hint(this.protyle);
if (mergedOptions.render.breadcrumb) {
this.protyle.breadcrumb = new Breadcrumb(this.protyle);
}
/// #if !MOBILE
if (mergedOptions.render.title) {
this.protyle.title = new Title(this.protyle);
}
/// #endif
if (mergedOptions.render.background) {
this.protyle.background = new Background(this.protyle);
}
this.protyle.element.innerHTML = "";
this.protyle.element.classList.add("protyle");
if (mergedOptions.render.breadcrumb) {
this.protyle.element.appendChild(this.protyle.breadcrumb.element.parentElement);
}
this.protyle.undo = new Undo();
this.protyle.wysiwyg = new WYSIWYG(this.protyle);
this.protyle.toolbar = new Toolbar(this.protyle);
this.protyle.scroll = new Scroll(this.protyle); // 不能使用 render.scroll 来判读是否初始化,除非重构后面用到的相关变量
if (this.protyle.options.render.gutter) {
this.protyle.gutter = new Gutter(this.protyle);
}
if (mergedOptions.upload.url || mergedOptions.upload.handler) {
this.protyle.upload = new Upload();
}
this.init();
if (!mergedOptions.action.includes(Constants.CB_GET_HISTORY)) {
this.protyle.ws = new Model({
id: this.protyle.id,
type: "protyle",
msgCallback: (data) => {
switch (data.cmd) {
case "transactions":
data.data[0].doOperations.forEach((item: IOperation) => {
onTransaction(this.protyle, item, false);
});
break;
case "heading2doc":
case "li2doc":
if (this.protyle.block.rootID === data.data.srcRootBlockID) {
if (this.protyle.block.showAll && data.cmd === "heading2doc" && !this.protyle.options.backlinkData) {
fetchPost("/api/filetree/getDoc", {
id: this.protyle.block.rootID,
size: window.siyuan.config.editor.dynamicLoadBlocks,
}, getResponse => {
onGet(getResponse, this.protyle);
});
} else {
reloadProtyle(this.protyle);
}
/// #if !MOBILE
if (data.cmd === "heading2doc") {
// 文档标题互转后,需更新大纲
updatePanelByEditor(this.protyle, false, false, true);
}
/// #endif
}
break;
case "rename":
if (this.protyle.path === data.data.path) {
if (this.protyle.model) {
this.protyle.model.parent.updateTitle(data.data.title);
}
if (this.protyle.background) {
this.protyle.background.ial.title = data.data.title;
}
}
if (this.protyle.options.render.title && this.protyle.block.parentID === data.data.id &&
this.protyle.title.editElement.textContent !== data.data.title) {
this.protyle.title.setTitle(data.data.title);
}
// update ref
this.protyle.wysiwyg.element.querySelectorAll(`[data-type~="block-ref"][data-id="${data.data.id}"]`).forEach(item => {
if (item.getAttribute("data-subtype") === "d") {
item.textContent = data.data.refText;
}
});
break;
case "moveDoc":
if (this.protyle.path === data.data.fromPath) {
this.protyle.path = data.data.newPath;
this.protyle.notebookId = data.data.toNotebook;
}
break;
case "unmount":
if (this.protyle.model && this.protyle.notebookId === data.data.box) {
this.protyle.model.parent.parent.removeTab(this.protyle.model.parent.id, false, false);
}
break;
case "removeDoc":
if (this.protyle.model && data.data.ids.includes(this.protyle.block.rootID)) {
this.protyle.model.parent.parent.removeTab(this.protyle.model.parent.id, false, false);
}
break;
}
}
});
setPadding(this.protyle);
if (options.backlinkData) {
this.protyle.block.rootID = options.blockId;
renderBacklink(this.protyle, options.backlinkData);
return;
}
fetchPost("/api/filetree/getDoc", {
id: options.blockId,
k: options.key || "",
mode: (mergedOptions.action && mergedOptions.action.includes(Constants.CB_GET_CONTEXT)) ? 3 : 0, // 0: 仅当前 ID默认值1向上 2向下3上下都加载4加载最后
size: mergedOptions.action?.includes(Constants.CB_GET_ALL) ? Constants.SIZE_GET_MAX : window.siyuan.config.editor.dynamicLoadBlocks,
}, getResponse => {
onGet(getResponse, this.protyle, mergedOptions.action, options.scrollAttr);
if (this.protyle.model) {
/// #if !MOBILE
if (mergedOptions.action?.includes(Constants.CB_GET_FOCUS)) {
setPanelFocus(this.protyle.model.element.parentElement.parentElement);
}
updatePanelByEditor(this.protyle, false);
/// #endif
}
// 需等待 getDoc 完成后再执行,否则在无页签的时候 updatePanelByEditor 会执行2次
// 只能用 focusin否则点击表格无法执行
this.protyle.wysiwyg.element.addEventListener("focusin", () => {
/// #if !MOBILE
if (this.protyle && this.protyle.model) {
let needUpdate = true;
if (this.protyle.model.element.parentElement.parentElement.classList.contains("layout__wnd--active") && this.protyle.model.headElement.classList.contains("item--focus")) {
needUpdate = false;
}
if (!needUpdate) {
return;
}
setPanelFocus(this.protyle.model.element.parentElement.parentElement);
updatePanelByEditor(this.protyle, false);
} else {
// 悬浮层应移除其余面板高亮,否则按键会被面板监听到
document.querySelectorAll(".layout__tab--active").forEach(item => {
item.classList.remove("layout__tab--active");
});
document.querySelectorAll(".layout__wnd--active").forEach(item => {
item.classList.remove("layout__wnd--active");
});
}
/// #else
showKeyboardToolbar();
/// #endif
});
// 需等渲染完后再回调,用于定位搜索字段 https://github.com/siyuan-note/siyuan/issues/3171
if (mergedOptions.after) {
mergedOptions.after(this);
}
});
}
}
private init() {
this.protyle.lute = setLute({
emojiSite: this.protyle.options.hint.emojiPath,
emojis: this.protyle.options.hint.emoji,
headingAnchor: false,
listStyle: this.protyle.options.preview.markdown.listStyle,
paragraphBeginningSpace: this.protyle.options.preview.markdown.paragraphBeginningSpace,
sanitize: this.protyle.options.preview.markdown.sanitize,
});
this.protyle.preview = new Preview(this.protyle);
initUI(this.protyle);
}
/** 聚焦到编辑器 */
public focus() {
this.protyle.wysiwyg.element.focus();
}
/** 上传是否还在进行中 */
public isUploading() {
return this.protyle.upload.isUploading;
}
/** 清空 undo & redo 栈 */
public clearStack() {
this.protyle.undo.clear();
}
/** 销毁编辑器 */
public destroy() {
destroy(this.protyle);
}
}