2023-05-11 13:27:25 +00:00
|
|
|
/**
|
|
|
|
* 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, autocomplete) {
|
|
|
|
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) {
|
|
|
|
var allowedKinds;
|
|
|
|
if (!autocomplete) {
|
|
|
|
allowedKinds = new Set();
|
|
|
|
document.querySelectorAll(".kind_checkbox").forEach((checkbox) =>
|
|
|
|
{
|
|
|
|
if (checkbox.checked) {
|
|
|
|
allowedKinds.add(checkbox.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
const result = dataCenter.search(text, false, allowedKinds, maxResults);
|
|
|
|
|
|
|
|
// in case user has updated the input.
|
|
|
|
if (ev.target.value != text) return;
|
|
|
|
|
|
|
|
// update autocomplete results
|
|
|
|
removeAllChildren(sr);
|
|
|
|
for (const { name, kind, 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 (!autocomplete) {
|
|
|
|
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, true));
|
|
|
|
if(SEARCH_PAGE_INPUT) {
|
|
|
|
SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(dataCenter, null, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS, false))
|
|
|
|
document.querySelectorAll(".kind_checkbox").forEach((checkbox) =>
|
|
|
|
checkbox.addEventListener("input", ev => SEARCH_PAGE_INPUT.dispatchEvent(new Event("input")))
|
|
|
|
);
|
|
|
|
SEARCH_PAGE_INPUT.dispatchEvent(new Event("input"))
|
2023-06-06 00:42:43 +00:00
|
|
|
};
|
|
|
|
SEARCH_INPUT.dispatchEvent(new Event("input"))
|
2023-05-11 13:27:25 +00:00
|
|
|
})
|
|
|
|
.catch(e => {
|
|
|
|
SEARCH_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, ac_results, AC_MAX_RESULTS,true ));
|
|
|
|
if(SEARCH_PAGE_INPUT) {
|
|
|
|
SEARCH_PAGE_INPUT.addEventListener("input", ev => handleSearch(null, e, ev, SEARCH_RESULTS, SEARCH_PAGE_MAX_RESULTS, false));
|
|
|
|
}
|
|
|
|
});
|