diff --git a/theme/ht_searcher.js b/theme/ht_searcher.js index bf11595ad..dfed69d36 100644 --- a/theme/ht_searcher.js +++ b/theme/ht_searcher.js @@ -1,40 +1,96 @@ -/* ht_searcher.js --------------------------------------------------------- */ +/* ht_searcher.js ───────────────────────────────────────────────── */ +/* Everything – UI + worker – lives in this one file. */ + (() => { - const WRAPPER = document.getElementById('search-wrapper'); - const TOGGLE = document.getElementById('search-toggle'); - const INPUT = document.getElementById('searchbar'); - const LIST = document.getElementById('searchresults'); - const HOTKEY = 83; // “s” - let worker, debounce; + /* ────────────────────────────── + 0. Build an inline Web Worker + ────────────────────────────── */ + const workerCode = ` + /* inside the worker thread ################################## */ + self.window = self; /* let searchindex.js use window */ - function startWorker() { - if (worker) return; - worker = new Worker('/search-worker.js', { type:'module' }); - worker.onmessage = ({data}) => { - LIST.innerHTML = data.slice(0,30).map(h => - `
  • ${h.doc.title}
  • ` - ).join(''); - }; - } + /* 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'); + } - async function openUI() { - WRAPPER.classList.remove('hidden'); - INPUT.focus(); - startWorker(); // fetches CDN/GitHub in parallel - } + /* 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' } + ); + if (!r.ok) throw new Error(r.status); + const blobURL = URL.createObjectURL( + new Blob([await r.text()], { type: 'application/javascript' }) + ); /* force correct MIME */ + importScripts(blobURL); + } catch (_) { + importScripts('/searchindex.js'); + } - TOGGLE.addEventListener('click', openUI); + /* 3. Build index & reply to queries */ + const idx = elasticlunr.Index.load(self.search.index); + + self.onmessage = ({ data: q }) => { + const hits = idx.search(q, { bool: 'AND', expand: true }); + postMessage(hits); + }; + })(); + `; + + /* Turn the string into a real worker file */ + const workerURL = URL.createObjectURL( + 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 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 + '
  • ' + ).join(''); + }; + + worker.onmessage = ({ data }) => paint(data); + + /* Open the search UI */ + const open = () => { + WRAP.classList.remove('hidden'); + INP.focus(); + }; + + /* Toggle button */ + 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(); openUI(); + e.preventDefault(); + open(); } }); - INPUT.addEventListener('input', e => { + /* Debounced input → worker */ + INP.addEventListener('input', e => { clearTimeout(debounce); debounce = setTimeout(() => { - worker?.postMessage(e.target.value.trim()); - }, 120); // small debounce keeps typing smooth + worker.postMessage(e.target.value.trim()); + }, 120); }); })(); \ No newline at end of file diff --git a/theme/search-worker.js b/theme/search-worker.js deleted file mode 100644 index f221ce369..000000000 --- a/theme/search-worker.js +++ /dev/null @@ -1,41 +0,0 @@ -/* search-worker.js ------------------------------------------------------- */ -/* Make code written for window work in a worker: */ -self.window = self; - -//////////////////////////////////////////////////////////////////////////// -// 1. elasticlunr.min.js : CDN first → local fallback -//////////////////////////////////////////////////////////////////////////// -try { - importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); -} catch (e) { - importScripts('/elasticlunr.min.js'); // ship this with your site -} - -//////////////////////////////////////////////////////////////////////////// -// 2. searchindex.js : GitHub Raw first → local fallback -// We fetch → wrap in a Blob({type:'application/javascript'}) to bypass -// GitHub’s text/plain + nosniff MIME blocking. -//////////////////////////////////////////////////////////////////////////// -try { - const res = await fetch( - 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js', - {mode: 'cors'} - ); - if (!res.ok) throw new Error(res.status); - const blobUrl = URL.createObjectURL( - new Blob([await res.text()], { type:'application/javascript' }) - ); - importScripts(blobUrl); // correct MIME, runs once -} catch (e) { - importScripts('/searchindex.js'); // offline fallback -} - -//////////////////////////////////////////////////////////////////////////// -// 3. Build the index once and answer queries -//////////////////////////////////////////////////////////////////////////// -const idx = elasticlunr.Index.load(self.search.index); - -self.onmessage = ({data: q}) => { - postMessage(idx.search(q, { bool:'AND', expand:true })); -}; -