import * as path from "path"; import {fetchPost} from "./fetch"; import {Dialog} from "../dialog"; import {escapeHtml} from "./escape"; import {getSearch, isMobile} from "./functions"; import {focusByRange} from "../protyle/util/selection"; import {unicode2Emoji} from "../emoji"; import {Constants} from "../constants"; import {showMessage} from "../dialog/message"; export const getIdZoomInByPath = () => { const searchParams = new URLSearchParams(window.location.search); const PWAURL = searchParams.get("url"); let id = ""; let isZoomIn = false; if (/^web\+siyuan:\/\/blocks\/\d{14}-\w{7}/.test(PWAURL)) { // PWA 捕获 web+siyuan://blocks/20221031001313-rk7sd0e?focus=1 id = PWAURL.substring(20, 20 + 22); isZoomIn = getSearch("focus", PWAURL) === "1"; } else if (window.JSAndroid) { // PAD 通过思源协议打开 const SYURL = window.JSAndroid.getBlockURL(); id = getIdFromSYProtocol(SYURL); isZoomIn = getSearch("focus", SYURL) === "1"; } else { // 支持通过 URL 查询字符串参数 `id` 和 `focus` 跳转到 Web 端指定块 https://github.com/siyuan-note/siyuan/pull/7086 id = searchParams.get("id"); isZoomIn = searchParams.get("focus") === "1"; } return { id, isZoomIn }; }; export const isSYProtocol = (url: string) => { return /^siyuan:\/\/blocks\/\d{14}-\w{7}/.test(url); }; export const getIdFromSYProtocol = (url: string) => { return url.substring(16, 16 + 22); }; /* redirect to auth page */ export const redirectToCheckAuth = (to: string = window.location.href) => { const url = new URL(window.location.origin); url.pathname = "/check-auth"; url.searchParams.set("to", to); window.location.href = url.href; }; export const addBaseURL = () => { let baseURLElement = document.getElementById("baseURL"); if (!baseURLElement) { baseURLElement = document.createElement("base"); baseURLElement.id = "baseURL"; } baseURLElement.setAttribute("href", location.origin); document.getElementsByTagName("head")[0].appendChild(baseURLElement); }; export const getDisplayName = (filePath: string, basename = true, removeSY = false) => { let name = filePath; if (basename) { name = pathPosix().basename(filePath); } if (removeSY && name.endsWith(".sy")) { name = name.substr(0, name.length - 3); } return name; }; export const getAssetName = (assetPath: string) => { return assetPath.substring(7, assetPath.length - pathPosix().extname(assetPath).length - 23); }; export const isLocalPath = (link: string) => { if (!link) { return false; } link = link.trim(); if (1 > link.length) { return false; } link = link.toLowerCase(); if (link.startsWith("assets/") || link.startsWith("file://") || link.startsWith("\\\\") /* Windows 网络共享路径 */) { return true; } const colonIdx = link.indexOf(":"); return 1 === colonIdx; // 冒号前面只有一个字符认为是 Windows 盘符而不是网络协议 }; export const pathPosix = () => { if (path.posix) { return path.posix; } return path; }; export const originalPath = () => { return path; }; export const getTopPaths = (liElements: Element[]) => { const fromPaths: string[] = []; liElements.forEach((item: HTMLElement) => { if (item.getAttribute("data-type") !== "navigation-root") { const dataPath = item.getAttribute("data-path"); const isChild = fromPaths.find(item => { if (dataPath.startsWith(item.replace(".sy", ""))) { return true; } }); if (!isChild) { fromPaths.push(dataPath); } } }); return fromPaths; }; export const moveToPath = (fromPaths: string[], toNotebook: string, toPath: string) => { fetchPost("/api/filetree/moveDocs", { toNotebook, fromPaths, toPath, }); }; export const movePathTo = (cb: (toPath: string[], toNotebook: string[]) => void, paths?: string[], range?: Range, title?: string, flashcard = false) => { const exitDialog = window.siyuan.dialogs.find((item) => { if (item.element.querySelector("#foldList")) { item.destroy(); return true; } }); if (exitDialog) { return; } const dialog = new Dialog({ title: `${title || window.siyuan.languages.move}
`, content: `
`, width: isMobile() ? "90vw" : "50vw", destroyCallback() { if (range) { focusByRange(range); } } }); if (paths && paths.length > 0) { fetchPost("/api/filetree/getHPathsByPaths", {paths}, (response) => { dialog.element.querySelector(".b3-dialog__header .ft__smaller").innerHTML = escapeHtml(response.data.join(" ")); }); } const searchListElement = dialog.element.querySelector("#foldList"); const searchTreeElement = dialog.element.querySelector("#foldTree"); setNoteBook((notebooks) => { let html = ""; notebooks.forEach((item) => { if (!item.closed) { html += ``; } }); searchTreeElement.innerHTML = html; }, flashcard); const inputElement = dialog.element.querySelector(".b3-text-field") as HTMLInputElement; inputElement.focus(); const inputEvent = (event?: InputEvent) => { if (event && event.isComposing) { return; } if (inputElement.value.trim() === "") { searchListElement.classList.add("fn__none"); searchTreeElement.classList.remove("fn__none"); return; } searchTreeElement.classList.add("fn__none"); searchListElement.classList.remove("fn__none"); searchListElement.scrollTo(0, 0); fetchPost("/api/filetree/searchDocs", { k: inputElement.value, flashcard, }, (data) => { let fileHTML = ""; data.data.forEach((item: { boxIcon: string, box: string, hPath: string, path: string }) => { fileHTML += `
  • ${unicode2Emoji(item.boxIcon || Constants.SIYUAN_IMAGE_NOTE, false, "b3-list-item__graphic", true)} ${escapeHtml(item.hPath)}
  • `; }); searchListElement.innerHTML = fileHTML; }); }; inputEvent(); inputElement.addEventListener("compositionend", (event: InputEvent) => { inputEvent(event); }); inputElement.addEventListener("input", (event: InputEvent) => { inputEvent(event); }); const lineHeight = 28; inputElement.addEventListener("keydown", (event: KeyboardEvent) => { if (event.isComposing) { return; } const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement; const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus"); if (currentItemElements.length === 0) { return; } let currentItemElement: HTMLElement = currentItemElements[0] as HTMLElement; if (event.key.startsWith("Arrow")) { currentItemElements.forEach((item, index) => { if (index !== 0) { item.classList.remove("b3-list-item--focus"); } }); } if (searchListElement.classList.contains("fn__none")) { if ((event.key === "ArrowRight" && !currentItemElement.querySelector(".b3-list-item__arrow--open") && !currentItemElement.querySelector(".b3-list-item__toggle").classList.contains("fn__hidden")) || (event.key === "ArrowLeft" && currentItemElement.querySelector(".b3-list-item__arrow--open"))) { getLeaf(currentItemElement, flashcard); event.preventDefault(); return; } if (event.key === "ArrowLeft") { let parentElement = currentItemElement.parentElement.previousElementSibling; if (parentElement) { if (parentElement.tagName !== "LI") { parentElement = currentPanelElement.querySelector(".b3-list-item"); } currentItemElement.classList.remove("b3-list-item--focus"); parentElement.classList.add("b3-list-item--focus"); const parentRect = parentElement.getBoundingClientRect(); const fileRect = currentPanelElement.getBoundingClientRect(); if (parentRect.top < fileRect.top || parentRect.bottom > fileRect.bottom) { parentElement.scrollIntoView(parentRect.top < fileRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowDown" || event.key === "ArrowRight") { let nextElement = currentItemElement; while (nextElement) { if (nextElement.nextElementSibling) { if (nextElement.nextElementSibling.classList.contains("fn__none")) { nextElement = nextElement.nextElementSibling as HTMLElement; } else { if (nextElement.nextElementSibling.tagName === "UL") { nextElement = nextElement.nextElementSibling.firstElementChild as HTMLElement; } else { nextElement = nextElement.nextElementSibling as HTMLElement; } break; } } else { if (nextElement.parentElement.id === "foldTree") { break; } else { nextElement = nextElement.parentElement; } } } if (nextElement.classList.contains("b3-list-item")) { currentItemElement.classList.remove("b3-list-item--focus"); nextElement.classList.add("b3-list-item--focus"); const nextRect = nextElement.getBoundingClientRect(); const fileRect = searchTreeElement.getBoundingClientRect(); if (nextRect.top < fileRect.top || nextRect.bottom > fileRect.bottom) { nextElement.scrollIntoView(nextRect.top < fileRect.top); } } event.preventDefault(); return true; } if (event.key === "ArrowUp") { let previousElement = currentItemElement; while (previousElement) { if (previousElement.previousElementSibling) { if (previousElement.previousElementSibling.classList.contains("fn__none")) { previousElement = previousElement.previousElementSibling as HTMLElement; } else { if (previousElement.previousElementSibling.tagName === "LI") { previousElement = previousElement.previousElementSibling as HTMLElement; } else { const liElements = previousElement.previousElementSibling.querySelectorAll(".b3-list-item"); previousElement = liElements[liElements.length - 1] as HTMLElement; } break; } } else { if (previousElement.parentElement.id === "foldTree") { break; } else { previousElement = previousElement.parentElement; } } } if (previousElement.classList.contains("b3-list-item")) { currentItemElement.classList.remove("b3-list-item--focus"); previousElement.classList.add("b3-list-item--focus"); const previousRect = previousElement.getBoundingClientRect(); const fileRect = searchTreeElement.getBoundingClientRect(); if (previousRect.top < fileRect.top || previousRect.bottom > fileRect.bottom) { previousElement.scrollIntoView(previousRect.top < fileRect.top); } } event.preventDefault(); } } else { if (event.key === "ArrowDown") { currentItemElement.classList.remove("b3-list-item--focus"); if (!currentItemElement.nextElementSibling) { currentPanelElement.children[0].classList.add("b3-list-item--focus"); } else { currentItemElement.nextElementSibling.classList.add("b3-list-item--focus"); } currentItemElement = currentPanelElement.querySelector(".b3-list-item--focus"); if (currentPanelElement.scrollTop < currentItemElement.offsetTop - currentPanelElement.clientHeight + lineHeight || currentPanelElement.scrollTop > currentItemElement.offsetTop) { currentPanelElement.scrollTop = currentItemElement.offsetTop - currentPanelElement.clientHeight + lineHeight; } event.preventDefault(); return; } if (event.key === "ArrowUp") { currentItemElement.classList.remove("b3-list-item--focus"); if (!currentItemElement.previousElementSibling) { const length = currentPanelElement.children.length; currentPanelElement.children[length - 1].classList.add("b3-list-item--focus"); } else { currentItemElement.previousElementSibling.classList.add("b3-list-item--focus"); } currentItemElement = currentPanelElement.querySelector(".b3-list-item--focus"); if (currentPanelElement.scrollTop < currentItemElement.offsetTop - currentPanelElement.clientHeight + lineHeight || currentPanelElement.scrollTop > currentItemElement.offsetTop - lineHeight * 2) { currentPanelElement.scrollTop = currentItemElement.offsetTop - lineHeight * 2; } event.preventDefault(); return; } } if (event.key === "Enter") { const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus"); if (currentItemElements.length === 0) { return; } const pathList: string[] = []; const notebookIdList: string[] = []; currentItemElements.forEach(item => { pathList.push(item.getAttribute("data-path")); notebookIdList.push(item.getAttribute("data-box")); }); cb(pathList, notebookIdList); dialog.destroy(); event.preventDefault(); } }); dialog.element.addEventListener("click", (event) => { let target = event.target as HTMLElement; while (target && !target.isEqualNode(dialog.element)) { if (target.classList.contains("b3-list-item__toggle")) { getLeaf(target.parentElement, flashcard); event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("b3-button--text")) { const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement; const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus"); if (currentItemElements.length === 0) { return; } const pathList: string[] = []; const notebookIdList: string[] = []; currentItemElements.forEach(item => { pathList.push(item.getAttribute("data-path")); notebookIdList.push(item.getAttribute("data-box")); }); cb(pathList, notebookIdList); dialog.destroy(); event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("b3-button--cancel")) { dialog.destroy(); event.preventDefault(); event.stopPropagation(); break; } else if (target.classList.contains("b3-list-item")) { const currentPanelElement = searchListElement.classList.contains("fn__none") ? searchTreeElement : searchListElement; const currentItemElements = currentPanelElement.querySelectorAll(".b3-list-item--focus"); if (currentItemElements.length === 0) { return; } if (title === window.siyuan.languages.specifyPath && (event.ctrlKey || event.metaKey)) { if (currentItemElements.length === 1 && currentItemElements[0].isSameNode(target)) { // 至少需选中一个 } else { target.classList.toggle("b3-list-item--focus"); } } else { currentItemElements[0].classList.remove("b3-list-item--focus"); target.classList.add("b3-list-item--focus"); } event.preventDefault(); event.stopPropagation(); break; } target = target.parentElement; } inputElement.focus(); }); }; const getLeaf = (liElement: HTMLElement, flashcard:boolean) => { const toggleElement = liElement.querySelector(".b3-list-item__arrow"); if (toggleElement.classList.contains("b3-list-item__arrow--open")) { toggleElement.classList.remove("b3-list-item__arrow--open"); if (liElement.nextElementSibling && liElement.nextElementSibling.tagName === "UL") { liElement.nextElementSibling.classList.add("fn__none"); } return; } if (liElement.nextElementSibling && liElement.nextElementSibling.tagName === "UL") { toggleElement.classList.add("b3-list-item__arrow--open"); liElement.nextElementSibling.classList.remove("fn__none"); return; } const notebookId = liElement.getAttribute("data-box"); fetchPost("/api/filetree/listDocsByPath", { notebook: notebookId, path: liElement.getAttribute("data-path"), sort: window.siyuan.config.fileTree.sort, flashcard, }, response => { if (response.data.path === "/" && response.data.files.length === 0) { showMessage(window.siyuan.languages.emptyContent); return; } let fileHTML = ""; response.data.files.forEach((item: IFile) => { let countHTML = ""; if (item.count && item.count > 0) { countHTML = `${item.count}`; } fileHTML += `
  • ${unicode2Emoji(item.icon || (item.subFileCount === 0 ? Constants.SIYUAN_IMAGE_FILE : Constants.SIYUAN_IMAGE_FOLDER), false, "b3-list-item__graphic", true)} ${getDisplayName(item.name, true, true)} ${countHTML}
  • `; }); if (fileHTML === "") { return; } toggleElement.classList.add("b3-list-item__arrow--open"); liElement.insertAdjacentHTML("afterend", ``); const nextElement = liElement.nextElementSibling; setTimeout(() => { nextElement.setAttribute("style", `height:${nextElement.childElementCount * liElement.clientHeight}px;`); setTimeout(() => { nextElement.classList.remove("file-tree__sliderDown"); nextElement.removeAttribute("style"); }, 120); }, 2); }); }; export const getNotebookName = (id: string) => { let rootPath = ""; window.siyuan.notebooks.find((item) => { if (item.id === id) { rootPath = item.name; return true; } }); return rootPath; }; export const getNotebookIcon = (id: string) => { let rootPath = ""; window.siyuan.notebooks.find((item) => { if (item.id === id) { rootPath = item.icon; return true; } }); return rootPath; }; export const setNotebookName = (id: string, name: string) => { window.siyuan.notebooks.find((item) => { if (item.id === id) { item.name = name; return true; } }); }; export const getOpenNotebookCount = () => { let count = 0; window.siyuan.notebooks.forEach(item => { if (!item.closed) { count++; } }); return count; }; export const setNoteBook = (cb?: (notebook: INotebook[]) => void, flashcard = false) => { fetchPost("/api/notebook/lsNotebooks", { flashcard }, (response) => { if (!flashcard) { window.siyuan.notebooks = response.data.notebooks; } if (cb) { cb(response.data.notebooks); } }); };