siyuan/app/src/protyle/index.ts

247 lines
11 KiB
TypeScript
Raw Normal View History

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);
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);
2022-08-31 13:19:03 +08:00
});
} 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;
2022-09-30 00:13:07 +08:00
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);
}
}