|
21 | 21 | try { importScripts('https://cdn.jsdelivr.net/npm/elasticlunr@0.9.5/elasticlunr.min.js'); } |
22 | 22 | catch { importScripts(abs('/elasticlunr.min.js')); } |
23 | 23 | |
24 | | - /* 2 — load a single index (remote → local) */ |
25 | | - async function loadIndex(remote, local, isCloud=false){ |
26 | | - let rawLoaded = false; |
| 24 | + /* 2 — load a single index (remote → local) */ |
| 25 | + async function loadIndex(remote, local, isCloud=false){ |
| 26 | + let rawLoaded = false; |
| 27 | + if(remote){ |
27 | 28 | try { |
28 | 29 | const r = await fetch(remote,{mode:'cors'}); |
29 | 30 | if (!r.ok) throw new Error('HTTP '+r.status); |
30 | 31 | importScripts(URL.createObjectURL(new Blob([await r.text()],{type:'application/javascript'}))); |
31 | 32 | rawLoaded = true; |
32 | 33 | } catch(e){ console.warn('remote',remote,'failed →',e); } |
33 | | - if(!rawLoaded){ |
34 | | - try { importScripts(abs(local)); rawLoaded = true; } |
35 | | - catch(e){ console.error('local',local,'failed →',e); } |
| 34 | + } |
| 35 | + if(!rawLoaded && local){ |
| 36 | + try { importScripts(abs(local)); rawLoaded = true; } |
| 37 | + catch(e){ console.error('local',local,'failed →',e); } |
| 38 | + } |
| 39 | + if(!rawLoaded) return null; /* give up on this index */ |
| 40 | + const data = { json:self.search.index, urls:self.search.doc_urls, cloud:isCloud }; |
| 41 | + delete self.search.index; delete self.search.doc_urls; |
| 42 | + return data; |
| 43 | + } |
| 44 | +
|
| 45 | + async function loadWithFallback(remotes, local, isCloud=false){ |
| 46 | + if(remotes.length){ |
| 47 | + const [primary, ...secondary] = remotes; |
| 48 | + const primaryData = await loadIndex(primary, null, isCloud); |
| 49 | + if(primaryData) return primaryData; |
| 50 | +
|
| 51 | + if(local){ |
| 52 | + const localData = await loadIndex(null, local, isCloud); |
| 53 | + if(localData) return localData; |
| 54 | + } |
| 55 | +
|
| 56 | + for (const remote of secondary){ |
| 57 | + const data = await loadIndex(remote, null, isCloud); |
| 58 | + if(data) return data; |
36 | 59 | } |
37 | | - if(!rawLoaded) return null; /* give up on this index */ |
38 | | - const data = { json:self.search.index, urls:self.search.doc_urls, cloud:isCloud }; |
39 | | - delete self.search.index; delete self.search.doc_urls; |
40 | | - return data; |
41 | 60 | } |
42 | | - |
43 | | - (async () => { |
44 | | - const MAIN_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks/refs/heads/master/searchindex.js'; |
45 | | - const CLOUD_RAW = 'https://raw.githubusercontent.com/HackTricks-wiki/hacktricks-cloud/refs/heads/master/searchindex.js'; |
46 | | - |
47 | | - const indices = []; |
48 | | - const main = await loadIndex(MAIN_RAW , '/searchindex-book.js', false); if(main) indices.push(main); |
49 | | - const cloud= await loadIndex(CLOUD_RAW, '/searchindex.js', true ); if(cloud) indices.push(cloud); |
50 | | - |
| 61 | +
|
| 62 | + return local ? loadIndex(null, local, isCloud) : null; |
| 63 | + } (async () => { |
| 64 | + const htmlLang = (document.documentElement.lang || 'en').toLowerCase(); |
| 65 | + const lang = htmlLang.split('-')[0]; |
| 66 | + const mainReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks/releases/download'; |
| 67 | + const cloudReleaseBase = 'https://github.com/HackTricks-wiki/hacktricks-cloud/releases/download'; |
| 68 | +
|
| 69 | + const mainTags = Array.from(new Set(['searchindex-' + lang, 'searchindex-en', 'searchindex-master'])); |
| 70 | + const cloudTags = Array.from(new Set(['searchindex-' + lang, 'searchindex-en', 'searchindex-master'])); |
| 71 | +
|
| 72 | + const MAIN_REMOTE_SOURCES = mainTags.map(function(tag) { return mainReleaseBase + '/' + tag + '/searchindex.js'; }); |
| 73 | + const CLOUD_REMOTE_SOURCES = cloudTags.map(function(tag) { return cloudReleaseBase + '/' + tag + '/searchindex.js'; }); |
| 74 | +
|
| 75 | + const indices = []; |
| 76 | + const main = await loadWithFallback(MAIN_REMOTE_SOURCES , '/searchindex-book.js', false); if(main) indices.push(main); |
| 77 | + const cloud= await loadWithFallback(CLOUD_REMOTE_SOURCES, '/searchindex.js', true ); if(cloud) indices.push(cloud); |
51 | 78 | if(!indices.length){ postMessage({ready:false, error:'no-index'}); return; } |
52 | 79 | |
53 | 80 | /* build index objects */ |
|
160 | 187 | /* ───────────── worker messages ───────────── */ |
161 | 188 | worker.onmessage = ({data}) => { |
162 | 189 | if(data && data.ready!==undefined){ |
163 | | - if(data.ready){ icon.innerHTML=READY_ICON; icon.setAttribute('aria-label','Open search (S)'); } |
164 | | - else { icon.textContent='❌'; icon.setAttribute('aria-label','Search unavailable'); } |
| 190 | + if(data.ready){ |
| 191 | + icon.innerHTML=READY_ICON; |
| 192 | + icon.setAttribute('aria-label','Open search (S)'); |
| 193 | + icon.removeAttribute('title'); |
| 194 | + } |
| 195 | + else { |
| 196 | + icon.textContent='❌'; |
| 197 | + icon.setAttribute('aria-label','Search unavailable'); |
| 198 | + icon.setAttribute('title','Search is unavailable'); |
| 199 | + } |
165 | 200 | return; |
166 | 201 | } |
167 | 202 | const docs=data, q=bar.value.trim(), terms=q.split(/\s+/).filter(Boolean); |
|
0 commit comments