A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
1import {fetchPost, fetchSyncPost} from "../../util/fetch";
2import {Constants} from "../../constants";
3import {focusByRange, focusByWbr} from "../util/selection";
4import {writeText} from "../util/compatibility";
5
6export const previewTemplate = (pathString: string, element: Element, parentId: string) => {
7 if (!pathString) {
8 element.innerHTML = "";
9 return;
10 }
11 fetchPost("/api/template/render", {
12 id: parentId,
13 path: pathString,
14 preview: true
15 }, (response) => {
16 element.innerHTML = `<div class="protyle-wysiwyg" style="padding: 8px">${response.data.content.replace(/contenteditable="true"/g, "")}</div>`;
17 });
18};
19
20const mergeElement = (a: Element, b: Element, after = true) => {
21 if (!a.getAttribute("data-type") || !b.getAttribute("data-type")) {
22 return false;
23 }
24 a.setAttribute("data-type", a.getAttribute("data-type").replace("search-mark", "").trim());
25 b.setAttribute("data-type", b.getAttribute("data-type").replace("search-mark", "").trim());
26 const attributes = a.attributes;
27 let isMatch = true;
28 for (let i = 0; i < attributes.length; i++) {
29 if (b.getAttribute(attributes[i].name) !== attributes[i].value) {
30 isMatch = false;
31 }
32 }
33
34 if (isMatch) {
35 if (after) {
36 a.innerHTML = a.innerHTML + b.innerHTML;
37 } else {
38 a.innerHTML = b.innerHTML + a.innerHTML;
39 }
40 b.remove();
41 }
42 return isMatch;
43};
44
45export const removeSearchMark = (element: HTMLElement) => {
46 let previousElement = element.previousSibling as HTMLElement;
47 while (previousElement && previousElement.nodeType !== 3) {
48 if (!mergeElement(element, previousElement, false)) {
49 break;
50 } else {
51 previousElement = element.previousSibling as HTMLElement;
52 }
53 }
54 let nextElement = element.nextSibling as HTMLElement;
55 while (nextElement && nextElement.nodeType !== 3) {
56 if (!mergeElement(element, nextElement)) {
57 break;
58 } else {
59 nextElement = element.nextSibling as HTMLElement;
60 }
61 }
62
63 if ((element.getAttribute("data-type") || "").includes("search-mark")) {
64 element.setAttribute("data-type", element.getAttribute("data-type").replace("search-mark", "").trim());
65 }
66};
67
68export const removeInlineType = (inlineElement: HTMLElement, type: string, range?: Range) => {
69 const types = inlineElement.getAttribute("data-type").split(" ");
70 if (types.length === 1) {
71 const linkParentElement = inlineElement.parentElement;
72 inlineElement.outerHTML = inlineElement.innerHTML.replace(Constants.ZWSP, "") + "<wbr>";
73 if (range) {
74 focusByWbr(linkParentElement, range);
75 }
76 } else {
77 types.find((itemType, index) => {
78 if (type === itemType) {
79 types.splice(index, 1);
80 return true;
81 }
82 });
83 inlineElement.setAttribute("data-type", types.join(" "));
84 if (type === "a") {
85 inlineElement.removeAttribute("data-href");
86 } else if (type === "file-annotation-ref") {
87 inlineElement.removeAttribute("data-id");
88 } else if (type === "block-ref") {
89 inlineElement.removeAttribute("data-id");
90 inlineElement.removeAttribute("data-subtype");
91 }
92 if (range) {
93 range.selectNodeContents(inlineElement);
94 range.collapse(false);
95 focusByRange(range);
96 }
97 }
98};
99
100export const toolbarKeyToMenu = (toolbar: Array<string | IMenuItem>) => {
101 const toolbarItem: IMenuItem [] = [{
102 name: "block-ref",
103 hotkey: window.siyuan.config.keymap.editor.insert.ref.custom,
104 lang: "ref",
105 icon: "iconRef",
106 tipPosition: "ne",
107 }, {
108 name: "a",
109 hotkey: window.siyuan.config.keymap.editor.insert.link.custom,
110 lang: "link",
111 icon: "iconLink",
112 tipPosition: "n",
113 }, {
114 name: "strong",
115 lang: "bold",
116 hotkey: window.siyuan.config.keymap.editor.insert.bold.custom,
117 icon: "iconBold",
118 tipPosition: "n",
119 }, {
120 name: "em",
121 lang: "italic",
122 hotkey: window.siyuan.config.keymap.editor.insert.italic.custom,
123 icon: "iconItalic",
124 tipPosition: "n",
125 }, {
126 name: "u",
127 lang: "underline",
128 hotkey: window.siyuan.config.keymap.editor.insert.underline.custom,
129 icon: "iconUnderline",
130 tipPosition: "n",
131 }, {
132 name: "s",
133 lang: "strike",
134 hotkey: window.siyuan.config.keymap.editor.insert.strike.custom,
135 icon: "iconStrike",
136 tipPosition: "n",
137 }, {
138 name: "mark",
139 lang: "mark",
140 hotkey: window.siyuan.config.keymap.editor.insert.mark.custom,
141 icon: "iconMark",
142 tipPosition: "n",
143 }, {
144 name: "sup",
145 lang: "sup",
146 hotkey: window.siyuan.config.keymap.editor.insert.sup.custom,
147 icon: "iconSup",
148 tipPosition: "n",
149 }, {
150 name: "sub",
151 lang: "sub",
152 hotkey: window.siyuan.config.keymap.editor.insert.sub.custom,
153 icon: "iconSub",
154 tipPosition: "n",
155 }, {
156 name: "kbd",
157 lang: "kbd",
158 hotkey: window.siyuan.config.keymap.editor.insert.kbd.custom,
159 icon: "iconKeymap",
160 tipPosition: "n",
161 }, {
162 name: "tag",
163 lang: "tag",
164 hotkey: window.siyuan.config.keymap.editor.insert.tag.custom,
165 icon: "iconTags",
166 tipPosition: "n",
167 }, {
168 name: "code",
169 lang: "inline-code",
170 hotkey: window.siyuan.config.keymap.editor.insert["inline-code"].custom,
171 icon: "iconInlineCode",
172 tipPosition: "n",
173 }, {
174 name: "inline-math",
175 lang: "inline-math",
176 hotkey: window.siyuan.config.keymap.editor.insert["inline-math"].custom,
177 icon: "iconMath",
178 tipPosition: "n",
179 }, {
180 name: "inline-memo",
181 lang: "memo",
182 hotkey: window.siyuan.config.keymap.editor.insert.memo.custom,
183 icon: "iconM",
184 tipPosition: "n",
185 }, {
186 name: "text",
187 lang: "appearance",
188 hotkey: window.siyuan.config.keymap.editor.insert.appearance.custom,
189 icon: "iconFont",
190 tipPosition: "n",
191 }, {
192 name: "clear",
193 lang: "clearInline",
194 hotkey: window.siyuan.config.keymap.editor.insert.clearInline.custom,
195 icon: "iconClear",
196 tipPosition: "n",
197 }, {
198 name: "|",
199 }];
200 const toolbarResult: IMenuItem[] = [];
201 toolbar.forEach((menuItem: IMenuItem) => {
202 let currentMenuItem = menuItem;
203 toolbarItem.find((defaultMenuItem: IMenuItem) => {
204 if (typeof menuItem === "string" && defaultMenuItem.name === menuItem) {
205 currentMenuItem = defaultMenuItem;
206 return true;
207 }
208 if (typeof menuItem === "object" && defaultMenuItem.name === menuItem.name) {
209 currentMenuItem = Object.assign({}, defaultMenuItem, menuItem);
210 return true;
211 }
212 });
213 toolbarResult.push(currentMenuItem);
214 });
215 return toolbarResult;
216};
217
218export const copyTextByType = async (ids: string[],
219 type: "ref" | "blockEmbed" | "protocol" | "protocolMd" | "hPath" | "id") => {
220 let text = "";
221 for (let i = 0; i < ids.length; i++) {
222 const id = ids[i];
223 if (ids.length > 1) {
224 text += "- ";
225 }
226 if (type === "ref") {
227 const response = await fetchSyncPost("/api/block/getRefText", {id});
228 text += `((${id} '${response.data}'))`;
229 } else if (type === "blockEmbed") {
230 text += `{{select * from blocks where id='${id}'}}`;
231 } else if (type === "protocol") {
232 text += `siyuan://blocks/${id}`;
233 } else if (type === "protocolMd") {
234 const response = await fetchSyncPost("/api/block/getRefText", {id});
235 text += `[${response.data}](siyuan://blocks/${id})`;
236 } else if (type === "hPath") {
237 const response = await fetchSyncPost("/api/filetree/getHPathByID", {id});
238 text += response.data;
239 } else if (type === "id") {
240 text += id;
241 }
242 if (ids.length > 1 && i !== ids.length - 1) {
243 text += "\n";
244 }
245 }
246 writeText(text);
247};