diff options
Diffstat (limited to 'static/js/fixedsearch.ts')
-rw-r--r-- | static/js/fixedsearch.ts | 194 |
1 files changed, 194 insertions, 0 deletions
diff --git a/static/js/fixedsearch.ts b/static/js/fixedsearch.ts new file mode 100644 index 0000000..26b9ac1 --- /dev/null +++ b/static/js/fixedsearch.ts @@ -0,0 +1,194 @@ +(function () { + self.addEventListener("DOMContentLoaded", function () { + /* + Originally Based on fixedsearch, a super fast, client side search for Hugo.io with Fusejs.io + based on https://gist.github.com/cmod/5410eae147e4318164258742dd053993 + */ + + const form = document.getElementById("search-form"); // search form + const query = document.getElementById("search-input"); // input box for search + const submit = document.getElementById("search-submit"); // form submit button + const dropdown = document.getElementById("search-results"); // targets the <ul> + + form.addEventListener("focusin", function () { + initialize(); + }); + + form.addEventListener("submit", function (event) { + event.preventDefault(); + return false; + }); + + form.addEventListener("keydown", function (event) { + const head = + dropdown.firstChild.nextElementSibling.firstChild.nextElementSibling; + const tail = dropdown.lastElementChild.firstChild.nextElementSibling; + + // ESC (27) + if (query.contains(event.target)) { + if (event.keyCode == 27) { + document.activeElement.blur(); + dropdown.setAttribute("hidden", ""); + } + } + + // DOWN (40) + if (event.keyCode == 40) { + event.preventDefault(); + if (document.activeElement == query) head.focus(); + else if (document.activeElement == tail) query.focus(); + else { + document.activeElement.parentElement.nextElementSibling.firstChild + .nextElementSibling.focus(); + } + } + + // UP (38) + if (event.keyCode == 38) { + event.preventDefault(); + if (document.activeElement == query) tail.focus(); + else if (document.activeElement == head) query.focus(); + else { + document.activeElement.parentElement.previousElementSibling.firstChild + .nextElementSibling.focus(); + } + } + + // BACKSPACE (8) + if (event.keyCode == 8) { + if (document.activeElement != query) { + event.preventDefault(); + query.focus(); + } + } + + // ENTER (13) + if (event.keyCode == 13) { + if (dropdown && document.activeElement == query) { + event.preventDefault(); + head.focus(); + self.window.location = document.activeElement.href; + } + } + }); + + let scrolls = 0; + + self.addEventListener("scroll", function () { + if (scrolls > 3) { + scrolls = 0; + document.activeElement.blur(); + dropdown.setAttribute("hidden", ""); + } + scrolls++; + }); + + document.addEventListener("click", function (event) { + if (!form.contains(event.target)) { + dropdown.setAttribute("hidden", ""); + } + }); + + function fetch_JSON(path, callback) { + const httpRequest = new XMLHttpRequest(); + httpRequest.onreadystatechange = function () { + if (httpRequest.readyState === 4) { + if (httpRequest.status === 200) { + const data = JSON.parse(httpRequest.responseText); + if (callback) callback(data); + } + } + }; + httpRequest.open("GET", path); + httpRequest.send(); + } + + /* Load script based on https://stackoverflow.com/a/55451823 */ + + function load_script(url) { + return new Promise(function (resolve, reject) { + const script = document.createElement("script"); + script.onerror = reject; + script.onload = resolve; + if (document.currentScript) { + document.currentScript.parentNode.insertBefore( + script, + document.currentScript, + ); + } else { + document.head.appendChild(script); + } + script.src = url; + }); + } + + let first_run = true; // allow us to delay loading json data unless search activated + + function initialize() { + if (first_run) { + load_script(window.location.origin + "/js/fuzzysort.js") + .then(() => { + first_run = false; + fetch_JSON("/index.json", function (data) { + const options = { + key: ["title"], + }; + + query.addEventListener("keyup", function () { + search(query.value, data.items, options); + }); + + query.addEventListener("focusin", function () { + search(query.value, data.items, options); + }); + + search(query.value, data.items, options); + }); + }).catch((error) => { + console.log("Error failed to load fuzzy sort: " + error); + }); + } + } + + function search(term, data, options) { + const results = fuzzysort.go(term, data, options); + let items = ""; + + if (results.length === 0 && term.length >= 0) { + let separator = "—"; + if (term.length === 0) separator = ""; + items = ` + <li> + <a href="javascript: void(0)" tabindex="0">${term} ${separator} No Results Found</a> + </li> + `; + dropdown.removeAttribute("hidden"); + } else { + dropdown.removeAttribute("hidden"); + for (const string in results.slice(0, 10)) { + const decode = document.createElement("textarea"); + decode.innerHTML = results[string].obj.title; + + let highlight = fuzzysort.highlight( + fuzzysort.single(term, decode.value), + "<span>", + "</span>", + ); + + if (highlight === null) { + highlight = decode.value; + } + + items = items + + ` + <li> + <a href="${results[string].obj.url}" tabindex="0">${highlight}</a> + </li> + `; + } + } + + dropdown.innerHTML = items; + } + }); +})(); |