A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 247 lines 8.5 kB view raw
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};