aboutsummaryrefslogtreecommitdiff
path: root/static/js/tinysearch.js
diff options
context:
space:
mode:
authorJieiku2026-08-14 15:10:15 -0700
committerJieiku2026-08-14 15:10:15 -0700
commitc2d72e09747a2a4abe9602e1ba49a1b4649c9d3c (patch)
tree51a272ef184752e27bb70c07e4726dbdbbe6e237 /static/js/tinysearch.js
parentd52609f456f233ec0845c8a57a1e27583f0813ad (diff)
downloadabridge-c2d72e09747a2a4abe9602e1ba49a1b4649c9d3c.tar.gz
abridge-c2d72e09747a2a4abe9602e1ba49a1b4649c9d3c.tar.bz2
abridge-c2d72e09747a2a4abe9602e1ba49a1b4649c9d3c.zip
Update for Zola 0.23.3
Diffstat (limited to 'static/js/tinysearch.js')
-rw-r--r--static/js/tinysearch.js350
1 files changed, 178 insertions, 172 deletions
diff --git a/static/js/tinysearch.js b/static/js/tinysearch.js
index 183eb05..0a93d8f 100644
--- a/static/js/tinysearch.js
+++ b/static/js/tinysearch.js
@@ -2,252 +2,258 @@ window.onload = function () {
if (document.body.contains(document.goSearch)) {
document.goSearch.onsubmit = function () { return goSearchNow() };
- (function () {//anonymous function to keep from defining global variables.
-
+ (function () {
var searchinput = document.getElementById("searchinput");
- function suggestionFocus(e) {
- if (e.keyCode === 191//forward slash
- && document.activeElement.tagName !== "INPUT"
- && document.activeElement.tagName !== "TEXTAREA") {
- e.preventDefault();
- searchinput.focus();
- suggestions.classList.remove('d-none');
- }
+ var suggestions = document.getElementById("suggestions");
- if (e.keyCode === 27 ) {//escape
- searchinput.blur();
- suggestions.classList.add('d-none');
- closeAllLists();
+ function getBaseUrl() {
+ var baseUrl = document.querySelector("meta[name='base']").getAttribute("content");
+ if (baseUrl && baseUrl.slice(-1) == "/") {
+ baseUrl = baseUrl.slice(0, -1);
+ }
+ return baseUrl || "";
}
- const focusableSuggestions= suggestions.querySelectorAll('a');
- if (suggestions.classList.contains('d-none')
- || focusableSuggestions.length === 0) {
- return;
+ // Permalinks in the WASM index were baked at build time (zola base_url).
+ // Rewrite to the live site from <meta name="base"> so links match the current host.
+ function resolveResultUrl(url) {
+ if (!url) return url;
+ var baseUrl = getBaseUrl();
+ if (!baseUrl) return url;
+ try {
+ if (url.charAt(0) === "/") {
+ return baseUrl + url;
+ }
+ var parsed = new URL(url, baseUrl);
+ var base = new URL(baseUrl);
+ return base.origin + parsed.pathname + parsed.search + parsed.hash;
+ } catch (e) {
+ return url;
+ }
}
- const focusable= [...focusableSuggestions];
- const index = focusable.indexOf(document.activeElement);
- let nextIndex = 0;
+ function suggestionFocus(e) {
+ if (e.keyCode === 191
+ && document.activeElement.tagName !== "INPUT"
+ && document.activeElement.tagName !== "TEXTAREA") {
+ e.preventDefault();
+ searchinput.focus();
+ suggestions.classList.remove('d-none');
+ }
- if (e.keyCode === 38) {//up arrow
- e.preventDefault();
- nextIndex= index > 0 ? index-1 : 0;
- focusableSuggestions[nextIndex].focus();
- }
- else if (e.keyCode === 40) {//down arrow
- e.preventDefault();
- nextIndex= index+1 < focusable.length ? index+1 : index;
- focusableSuggestions[nextIndex].focus();
- }
+ if (e.keyCode === 27) {
+ searchinput.blur();
+ suggestions.classList.add('d-none');
+ closeAllLists();
+ }
+
+ const focusableSuggestions = suggestions.querySelectorAll('a');
+ if (suggestions.classList.contains('d-none')
+ || focusableSuggestions.length === 0) {
+ return;
+ }
+ const focusable = [...focusableSuggestions];
+ const index = focusable.indexOf(document.activeElement);
+
+ let nextIndex = 0;
+ if (e.keyCode === 38) {
+ e.preventDefault();
+ nextIndex = index > 0 ? index - 1 : 0;
+ focusableSuggestions[nextIndex].focus();
+ }
+ else if (e.keyCode === 40) {
+ e.preventDefault();
+ nextIndex = index + 1 < focusable.length ? index + 1 : index;
+ focusableSuggestions[nextIndex].focus();
+ }
}
document.addEventListener("keydown", suggestionFocus);
var loaded = false;
- document.getElementById('searchinput').onfocus = function() {
+ document.getElementById('searchinput').onfocus = function () {
if (!loaded) {
lazyLoad();
loaded = true;
}
document.getElementById('searchinput').onfocus = '';
- }
+ };
+
async function lazyLoad() {
- var baseUrl = document.querySelector("meta[name='base']").getAttribute("content");
- if (baseUrl.slice(-1) == "/") {
- baseUrl = baseUrl.slice(0, -1);
- }
- await init(baseUrl + "/tinysearch_engine_bg.wasm");
+ // Post-0.9 tinysearch (no wasm-pack): tinysearch_engine.wasm
+ await initWasm(getBaseUrl() + "/tinysearch_engine.wasm");
}
- // in page results when press enter or click search icon from search box
function closeSearchNow() {
const main = document.querySelector("main");
- main.innerHTML = window.main
+ main.innerHTML = window.main;
}
+
function goSearchNow() {
const main = document.querySelector("main");
if (!window.main) {
- window.main = main.innerHTML
- };
- var results = document.getElementById("suggestions");// suggestions div generated by search box
-
- var ResultsClone = results.cloneNode(true);// make a clone of the results, so that we can alter it
- ResultsClone.id = "results";// alter the id of our clone, so that we can apply different css style
-
- var headerDiv = document.createElement("div");// create a div element
-
- var headerContent = '<form name="closeSearch"><h2><button type="submit" title="Close Search"><i class="svgs x"></i></button> <i class="svgs search"></i> '.concat(document.getElementById("searchinput").value, "</h2></form>");// header to use at top of results page
+ window.main = main.innerHTML;
+ }
+ var results = document.getElementById("suggestions");
- headerDiv.innerHTML = headerContent;// document element div (headerDiv), set the inner contents to our header html (headerContent)
+ var ResultsClone = results.cloneNode(true);
+ ResultsClone.id = "results";
- ResultsClone.insertBefore(headerDiv, ResultsClone.firstChild);//insert our header div at the top of the page
+ var headerDiv = document.createElement("div");
+ var headerContent = '<form name="closeSearch"><h2><button type="submit" title="Close Search"><i class="svgs x"></i></button> <i class="svgs search"></i> '.concat(document.getElementById("searchinput").value, "</h2></form>");
+ headerDiv.innerHTML = headerContent;
+ ResultsClone.insertBefore(headerDiv, ResultsClone.firstChild);
- main.innerHTML = ResultsClone.outerHTML;//display ResultsClone.outerHTML as the page
- results.innerHTML = "";// clear the suggestions div popup
- document.getElementById("searchinput").value = "";// clear the search input box
- document.body.contains(document.closeSearch) && (document.closeSearch.onsubmit = function() { closeSearchNow() })
- return false
+ main.innerHTML = ResultsClone.outerHTML;
+ results.innerHTML = "";
+ document.getElementById("searchinput").value = "";
+ document.body.contains(document.closeSearch) && (document.closeSearch.onsubmit = function () { closeSearchNow() });
+ return false;
}
- /* Close search suggestion popup list */
function closeAllLists(elmnt) {
- var suggestions = document.getElementById("suggestions");
while (suggestions.firstChild) {
suggestions.removeChild(suggestions.firstChild);
}
}
function markTerm(input, term) {
- return input.replace(new RegExp('(^|)(' + term + ')(|$)','ig'), '$1<mark>$2</mark>$3');
+ if (!input) return "";
+ return String(input).replace(new RegExp('(^|)(' + term + ')(|$)', 'ig'), '$1<mark>$2</mark>$3');
}
- function autocomplete(inp) {
+ function unwrapMeta(meta) {
+ if (!meta) return "";
+ if (typeof meta === "string" && meta.charAt(0) === "{") {
+ try {
+ var m = JSON.parse(meta);
+ return m.meta || m.description || Object.values(m)[0] || "";
+ } catch (e) {
+ return meta;
+ }
+ }
+ return meta;
+ }
+ function autocomplete(inp) {
inp.addEventListener("input", function (e) {
- var suggestions, entry, i, val = this.value;
+ var entry, i, val = this.value;
- /*close any already open lists of autocompleted values*/
closeAllLists();
if (!val) {
return false;
}
+ if (!wasmReady) {
+ return false;
+ }
- suggestions = document.getElementById("suggestions");// suggestions div, generated by search box
-
- let arr = search(val, 99);//limit results
+ let arr = doSearch(val, 99);
for (i = 0; i < arr.length; i++) {
let elem = arr[i];
+ var title, url, meta;
+ if (Array.isArray(elem)) {
+ title = elem[0];
+ url = elem[1];
+ meta = elem[2];
+ } else {
+ title = elem.title;
+ url = elem.url;
+ meta = unwrapMeta(elem.meta || elem.description || "");
+ }
entry = document.createElement("DIV");
entry.innerHTML = '<a href><span></span><span></span></a>';
- var a, t, d;
- a = entry.querySelector('a'),
- t = entry.querySelector('span:first-child'),
- d = entry.querySelector('span:nth-child(2)');
- a.href = `${elem[1]}?q=${encodeURIComponent(val)}`;//a link
- //t.innerHTML = markTerm(elem[0], val);//title
- t.innerHTML = elem[0];//title
- d.innerHTML = markTerm(elem[2], val);//description
+ var a = entry.querySelector('a'),
+ t = entry.querySelector('span:first-child'),
+ d = entry.querySelector('span:nth-child(2)');
+ var resolved = resolveResultUrl(url);
+ a.href = resolved + (resolved.indexOf('?') >= 0 ? '&' : '?') + 'q=' + encodeURIComponent(val);
+ t.innerHTML = title || "";
+ d.innerHTML = markTerm(meta || "", val);
suggestions.appendChild(entry);
}
- })
+ });
}
+ // ---- New tinysearch WASM API (vanilla cargo build, no wasm-pack) ----
+ var wasmModule = null;
+ var memory = null;
+ var searchFunction = null;
+ var freeFunction = null;
+ var wasmReady = false;
+ var textEncoder = new TextEncoder();
+ var textDecoder = new TextDecoder("utf-8");
- let wasm, cachedTextDecoder = (autocomplete(document.getElementById("searchinput")), new TextDecoder("utf-8", {
- ignoreBOM: !0,
- fatal: !0
- })),
- cachegetUint8Memory0 = (cachedTextDecoder.decode(), null);
-
- function getUint8Memory0() {
- return cachegetUint8Memory0 = null !== cachegetUint8Memory0 && cachegetUint8Memory0.buffer === wasm.memory.buffer ? cachegetUint8Memory0 : new Uint8Array(wasm.memory.buffer)
+ function stringToWasmPtr(str) {
+ var bytes = textEncoder.encode(str + "\0");
+ var ptr = 0;
+ if (wasmModule.exports.__wbindgen_malloc) {
+ ptr = wasmModule.exports.__wbindgen_malloc(bytes.length);
+ }
+ if (!ptr) {
+ ptr = 1024;
+ }
+ new Uint8Array(memory.buffer, ptr, bytes.length).set(bytes);
+ return ptr;
}
- function getStringFromWasm0(e, t) {
- return cachedTextDecoder.decode(getUint8Memory0().subarray(e, e + t))
+ function wasmPtrToString(ptr) {
+ if (!ptr) return null;
+ var memoryArray = new Uint8Array(memory.buffer);
+ var length = 0;
+ while (memoryArray[ptr + length] !== 0) {
+ length++;
+ if (length > 10 * 1024 * 1024) break;
+ }
+ return textDecoder.decode(memoryArray.subarray(ptr, ptr + length));
}
- const heap = new Array(32).fill(void 0);
- heap.push(void 0, null, !0, !1);
- let heap_next = heap.length;
- function addHeapObject(e) {
- heap_next === heap.length && heap.push(heap.length + 1);
- var t = heap_next;
- return heap_next = heap[t], heap[t] = e, t
- }
- let WASM_VECTOR_LEN = 0,
- cachedTextEncoder = new TextEncoder("utf-8");
- const encodeString = "function" == typeof cachedTextEncoder.encodeInto ? function(e, t) {
- return cachedTextEncoder.encodeInto(e, t)
- } : function(e, t) {
- var n = cachedTextEncoder.encode(e);
- return t.set(n), {
- read: e.length,
- written: n.length
- }
- };
+ function doSearch(query, limit) {
+ if (!wasmReady || !query) return [];
+ limit = limit || 10;
+ try {
+ var queryPtr = stringToWasmPtr(query);
+ var resultPtr = searchFunction(queryPtr, limit);
- function passStringToWasm0(e, t, n) {
- if (void 0 === n) {
- const n = cachedTextEncoder.encode(e),
- a = t(n.length);
- return getUint8Memory0().subarray(a, a + n.length).set(n), WASM_VECTOR_LEN = n.length, a
- }
- let a = e.length,
- o = t(a);
- const i = getUint8Memory0();
- let r = 0;
- for (; r < a; r++) {
- const t = e.charCodeAt(r);
- if (127 < t) break;
- i[o + r] = t
- }
- if (r !== a) {
- 0 !== r && (e = e.slice(r)), o = n(o, a, a = r + 3 * e.length);
- const t = getUint8Memory0().subarray(o + r, o + a);
- r += encodeString(e, t).written
- }
- return WASM_VECTOR_LEN = r, o
- }
+ if (wasmModule.exports.__wbindgen_free && queryPtr > 1024) {
+ wasmModule.exports.__wbindgen_free(queryPtr, query.length + 1);
+ }
- function getObject(e) {
- return heap[e]
- }
+ if (!resultPtr) return [];
- function dropObject(e) {
- e < 36 || (heap[e] = heap_next, heap_next = e)
- }
+ var resultString = wasmPtrToString(resultPtr);
+ freeFunction(resultPtr);
- function takeObject(e) {
- var t = getObject(e);
- return dropObject(e), t
+ if (!resultString) return [];
+ var results = JSON.parse(resultString);
+ return Array.isArray(results) ? results : [];
+ } catch (err) {
+ console.error("tinysearch error:", err);
+ return [];
+ }
}
- function search(e, t) {
- var e = passStringToWasm0(e, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc),
- n = WASM_VECTOR_LEN;
- return takeObject(wasm.search(e, n, t))
- }
- async function load(e, t) {
- if ("function" == typeof Response && e instanceof Response) {
- if ("function" == typeof WebAssembly.instantiateStreaming) try {
- return await WebAssembly.instantiateStreaming(e, t)
- } catch (t) {
- if ("application/wasm" == e.headers.get("Content-Type")) throw t;
- console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", t)
- }
- var n = await e.arrayBuffer();
- return WebAssembly.instantiate(n, t)
+ async function initWasm(url) {
+ var response = await fetch(url);
+ if (!response.ok) {
+ throw new Error("Failed to fetch WASM: " + response.status + " " + response.statusText + " (" + url + ")");
}
- n = await WebAssembly.instantiate(e, t);
- return n instanceof WebAssembly.Instance ? {
- instance: n,
- module: e
- } : n
- }
- async function init(e) {
- var baseUrl = document.querySelector("meta[name='base']").getAttribute("content");
- if (baseUrl.slice(-1) == "/") {
- baseUrl = baseUrl.slice(0, -1);
+ var wasmBytes = await response.arrayBuffer();
+ var module = await WebAssembly.instantiate(wasmBytes);
+ wasmModule = module.instance;
+ memory = wasmModule.exports.memory;
+ searchFunction = wasmModule.exports.search;
+ freeFunction = wasmModule.exports.free_search_result;
+
+ if (!searchFunction || !freeFunction || !memory) {
+ throw new Error("Required WASM exports not found (need search, free_search_result, memory)");
}
- void 0 === e && (e = new URL(baseUrl + "/tinysearch_engine_bg.wasm"));
- const t = {
- wbg: {}
- };
- t.wbg.__wbindgen_json_parse = function(e, t) {
- return addHeapObject(JSON.parse(getStringFromWasm0(e, t)))
- };
- var {
- instance: e,
- module: n
- } = await load(await (e = "string" == typeof e || "function" == typeof Request && e instanceof Request || "function" == typeof URL && e instanceof URL ? fetch(e) : e), t);
- return wasm = e.exports, init.__wbindgen_wasm_module = n, wasm
+ wasmReady = true;
}
+ autocomplete(document.getElementById("searchinput"));
document.goSearch.onsubmit = function () { return goSearchNow() };
}());
}