diff --git a/theme/ht_searcher.js b/theme/ht_searcher.js index dfed69d36..db9b55b76 100644 --- a/theme/ht_searcher.js +++ b/theme/ht_searcher.js @@ -1,96 +1,92 @@ -/* ht_searcher.js ───────────────────────────────────────────────── */ -/* Everything – UI + worker – lives in this one file. */ - +/* ht_searcher.js ─────────────────────────────────────────────── */ (() => { - /* ────────────────────────────── - 0. Build an inline Web Worker - ────────────────────────────── */ + /* ───────────── 0. Inline Web-Worker code ────────────────────── */ const workerCode = ` - /* inside the worker thread ################################## */ - self.window = self; /* let searchindex.js use window */ - - /* 1. elasticlunr.min.js (CDN → local) */ + /* Make scripts written for browsers happy inside the worker */ + self.window = self; + self.search = self.search || {}; /* ensure object */ + + const abs = p => location.origin + p; /* helper */ + + /* 1 ─ elasticlunr.min.js (CDN → local) */ try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); - } catch (_) { - importScripts('/elasticlunr.min.js'); + } catch (e) { + console.error('elasticlunr CDN failed →', e); + importScripts(abs('/elasticlunr.min.js')); } - - /* 2. searchindex.js (GitHub Raw → local) */ + + /* 2 ─ searchindex.js (GitHub Raw → local) */ (async () => { try { const r = await fetch( 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js', - { mode: 'cors' } + { mode:'cors' } ); - if (!r.ok) throw new Error(r.status); + if (!r.ok) throw new Error('HTTP ' + r.status); const blobURL = URL.createObjectURL( - new Blob([await r.text()], { type: 'application/javascript' }) - ); /* force correct MIME */ - importScripts(blobURL); - } catch (_) { - importScripts('/searchindex.js'); + new Blob([await r.text()], { type:'application/javascript' }) + ); + importScripts(blobURL); /* MIME coercion */ + } catch (e) { + console.error('GitHub index fetch failed →', e); + try { + importScripts(abs('/searchindex.js')); + } catch (e2) { + console.error('Local index load failed →', e2); + throw e2; /* abort loudly */ + } } - - /* 3. Build index & reply to queries */ + + /* 3 ─ build index & answer queries */ const idx = elasticlunr.Index.load(self.search.index); - - self.onmessage = ({ data: q }) => { - const hits = idx.search(q, { bool: 'AND', expand: true }); - postMessage(hits); + + self.onmessage = ({ data:q }) => { + if (!q) { postMessage([]); return; } /* empty search */ + const raw = idx.search(q, { bool:'AND', expand:true }); + const docs = raw.map(r => idx.documentStore.getDoc(r.ref)); + postMessage(docs); /* only docs cross thread */ }; })(); `; - - /* Turn the string into a real worker file */ + const workerURL = URL.createObjectURL( - new Blob([workerCode], { type: 'application/javascript' }) + new Blob([workerCode], { type:'application/javascript' }) ); - const worker = new Worker(workerURL); /* classic worker, supports importScripts */ - URL.revokeObjectURL(workerURL); /* clean up */ - - /* ────────────────────────────── - 1. Minimal search-UI glue - ────────────────────────────── */ - const WRAP = document.getElementById('search-wrapper'); - const TOG = document.getElementById('search-toggle'); - const INP = document.getElementById('searchbar'); - const LIST = document.getElementById('searchresults'); + const worker = new Worker(workerURL); + URL.revokeObjectURL(workerURL); /* tidy blob */ + + /* ───────────── 1. Tiny UI glue ─────────────────────────────── */ + const WRAP = document.getElementById('search-wrapper'); + const TOG = document.getElementById('search-toggle'); + const INP = document.getElementById('searchbar'); + const LIST = document.getElementById('searchresults'); const HOTKEY = 83; /* “s” */ let debounce; - - /* Paint results that come back from the worker */ - const paint = hits => { - LIST.innerHTML = hits.slice(0, 30).map(h => - '
  • ' + h.doc.title + '
  • ' + + /* paint results */ + worker.onmessage = ({ data:docs }) => { + LIST.innerHTML = docs.slice(0,30).map(d => + '
  • ' + d.title + '
  • ' ).join(''); }; - - worker.onmessage = ({ data }) => paint(data); - - /* Open the search UI */ - const open = () => { - WRAP.classList.remove('hidden'); - INP.focus(); - }; - - /* Toggle button */ + + /* open UI */ + const open = () => { WRAP.classList.remove('hidden'); INP.focus(); }; + TOG.addEventListener('click', open); - - /* Keyboard shortcut: “s” (no modifiers) */ document.addEventListener('keydown', e => { if (!e.metaKey && !e.ctrlKey && !e.altKey && e.keyCode === HOTKEY) { - e.preventDefault(); - open(); + e.preventDefault(); open(); } }); - - /* Debounced input → worker */ + + /* debounced keystrokes → worker */ INP.addEventListener('input', e => { clearTimeout(debounce); debounce = setTimeout(() => { worker.postMessage(e.target.value.trim()); }, 120); }); - })(); - \ No newline at end of file + })(); + \ No newline at end of file