/* * Fixed Search Copyright (C) 2020 Heracles Papatheodorou * Copyright (C) Craig Mod, Eddie Webb, and Matthew Daly * https://gist.github.com/Arty2/8b0c43581013753438a3d35c15091a9f#file-license-md * Licence: MIT | https://mit-license.org/ */ (function () { ["DOMContentLoaded", "URLChangedCustomEvent"].forEach(function (event) { self.addEventListener(event, function () { const container = document.querySelector("search-box"); const dropdown = document.querySelector("search-box ul"); const form = document.querySelector("search-box form"); const query = document.querySelector("search-box input"); const submit = document.querySelector("search-box button"); function first(element) { if (element.firstChild.nextElementSibling) { return element.firstChild.nextElementSibling.firstChild .nextElementSibling; } } function last(element) { return element.lastElementChild.firstChild.nextElementSibling; } function next(element) { return element.activeElement.parentElement.nextElementSibling.firstChild .nextElementSibling.focus(); } function previous(element) { return element.activeElement.parentElement.previousElementSibling .firstChild .nextElementSibling.focus(); } function press(keyname) { document.dispatchEvent(new KeyboardEvent("keydown", { "key": keyname })); } let selected; if (submit === null) return; submit.addEventListener("click", function (event) { if (selected) { event.preventDefault(); selected.focus(); return selected.click(); } first(dropdown).focus(); press("ArrowDown"); document.activeElement.click(); }); ["keyup", "click"].forEach(function (event) { form.addEventListener(event, function () { if (document.activeElement.nodeName === "A") { return selected = document.activeElement; } selected = undefined; }); }); form.addEventListener("focusin", function () { container.setAttribute("data-focus", ""); initialize(); }); form.addEventListener("submit", function (event) { event.preventDefault(); return false; }); form.addEventListener("keydown", function (event) { // ESC (27) if (form.contains(event.target)) { if (event.keyCode == 27) { document.activeElement.blur(); dropdown.setAttribute("hidden", ""); container.removeAttribute("data-focus"); } } // BACKSPACE (8) if (event.keyCode == 8) { if (document.activeElement != query) { event.preventDefault(); query.focus(); } } const head = first(dropdown); const tail = last(dropdown); // DOWN (40) if (event.keyCode == 40) { event.preventDefault(); if (document.activeElement == query) head.focus(); else if (document.activeElement == tail) query.focus(); else next(document); } // UP (38) if (event.keyCode == 38) { event.preventDefault(); if (document.activeElement == query) tail.focus(); else if (document.activeElement == head) query.focus(); else previous(document); } // ENTER (13) if (event.keyCode == 13) { if (dropdown && document.activeElement == query) { event.preventDefault(); head.focus(); head.click(); } } }); let scrolls = 0; self.addEventListener("scroll", function () { if (scrolls > 3) { scrolls = 0; document.activeElement.blur(); dropdown.setAttribute("hidden", ""); container.removeAttribute("data-focus"); } scrolls++; }); self.addEventListener("click", function (event) { if ( !form.contains(event.target) && !(document.activeElement === query) && !(document.activeElement === submit) ) { dropdown.setAttribute("hidden", ""); container.removeAttribute("data-focus"); } }); function fetch(url, callback) { const http = new XMLHttpRequest(); http.onreadystatechange = function () { if (http.readyState === 4 && http.status === 200 && callback) { callback(http); } }; http.open("GET", url); http.send(); } /* Load script based on https://stackoverflow.com/a/55451823 */ function 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 data = {}; let boot = true; const options = { key: ["title"] }; function isEmpty(obj) { return Object.keys(obj).length === 0; } function appendItemsTo(local, remote) { const paginated = Object.keys(remote).includes("next_url"); if (isEmpty(local)) { local = remote; } else { local.items = local.items.concat(remote.items); } if (paginated) { fetch(remote.next_url, function (request) { appendItemsTo(local, JSON.parse(request.responseText)); }); } else search(query.value, data.items, options); data = local; } function initialize() { if (boot) { script(window.location.origin + "/js/fuzzysort.js") .then(function () { fetch("/index.json", function (request) { appendItemsTo({}, JSON.parse(request.responseText)); search("", data.items, options); boot = false; }); ["keyup", "focusin"].forEach(function (event) { query.addEventListener(event, function () { if (data.items) search(query.value, data.items, options); else { boot = true; initialize(); } }); }); }).catch(function (error) { console.error("ERROR: Failed to load fuzzy search", error); }); } } function escape(text) { const escaped = document.createElement("textarea"); escaped.textContent = text; return escaped.innerHTML; } 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 = '
  • '.concat( escape(term), " ", ).concat(separator, " No Results Found
  • "); dropdown.removeAttribute("hidden"); container.setAttribute("data-focus", ""); } else { dropdown.removeAttribute("hidden"); for (var string in results.slice(0, 10)) { const title = results[string].obj.title; let highlight = fuzzysort.highlight( fuzzysort.single(escape(term), escape(title)), "", "", ); if (highlight === null) highlight = title; items = items + '\n
  • \n', ).concat(highlight, "\n
  • \n"); } } dropdown.innerHTML = items; } }); }); })();