/** * This module is used to handle user's interaction with the search form. */ import { DeclarationDataCenter } from "./declaration-data.js"; // Search form and input in the upper right toolbar const SEARCH_FORM = document.querySelector("#search_form"); const SEARCH_INPUT = SEARCH_FORM.querySelector("input[name=q]"); // Search form on the /search.html_page. These may be null. const SEARCH_PAGE_INPUT = document.querySelector("#search_page_query") const SEARCH_RESULTS = document.querySelector("#search_results") // Max results to show for autocomplete or /search.html page. const AC_MAX_RESULTS = 30 const SEARCH_PAGE_MAX_RESULTS = undefined // Create an `div#autocomplete_results` to hold all autocomplete results. let ac_results = document.createElement("div"); ac_results.id = "autocomplete_results"; SEARCH_FORM.appendChild(ac_results); /** * Attach `selected` class to the the selected autocomplete result. */ function handleSearchCursorUpDown(down) { const sel = ac_results.querySelector(`.selected`); if (sel) { sel.classList.remove("selected"); const toSelect = down ? sel.nextSibling : sel.previousSibling; toSelect && toSelect.classList.add("selected"); } else { const toSelect = down ? ac_results.firstChild : ac_results.lastChild; toSelect && toSelect.classList.add("selected"); } } /** * Perform search (when enter is pressed). */ function handleSearchEnter() { const sel = ac_results.querySelector(`.selected .result_link a`) || document.querySelector(`#search_button`); sel.click(); } /** * Allow user to navigate autocomplete results with up/down arrow keys, and choose with enter. */ SEARCH_INPUT.addEventListener("keydown", (ev) => { switch (ev.key) { case "Down": case "ArrowDown": ev.preventDefault(); handleSearchCursorUpDown(true); break; case "Up": case "ArrowUp": ev.preventDefault(); handleSearchCursorUpDown(false); break; case "Enter": ev.preventDefault(); handleSearchEnter(); break; } }); /** * Remove all children of a DOM node. */ function removeAllChildren(node) { while (node.firstChild) { node.removeChild(node.lastChild); } } /** * Handle user input and perform search. */ function handleSearch(dataCenter, err, ev, sr, maxResults, includedoc=false) { const text = ev.target.value; // If no input clear all. if (!text) { sr.removeAttribute("state"); removeAllChildren(sr); return; } // searching sr.setAttribute("state", "loading"); if (dataCenter) { const result = dataCenter.search(text, false, maxResults); // in case user has updated the input. if (ev.target.value != text) return; // update autocomplete results removeAllChildren(sr); for (const { name, doc, docLink } of result) { const row = sr.appendChild(document.createElement("div")); row.classList.add("search_result") const linkdiv = row.appendChild(document.createElement("div")) linkdiv.classList.add("result_link") const link = linkdiv.appendChild(document.createElement("a")); link.innerText = name; link.title = name; link.href = SITE_ROOT + docLink; if (includedoc) { const doctext = row.appendChild(document.createElement("div")); doctext.innerText = doc doctext.classList.add("result_doc") } } } // handle error else { removeAllChildren(sr); const d = sr.appendChild(document.createElement("a")); d.innerText = `Cannot fetch data, please check your network connection.\n${err}`; } sr.setAttribute("state", "done"); } DeclarationDataCenter.init() .then((dataCenter) => { // Search autocompletion. SEARCH_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev, ac_results, AC_MAX_RESULTS)); if(SEARCH_PAGE_INPUT) { SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS, true)) SEARCH_PAGE_INPUT.dispatchEvent(new Event("input")) } }) .catch(e => { SEARCH_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, ac_results, AC_MAX_RESULTS)); if(SEARCH_PAGE_INPUT) { SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS, true)); } });