import {closePanel} from "../util/closePanel"; import {openMobileFileById} from "../editor"; import {Constants} from "../../constants"; import {fetchPost} from "../../util/fetch"; import {getIconByType} from "../../editor/getIcon"; import {preventScroll} from "../../protyle/scroll/preventScroll"; import {openModel} from "./model"; import {getDisplayName, getNotebookIcon, getNotebookName, movePathTo, pathPosix} from "../../util/pathName"; import {filterMenu, getKeyByLiElement, initCriteriaMenu, moreMenu, queryMenu} from "../../search/menu"; import {setStorageVal} from "../../protyle/util/compatibility"; import {escapeGreat, escapeHtml} from "../../util/escape"; import {unicode2Emoji} from "../../emoji"; import {newFileByName} from "../../util/newFile"; import {showMessage} from "../../dialog/message"; import {reloadProtyle} from "../../protyle/util/reload"; import {activeBlur, hideKeyboardToolbar} from "../util/keyboardToolbar"; const replace = (element: Element, config: ISearchOption, isAll: boolean) => { if (config.method === 1 || config.method === 2) { showMessage(window.siyuan.languages._kernel[132]); return; } const searchListElement = element.querySelector("#searchList"); const replaceInputElement = element.querySelector("#toolbarReplace") as HTMLInputElement; const loadElement = replaceInputElement.nextElementSibling; if (!loadElement.classList.contains("fn__none")) { return; } let currentLiElement: HTMLElement = searchListElement.querySelector(".b3-list-item--focus"); if (!currentLiElement) { return; } loadElement.classList.remove("fn__none"); loadElement.nextElementSibling.classList.add("fn__none"); searchListElement.previousElementSibling.innerHTML = ""; let ids: string[] = []; if (isAll) { searchListElement.querySelectorAll('.b3-list-item[data-type="search-item"]').forEach(item => { ids.push(item.getAttribute("data-node-id")); }); } else { ids = [currentLiElement.getAttribute("data-node-id")]; } fetchPost("/api/search/findReplace", { k: config.method === 0 ? getKeyByLiElement(currentLiElement) : (document.querySelector("#toolbarSearch") as HTMLInputElement).value, r: replaceInputElement.value, ids, types: config.types, method: config.method, }, (response) => { loadElement.classList.add("fn__none"); loadElement.nextElementSibling.classList.remove("fn__none"); if (response.code === 1) { showMessage(response.msg); return; } if (ids.length > 1) { return; } reloadProtyle(window.siyuan.mobile.editor.protyle); if (currentLiElement.nextElementSibling) { currentLiElement.nextElementSibling.classList.add("b3-list-item--focus"); } else if (currentLiElement.previousElementSibling) { currentLiElement.previousElementSibling.classList.add("b3-list-item--focus"); } if (config.group === 1) { if (currentLiElement.nextElementSibling || currentLiElement.previousElementSibling) { currentLiElement.remove(); } else { const nextDocElement = currentLiElement.parentElement.nextElementSibling || currentLiElement.parentElement.previousElementSibling.previousElementSibling?.previousElementSibling; if (nextDocElement) { nextDocElement.nextElementSibling.firstElementChild.classList.add("b3-list-item--focus"); nextDocElement.nextElementSibling.classList.remove("fn__none"); nextDocElement.firstElementChild.firstElementChild.classList.add("b3-list-item__arrow--open"); } currentLiElement.parentElement.previousElementSibling.remove(); currentLiElement.parentElement.remove(); } } else { currentLiElement.remove(); } currentLiElement = searchListElement.querySelector(".b3-list-item--focus"); if (!currentLiElement) { searchListElement.innerHTML = `
${window.siyuan.languages.emptyContent}
`; return; } if (searchListElement.scrollTop < currentLiElement.offsetTop - searchListElement.clientHeight + 30 || searchListElement.scrollTop > currentLiElement.offsetTop) { searchListElement.scrollTop = currentLiElement.offsetTop - searchListElement.clientHeight + 30; } }); }; const updateConfig = (element: Element, newConfig: ISearchOption, config: ISearchOption) => { if (config.hasReplace !== newConfig.hasReplace) { if (newConfig.hasReplace) { element.querySelector('[data-type="toggle-replace"]').classList.add("toolbar__icon--active"); element.querySelector(".toolbar").classList.remove("fn__none"); } else { element.querySelector('[data-type="toggle-replace"]').classList.remove("toolbar__icon--active"); element.querySelector(".toolbar").classList.add("fn__none"); } } const searchPathElement = element.querySelector("#searchPath"); if (newConfig.hPath) { searchPathElement.classList.remove("fn__none"); searchPathElement.innerHTML = `
${escapeHtml(newConfig.hPath)}
`; } else { searchPathElement.classList.add("fn__none"); } if (config.group !== newConfig.group) { if (newConfig.group === 0) { element.querySelector('[data-type="expand"]').classList.add("fn__none"); element.querySelector('[data-type="contract"]').classList.add("fn__none"); } else { element.querySelector('[data-type="expand"]').classList.remove("fn__none"); element.querySelector('[data-type="contract"]').classList.remove("fn__none"); } } let includeChild = true; let enableIncludeChild = false; newConfig.idPath.forEach(newConfig => { if (newConfig.endsWith(".sy")) { includeChild = false; } if (newConfig.split("/").length > 1) { enableIncludeChild = true; } }); const searchIncludeElement = element.querySelector('[data-type="include"]'); if (includeChild) { searchIncludeElement.classList.add("toolbar__icon--active"); } else { searchIncludeElement.classList.remove("toolbar__icon--active"); } if (enableIncludeChild) { searchIncludeElement.removeAttribute("disabled"); } else { searchIncludeElement.setAttribute("disabled", "disabled"); } (document.querySelector("#toolbarSearch") as HTMLInputElement).value = newConfig.k; (element.querySelector("#toolbarReplace") as HTMLInputElement).value = newConfig.r; Object.assign(config, newConfig); window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config); setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]); updateSearchResult(config, element); window.siyuan.menus.menu.remove(); }; const onRecentBlocks = (data: IBlock[], config: ISearchOption, response?: IWebSocketData) => { const listElement = document.querySelector("#searchList"); let resultHTML = ""; data.forEach((item: IBlock, index: number) => { const title = getNotebookName(item.box) + getDisplayName(item.hPath, false); if (item.children) { resultHTML += `
${unicode2Emoji(getNotebookIcon(item.box) || Constants.SIYUAN_IMAGE_NOTE, false, "b3-list-item__graphic", true)} ${escapeGreat(title)}
`; item.children.forEach((childItem, childIndex) => { resultHTML += `
${unicode2Emoji(childItem.ial.icon, false, "b3-list-item__graphic", true)} ${childItem.content}
`; }); resultHTML += "
"; } else { resultHTML += `
${unicode2Emoji(item.ial.icon, false, "b3-list-item__graphic", true)} ${item.content}
${escapeGreat(title)}
`; } }); listElement.innerHTML = resultHTML || `
${window.siyuan.languages.newFile} ${(document.querySelector("#toolbarSearch") as HTMLInputElement).value}
`; listElement.scrollTop = 0; let countHTML = ""; if (response) { countHTML = `${window.siyuan.languages.findInDoc.replace("${x}", response.data.matchedRootCount).replace("${y}", response.data.matchedBlockCount)} ${config.page}/${response.data.pageCount || 1}`; } listElement.previousElementSibling.querySelector('[data-type="result"]').innerHTML = countHTML; }; let toolbarSearchTimeout = 0; const updateSearchResult = (config: ISearchOption, element: Element) => { clearTimeout(toolbarSearchTimeout); toolbarSearchTimeout = window.setTimeout(() => { const loadingElement = element.querySelector(".fn__loading--top"); loadingElement.classList.remove("fn__none"); const previousElement = element.querySelector('[data-type="previous"]'); const nextElement = element.querySelector('[data-type="next"]'); const inputElement = document.getElementById("toolbarSearch") as HTMLInputElement; if (inputElement.value === "" && (!config.idPath || config.idPath.length === 0)) { fetchPost("/api/block/getRecentUpdatedBlocks", {}, (response) => { onRecentBlocks(response.data, config); loadingElement.classList.add("fn__none"); previousElement.setAttribute("disabled", "true"); nextElement.setAttribute("disabled", "true"); }); } else { if (!config.page) { config.page = 1 } if (config.page > 1) { previousElement.removeAttribute("disabled"); } else { previousElement.setAttribute("disabled", "disabled"); } fetchPost("/api/search/fullTextSearchBlock", { query: inputElement.value, method: config.method, types: config.types, paths: config.idPath || [], groupBy: config.group, orderBy: config.sort, page: config.page, }, (response) => { onRecentBlocks(response.data.blocks, config, response); loadingElement.classList.add("fn__none"); if (config.page < response.data.pageCount) { nextElement.removeAttribute("disabled"); } else { nextElement.setAttribute("disabled", "disabled"); } }); } }, Constants.TIMEOUT_SEARCH); }; const initSearchEvent = (element: Element, config: ISearchOption) => { const searchInputElement = document.getElementById("toolbarSearch") as HTMLInputElement; searchInputElement.value = config.k || ""; searchInputElement.addEventListener("compositionend", (event: InputEvent) => { if (event && event.isComposing) { return; } config.page = 1; updateSearchResult(config, element); }); searchInputElement.addEventListener("input", (event: InputEvent) => { if (event && event.isComposing) { return; } config.page = 1; updateSearchResult(config, element); }); searchInputElement.addEventListener("blur", () => { if (config.removed) { config.k = searchInputElement.value; window.siyuan.storage[Constants.LOCAL_SEARCHDATA] = Object.assign({}, config); setStorageVal(Constants.LOCAL_SEARCHDATA, window.siyuan.storage[Constants.LOCAL_SEARCHDATA]); } }); const replaceInputElement = element.querySelector(".toolbar .b3-text-field") as HTMLInputElement; replaceInputElement.value = config.r || ""; const criteriaData: ISearchOption[] = []; initCriteriaMenu(element.querySelector("#criteria"), criteriaData); const searchListElement = element.querySelector("#searchList") as HTMLElement; element.addEventListener("click", (event: MouseEvent) => { let target = event.target as HTMLElement; while (target && !target.isSameNode(element)) { const type = target.getAttribute("data-type"); if (type === "previous") { if (!target.getAttribute("disabled")) { config.page--; updateSearchResult(config, element); } event.stopPropagation(); event.preventDefault(); break; } else if (type === "next") { if (!target.getAttribute("disabled")) { config.page++; updateSearchResult(config, element); } event.stopPropagation(); event.preventDefault(); break; } else if (type === "set-criteria") { config.removed = false; criteriaData.find(item => { if (item.name === target.innerText.trim()) { updateConfig(element, item, config); return true; } }); event.stopPropagation(); event.preventDefault(); break; } else if (type === "remove-criteria") { const name = target.parentElement.innerText.trim(); fetchPost("/api/storage/removeCriterion", {name}); criteriaData.find((item, index) => { if (item.name === name) { criteriaData.splice(index, 1); return true; } }); if (target.parentElement.parentElement.childElementCount === 1) { target.parentElement.parentElement.classList.add("fn__none"); target.parentElement.remove(); } else { target.parentElement.remove(); } event.stopPropagation(); event.preventDefault(); break; } else if (type === "remove-path") { config.idPath = []; config.hPath = ""; element.querySelector("#searchPath").classList.add("fn__none"); config.page = 1; updateSearchResult(config, element); const includeElement = element.querySelector('[data-type="include"]'); includeElement.classList.remove("toolbar__icon--active"); includeElement.setAttribute("disabled", "disabled"); event.stopPropagation(); event.preventDefault(); break; } else if (type === "expand") { Array.from(searchListElement.children).forEach(item => { if (item.classList.contains("b3-list-item")) { item.querySelector(".b3-list-item__arrow").classList.add("b3-list-item__arrow--open"); item.nextElementSibling.classList.remove("fn__none"); } }); event.stopPropagation(); event.preventDefault(); break; } else if (type === "contract") { Array.from(searchListElement.children).forEach(item => { if (item.classList.contains("b3-list-item")) { item.querySelector(".b3-list-item__arrow").classList.remove("b3-list-item__arrow--open"); item.nextElementSibling.classList.add("fn__none"); } }); event.stopPropagation(); event.preventDefault(); break; } else if (type === "path") { movePathTo((toPath, toNotebook) => { fetchPost("/api/filetree/getHPathsByPaths", {paths: toPath}, (response) => { config.idPath = []; const hPathList: string[] = []; let enableIncludeChild = false; toPath.forEach((item, index) => { if (item === "/") { config.idPath.push(toNotebook[index]); hPathList.push(getNotebookName(toNotebook[index])); } else { enableIncludeChild = true; config.idPath.push(pathPosix().join(toNotebook[index], item.replace(".sy", ""))); } }); if (response.data) { hPathList.push(...response.data); } config.hPath = hPathList.join(" "); const searchPathElement = element.querySelector("#searchPath"); searchPathElement.classList.remove("fn__none"); element.querySelector("#searchPath").innerHTML = `
${escapeHtml(config.hPath)}
`; const includeElement = element.querySelector('[data-type="include"]'); includeElement.classList.add("toolbar__icon--active"); if (enableIncludeChild) { includeElement.removeAttribute("disabled"); } else { includeElement.setAttribute("disabled", "disabled"); } config.page = 1; updateSearchResult(config, element); }); }, [], undefined, window.siyuan.languages.specifyPath); event.stopPropagation(); event.preventDefault(); break; } else if (type === "include" && !target.hasAttribute("disabled")) { target.classList.toggle("toolbar__icon--active"); if (target.classList.contains("toolbar__icon--active")) { config.idPath.forEach((item, index) => { if (item.endsWith(".sy")) { config.idPath[index] = item.replace(".sy", ""); } }); } else { config.idPath.forEach((item, index) => { if (!item.endsWith(".sy") && item.split("/").length > 1) { config.idPath[index] = item + ".sy"; } }); } config.page = 1; updateSearchResult(config, element); event.stopPropagation(); event.preventDefault(); break; } else if (type === "toggle-replace") { config.hasReplace = !config.hasReplace; replaceInputElement.parentElement.classList.toggle("fn__none"); target.classList.toggle("toolbar__icon--active"); event.stopPropagation(); event.preventDefault(); break; } else if (type === "more") { moreMenu(config, criteriaData, element, () => { config.page = 1; updateSearchResult(config, element); }, () => { updateConfig(element, { removed: true, sort: 0, group: 0, hasReplace: false, method: 0, hPath: "", idPath: [], k: "", r: "", page: 1, types: { document: window.siyuan.config.search.document, heading: window.siyuan.config.search.heading, list: window.siyuan.config.search.list, listItem: window.siyuan.config.search.listItem, codeBlock: window.siyuan.config.search.codeBlock, htmlBlock: window.siyuan.config.search.htmlBlock, mathBlock: window.siyuan.config.search.mathBlock, table: window.siyuan.config.search.table, blockquote: window.siyuan.config.search.blockquote, superBlock: window.siyuan.config.search.superBlock, paragraph: window.siyuan.config.search.paragraph, embedBlock: window.siyuan.config.search.embedBlock, } }, config); }); window.siyuan.menus.menu.element.style.zIndex = "220"; window.siyuan.menus.menu.fullscreen(); event.stopPropagation(); event.preventDefault(); break; } else if (type === "filter") { filterMenu(config, () => { updateSearchResult(config, element); }); event.stopPropagation(); event.preventDefault(); break; } else if (type === "query") { queryMenu(config, () => { config.page = 1; updateSearchResult(config, element); }); window.siyuan.menus.menu.element.style.zIndex = "220"; window.siyuan.menus.menu.fullscreen(); event.stopPropagation(); event.preventDefault(); break; } else if (type === "replace-all") { replace(element, config, true); event.stopPropagation(); event.preventDefault(); break; } else if (type === "replace") { replace(element, config, false); event.stopPropagation(); event.preventDefault(); break; } else if (target.classList.contains("b3-list-item__toggle")) { target.parentElement.nextElementSibling.classList.toggle("fn__none"); target.firstElementChild.classList.toggle("b3-list-item__arrow--open"); event.stopPropagation(); event.preventDefault(); break; } else if (target.classList.contains("b3-list-item")) { if (target.getAttribute("data-type") === "search-new") { newFileByName(searchInputElement.value); } else if (target.getAttribute("data-type") === "search-item") { const id = target.getAttribute("data-node-id"); if (window.siyuan.mobile.editor.protyle) { preventScroll(window.siyuan.mobile.editor.protyle); } fetchPost("/api/block/checkBlockFold", {id}, (foldResponse) => { openMobileFileById(id, foldResponse.data ? [Constants.CB_GET_ALL, Constants.CB_GET_HL] : [Constants.CB_GET_HL, Constants.CB_GET_CONTEXT]); }); closePanel(); } else if (target.querySelector(".b3-list-item__toggle")) { target.nextElementSibling.classList.toggle("fn__none"); target.firstElementChild.firstElementChild.classList.toggle("b3-list-item__arrow--open"); } event.stopPropagation(); event.preventDefault(); break; } target = target.parentElement; } }, false); }; export const popSearch = (config = window.siyuan.storage[Constants.LOCAL_SEARCHDATA] as ISearchOption) => { activeBlur(); hideKeyboardToolbar(); let includeChild = true; let enableIncludeChild = false; config.idPath.forEach(item => { if (item.endsWith(".sy")) { includeChild = false; } if (item.split("/").length > 1) { enableIncludeChild = true; } }); openModel({ title: ``, icon: "iconSearch", html: `
${escapeHtml(config.hPath)}
`, bindEvent(element) { initSearchEvent(element.firstElementChild, config); updateSearchResult(config, element); } }); };