PageSourceSearch

https://radiogroup.sk/assets/js/search.js?v=3

js radiogroup.sk collected 2026-10-02 06:40:01 UTC 8,761 bytes, 180 lines download raw bytes

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 {'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;'}[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.