1/* Fullscreen hľadanie â spoloÄné pre statické stránky aj pre stránky z CMS. */ 2 3(function(){ 4 /* Rádio Vlna poslucháÄom vyká, Fun rádio tyká; hlášky podľa stanice (MiÅ¡ka, 16. 9. 2026). 5 Na živých doménach rozhoduje hostname, v náhľade CMS a lokálne cesta /vlna/. */ 6 var VYKAM = (/vlna/.test(location.hostname) || /^\/vlna\//.test(location.pathname)) && !/^(\/vlna)?\/(radiogroup|case)\//.test(location.pathname); 7 var TX_CHYBA = VYKAM ? 'Hľadanie sa nepodarilo. Skúste to znova.' : 'Hľadanie sa nepodarilo. Skús to znova.'; 8 var TX_NIC = VYKAM ? 'Skúste iné slovo, hľadá sa v titulkoch Älánkov, názvoch relácià aj epizód.' : 'Skús iné slovo, hľadá sa v titulkoch Älánkov, názvoch relácià aj epizód.'; 9 10 if(window.__fsInit) return; window.__fsInit=1; // softnav re-runs inline scripts 11 var panel,input,body,results,histWrap,hist,cols,openBtn; 12 var data=[], ready=false, sel=-1; 13 var KEY='fr_search_history'; 14 15 var norm=function(s){return String(s||'').toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g,'')}; 16 var enc=function(s){return String(s||'').replace(/[&<>"]/g,function(c){return {'&':'&','<':'<','>':'>','"':'"'}[c]})}; 17 18 function readHist(){ try{return JSON.parse(localStorage.getItem(KEY)||'[]')}catch(e){return []} } 19 function pushHist(q){ 20 q=q.trim(); if(q.length<2) return; 21 var h=readHist().filter(function(x){return norm(x)!==norm(q)}); 22 h.unshift(q); h=h.slice(0,8); 23 try{localStorage.setItem(KEY,JSON.stringify(h))}catch(e){} 24 renderHist(); 25 } 26 function renderHist(){ 27 if(!histWrap) return; 28 var h=readHist(); 29 histWrap.hidden=!h.length; 30 hist.innerHTML=h.map(function(q){return '<button class="fs-chip" type="button" data-q="'+enc(q)+'">'+enc(q)+'</button>'}).join(''); 31 } 32 33 /* Hľadá sa naživo cez celý archÃv (45 097 Älánkov). Statický index mal 34 1 300 položiek, len Fun rádio, a bol zastaraný. */ 35 var STANICA = (location.host.indexOf('vlna') === 0 || /radiovlna/.test(location.host)) ? 'vlna' : 'funradio'; 36 function endpoint(q, limit){ 37 return '/hladanie/?station=' + STANICA + '&limit=' + (limit||24) + '&q=' + encodeURIComponent(q||''); 38 } 39 function loadIndex(){ 40 if(ready||window.__fsLoading) return; 41 window.__fsLoading=1; 42 fetch(endpoint('', 8)).then(function(r){return r.json()}).then(function(j){ 43 data=(j.polozky||[]); ready=true; renderPreview(); 44 if(input && input.value.trim()) run(input.value); 45 }).catch(function(){ window.__fsLoading=0 }); 46 } 47 48 49 /* Kým sa naÄÃta ponuka, panel ostával prázdny a obsah doÅho naskoÄil 50 naraz. Kostra držà výšku aj rytmus, takže sa niÄ nepreskupuje. */ 51 function renderKostru(){ 52 if(!cols) return; 53 var skupiny=['Älánky','Relácie a moderátori','Epizódy podcastov']; 54 cols.className='fs-kostra'; 55 cols.innerHTML=skupiny.map(function(n){ 56 var r=''; 57 for(var i=0;i<4;i++) r+='<a class="fs-item" aria-hidden="true"><span class="fs-item-t"></span><span class="fs-item-m"></span></a>'; 58 return '<div class="fs-group"><h4>'+n+'</h4>'+r+'</div>'; 59 }).join(''); 60 } 61 62 function renderPreview(){ 63 if(!cols||!ready) return; 64 cols.className=''; 65 var groups=[['Älánok','Älánky',4],['Relácia','Relácie a moderátori',4],['Epizóda','Epizódy podcastov',4]]; 66 cols.innerHTML=groups.map(function(g){ 67 var items=data.filter(function(x){return x.k===g[0]}).slice(0,g[2]); 68 if(!items.length) return ''; 69 return '<div class="fs-group"><h4>'+enc(g[1])+'</h4>'+items.map(function(x){ 70 var d=x.d? x.d.split('-').reverse().slice(0,2).join('. ')+'.' : ''; 71 return '<a class="fs-item" href="'+x.u+'"><span class="fs-item-t">'+enc(x.t)+'</span>'+ 72 (d? '<span class="fs-item-m">'+d+'</span>':'')+'</a>'; 73 }).join('')+'</div>'; 74 }).join(''); 75 } 76 77 function mark(t,q){ 78 var i=norm(t).indexOf(norm(q)); 79 if(i<0||!q) return enc(t); 80 return enc(t.slice(0,i))+'<mark>'+enc(t.slice(i,i+q.length))+'</mark>'+enc(t.slice(i+q.length)); 81 } 82 83 function run(q){ 84 q=(q||'').trim(); sel=-1; 85 if(q.length<2){ results.hidden=true; body.hidden=false; return } 86 if(!ready){ return } 87 body.hidden=true; results.hidden=false; 88 var n=norm(q); 89 if(!window.__fsCache) window.__fsCache={}; 90 var hotove=window.__fsCache[n]; 91 if(!hotove){ 92 results.innerHTML='<p class="fs-rmeta">Hľadámâ¦</p>'; 93 clearTimeout(window.__fsT); 94 window.__fsT=setTimeout(function(){ 95 fetch(endpoint(q, 30)).then(function(r){return r.json()}).then(function(j){ 96 window.__fsCache[n]=(j.polozky||[]); 97 if(input && norm(input.value.trim())===n) run(input.value); 98 }).catch(function(){ results.innerHTML='<p class="fs-none">'+TX_CHYBA+'</p>' }); 99 }, 220); 100 return; 101 } 102 var all=hotove; 103 all.sort(function(a,b){return norm(a.t).indexOf(n)-norm(b.t).indexOf(n)}); 104 var hits=all.slice(0,40); 105 results.innerHTML='<p class="fs-rmeta">'+(hits.length? hits.length+(all.length>40?'+':'')+' výsledkov pre â'+enc(q)+'"':'NiÄ sa nenaÅ¡lo pre â'+enc(q)+'"')+'</p>'+ 106 (hits.length? hits.map(function(x){ 107 return '<a class="fs-row" href="'+x.u+'"><span class="fs-kind">'+enc(x.k)+'</span>'+ 108 '<span class="fs-title">'+mark(x.t,q)+'</span>'+ 109 '<span class="fs-date">'+(x.d? x.d.split('-').reverse().slice(0,2).join('. ')+'.':'')+'</span></a>' 110 }).join('') : '<p class="fs-none">'+TX_NIC+'</p>'); 111 } 112 113 function open(e){ 114 if(!ready) renderKostru(); 115 var r=openBtn.getBoundingClientRect();
116 panel.style.setProperty('--fx', Math.round(r.left+r.width/2)+'px'); 117 panel.style.setProperty('--fy', Math.round(r.top+r.height/2)+'px'); 118 panel.hidden=false; 119 document.body.classList.add('fs-lock'); 120 // force a reflow so the transition has a start value â don't rely on rAF, 121 // this site's motion layer doesn't always run animation frames 122 void panel.offsetWidth; 123 panel.classList.add('is-open'); 124 setTimeout(function(){ try{input.focus()}catch(e){} }, 180); 125 loadIndex(); renderHist(); 126 } 127 function close(){ 128 panel.classList.remove('is-open'); 129 document.body.classList.remove('fs-lock'); 130 var done=function(){ panel.hidden=true }; 131 // don't rely on transitionend alone â this site's motion layer can swallow it 132 panel.addEventListener('transitionend', done, {once:true}); 133 setTimeout(done, 700); 134 } 135 136 function bind(){ 137 panel=document.getElementById('fsPanel'); if(!panel) return; 138 input=document.getElementById('fsInput'); body=document.getElementById('fsBody'); 139 results=document.getElementById('fsResults'); histWrap=document.getElementById('fsHistWrap'); 140 hist=document.getElementById('fsHist'); cols=document.getElementById('fsCols'); 141 142 document.addEventListener('click',function(e){ 143 var t=e.target.closest('#fsOpen'); 144 if(t){ openBtn=t; e.preventDefault(); open(); return } 145 if(e.target.closest('#fsClose')){ e.preventDefault(); close(); return } 146 if(e.target.closest('#fsClear')){ try{localStorage.removeItem(KEY)}catch(e2){} renderHist(); return } 147 var chip=e.target.closest('#fsHist .fs-chip'); 148 if(chip){ input.value=chip.dataset.q; run(input.value); input.focus() } 149 var row=e.target.closest('.fs-row'); 150 if(row && input.value.trim()) pushHist(input.value); 151 }); 152 153 var t; 154 input.addEventListener('input',function(){ clearTimeout(t); t=setTimeout(function(){run(input.value)},110) }); 155 input.addEventListener('keydown',function(e){ 156 var rows=results.hidden? [] : [].slice.call(results.querySelectorAll('.fs-row')); 157 if(e.key==='Escape'){ close() } 158 else if(e.key==='Enter'){ 159 if(sel>=0&&rows[sel]){ pushHist(input.value); location.href=rows[sel].getAttribute('href') } 160 else if(input.value.trim().length>1){ pushHist(input.value) } 161 } 162 else if(e.key==='ArrowDown'||e.key==='ArrowUp'){ 163 if(!rows.length) return; e.preventDefault(); 164 rows.forEach(function(r){r.classList.remove('is-sel')}); 165 sel=e.key==='ArrowDown'? Math.min(sel+1,rows.length-1) : Math.max(sel-1,0); 166 rows[sel].classList.add('is-sel'); 167 rows[sel].scrollIntoView({block:'nearest'}); 168 } 169 }); 170 document.addEventListener('keydown',function(e){ 171 if(e.key==='Escape'&&!panel.hidden) close(); 172 // "/" opens search, like every good site 173 if(e.key==='/'&&panel.hidden&&!/^(INPUT|TEXTAREA)$/.test(document.activeElement.tagName)){ 174 var b=document.getElementById('fsOpen'); if(b){ e.preventDefault(); openBtn=b; open() } 175 } 176 }); 177 } 178 179 if(document.readyState==='loading') document.addEventListener('DOMContentLoaded',bind); else bind(); 180})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.