1/* ============================================================ 2 FUN RÃDIO · V3 "ORGANIC" · správanie 3 ============================================================ */ 4(() => { 5 'use strict'; 6 7 /* Poistka proti dvom prehrávaÄom naraz. KeÄ sa skript z akéhokoľvek dôvodu 8 spustà v tom istom dokumente druhýkrát (mäkký prechod medzi stránkami, 9 návrat cez históriu), vznikol by druhý Audio a hralo by živé vysielanie 10 spolu s epizódou. Druhé spustenie sa preto ticho ukonÄÃ. */ 11 if (window.__frAppInit) return; 12 window.__frAppInit = true; 13 14 const $ = (s, r = document) => r.querySelector(s); 15 const $$ = (s, r = document) => [...r.querySelectorAll(s)]; 16 const clamp = (v, a, b) => Math.min(b, Math.max(a, v)); 17 const smooth = t => t * t * (3 - 2 * t); 18 19 const reduced = matchMedia('(prefers-reduced-motion: reduce)').matches; 20 if (reduced) document.body.classList.add('reduced'); 21 22 /* ---------- ikonky ---------- */ 23 const ICON_PLAY = '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><path d="M8 5.5v13a1 1 0 0 0 1.53.85l10-6.5a1 1 0 0 0 0-1.7l-10-6.5A1 1 0 0 0 8 5.5z"/></svg>'; 24 const ICON_PAUSE = '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor" aria-hidden="true"><rect x="6" y="5" width="4.4" height="14" rx="1.4"/><rect x="13.6" y="5" width="4.4" height="14" rx="1.4"/></svg>'; 25 const size = (svg, px) => svg.replace('width="16" height="16"', `width="${px}" height="${px}"`); 26 27 /* ============================================================ 28 NAVIGÃCIA · scvrknutie pri scrolle dole, rozvinutie hore 29 ============================================================ */ 30 const nav = $('.nav'); 31 let lastY = scrollY; 32 let navLock = false; 33 function navOnScroll() { 34 if (!nav || navLock) return; 35 const y = scrollY; 36 nav.classList.toggle('nav--scrolled', y > 8); 37 lastY = y; 38 } 39 40 /* overlay menu */ 41 const burger = $('.nav-burger'); 42 const menuOpen = () => { document.body.classList.add('menu-open'); navLock = true; burger?.setAttribute('aria-expanded', 'true'); }; 43 const menuClose = () => { document.body.classList.remove('menu-open'); navLock = false; burger?.setAttribute('aria-expanded', 'false'); }; 44 burger?.addEventListener('click', menuOpen); 45 $('.menu-close')?.addEventListener('click', menuClose); 46 $$('.menu-links a').forEach(a => a.addEventListener('click', menuClose)); 47 48 /* ============================================================ 49 REVEAL ON SCROLL · presunuté do /v3/motion.js (GSAP ScrollTrigger). 50 Starý .fx â .in mechanizmus je VYPNUTÃ; tu ostáva iba trigger 51 odometrových poÄÃtadiel, ktorý bežà so scroll listenerom nižšie. 52 ============================================================ */ 53 function countersOnScroll() { 54 countersCheck(); 55 } 56 57 /* ---------- poÄÃtadlá ---------- */ 58 let countersDone = false; 59 const statsBox = $('[data-stats]'); 60 function countersCheck() { 61 if (countersDone || !statsBox) return; 62 const vh = innerHeight || document.documentElement.clientHeight || 800; 63 if (statsBox.getBoundingClientRect().top > vh * 0.85) return; 64 countersDone = true; 65 $$('[data-count]').forEach(el => { 66 const target = parseFloat(el.dataset.count); 67 const fmt = n => el.dataset.space !== undefined 68 ? Math.round(n).toLocaleString('sk-SK') 69 : String(Math.round(n)); 70 if (!isFinite(target)) return; 71 if (reduced) { el.textContent = fmt(target); return; } 72 const ciel = fmt(target); 73 /* Odometer: pÃsmo nemá tabuľkové ÄÃslice, takže každá medzihodnota 74 mala inú Å¡Ãrku a zámok cez min-width poskakovanie nezastavil. 75 Každá ÄÃslica dostane pevný stĺpik Å¡iroký presne podľa cieľovej 76 ÄÃslice â Å¡Ãrka celku sa poÄas animácie nemenÃ, ÄÃslice tikajú 77 na mieste. Po dobehnutà sa vráti Äistý text: stĺpiky sú v px 78 a pÃsmo je viewportové (clamp), po otoÄenà telefónu by sa roziÅ¡li. */ 79 const spusti = () => { 80 try { 81 const cs = getComputedStyle(el); 82 const ctx = document.createElement('canvas').getContext('2d'); 83 if (!ctx) { el.textContent = ciel; return; } 84 ctx.font = `${cs.fontStyle} ${cs.fontWeight} ${cs.fontSize} ${cs.fontFamily}`; 85 const ls = parseFloat(cs.letterSpacing) || 0; 86 el.textContent = ''; 87 el.style.whiteSpace = 'nowrap'; 88 const sloty = []; 89 for (const znak of ciel) { 90 const s = document.createElement('span'); 91 s.textContent = znak; 92 if (/\d/.test(znak)) { 93 s.style.display = 'inline-block'; 94 s.style.width = (ctx.measureText(znak).width + ls) + 'px'; 95 s.style.textAlign = 'center'; 96 sloty.push(s); 97 } 98 el.appendChild(s); 99 } 100 const zapis = n => { 101 /* veľké ÄÃsla majú po celý Äas rovnaký poÄet ÄÃslic ako cieľ 102 (Å¡tart nepodlieza rád), takže sadnú na stĺpiky 1:1; malé 103 ÄÃsla od nuly sa zarovnávajú doprava ako na tachometri */ 104 const cifry = fmt(n).replace(/\D/g, ''); 105 const posun = sloty.length - cifry.length; 106 for (let i = 0; i < sloty.length; i++) { 107 sloty[i].textContent = i >= posun ? cifry[i - posun] : ''; 108 } 109 }; 110 /* Å tart nesmie podliezÅ¥ rád cieľa: 96 % z 1 035 000 je 993 600 â 111 menej ÄÃslic, iné zoskupenie a zlomený riadok uprostred 112 animácie. Preto sa dvÃha najmenej na okrúhly rád (1 000 000). */ 113 const rad = Math.pow(10, String(Math.round(target)).length - 1); 114 const from = target > 999 ? Math.max(target * 0.96, rad) : 0; 115 const dur = target > 999 ? 900 : 1100; 116 const t0 = performance.now(); 117 const easeOut = t => 1 - Math.pow(1 - t, 3); /* power3.out */ 118 const tick = now => { 119 const p = clamp((now - t0) / dur, 0, 1); 120 if (p < 1) { zapis(from + (target - from) * easeOut(p)); requestAnimationFrame(tick); } 121 else el.textContent = ciel; 122 }; 123 requestAnimationFrame(tick); 124 } catch (e) { el.textContent = ciel; } 125 }; 126 /* PÃsmo sa naÄÃtava so swap â meraÅ¥ sa smie až hotové pÃsmo, inak
127 sa stĺpiky zmerajú vo fallbacku a po výmene pÃsma nesedia. */ 128 if (document.fonts && document.fonts.ready) document.fonts.ready.then(spusti); 129 else spusti(); 130 }); 131 } 132 133 /* ============================================================ 134 HERO FLOAT · jemný paralax chipov a kariet (max 12 px) 135 ============================================================ */ 136 const floatEls = $$('[data-float]'); 137 function floatFrame() { 138 if (reduced) return; 139 const y = Math.min(scrollY, 500); 140 for (const el of floatEls) { 141 const t = clamp(y * parseFloat(el.dataset.float), -12, 12); 142 el.style.translate = `0 ${t}px`; 143 } 144 } 145 146 /* spoloÄný scroll handler */ 147 let raf = 0; 148 addEventListener('scroll', () => { 149 navOnScroll(); 150 if (raf) return; 151 raf = requestAnimationFrame(() => { countersOnScroll(); floatFrame(); raf = 0; }); 152 }, { passive: true }); 153 addEventListener('resize', countersOnScroll, { passive: true }); 154 addEventListener('load', countersOnScroll); 155 countersOnScroll(); 156 157 /* ============================================================ 158 STACK DECK · Naživo teraz 159 ============================================================ */ 160 (function initDeck() { 161 const deck = $('[data-deck]'); 162 if (!deck) return; 163 const cards = $$('.deck-card', deck); 164 let order = [...cards]; 165 let busy = false; 166 167 /* bodky */ 168 const dotsWrap = $('.deck-dots'); 169 cards.forEach((c, i) => { 170 c.dataset.index = String(i); 171 const d = document.createElement('button'); 172 d.className = 'dot'; 173 d.setAttribute('aria-label', `Karta ${i + 1}: ${c.dataset.title || ''}`); 174 d.addEventListener('click', () => { 175 if (busy) return; 176 while (order[0] !== c) order.push(order.shift()); 177 layout(); 178 }); 179 dotsWrap?.appendChild(d); 180 }); 181 const dots = $$('.dot', dotsWrap); 182 183 const DEPTH = [ 184 { y: 0, s: 1, o: 1 }, 185 { y: -22, s: .955, o: .9 }, 186 { y: -42, s: .91, o: .6 }, 187 { y: -58, s: .87, o: 0 }, 188 ]; 189 190 function place(card, d) { 191 const p = DEPTH[Math.min(d, DEPTH.length - 1)]; 192 card.style.zIndex = String(order.length - d); 193 card.style.transform = `translateY(${p.y}px) scale(${p.s})`; 194 card.style.opacity = String(p.o); 195 card.classList.toggle('is-top', d === 0); 196 card.style.pointerEvents = d === 0 ? 'auto' : 'none'; 197 card.setAttribute('aria-hidden', d === 0 ? 'false' : 'true'); 198 } 199 200 function layout(skip) { 201 order.forEach((card, d) => { if (card !== skip) place(card, d); }); 202 const idx = Number(order[0].dataset.index); 203 dots.forEach((dt, i) => dt.classList.toggle('active', i === idx)); 204 } 205 206 /* posun dopredu: vrchná karta odletà (dir = -1 doľava, 1 doprava) */ 207 function next(dir = -1) { 208 if (busy) return; 209 const top = order[0]; 210 order.push(order.shift()); 211 if (reduced) { layout(); return; } 212 busy = true; 213 top.classList.remove('dragging'); 214 top.classList.add('flying'); 215 top.style.zIndex = '20'; 216 top.style.transform = `translate(${dir * 120}%, -24px) rotate(${dir * 14}deg)`; 217 layout(top); // ostatné sa pružne posunú dopredu už poÄas letu 218 setTimeout(() => { 219 top.classList.remove('flying'); 220 top.classList.add('no-trans'); 221 top.style.boxShadow = ''; 222 layout(); // karta sa bez animácie zaradà na spodok 223 void top.offsetWidth; // reflow, aby Äalšà pohyb už animoval 224 top.classList.remove('no-trans'); 225 busy = false; 226 }, 460); 227 } 228 229 /* posun dozadu: spodná karta priletà navrch */ 230 function prev() { 231 if (busy) return; 232 const card = order.pop(); 233 order.unshift(card); 234 if (reduced) { layout(); return; } 235 busy = true; 236 card.classList.add('no-trans'); 237 card.style.zIndex = '20'; 238 card.style.opacity = '1'; 239 card.style.transform = 'translate(115%, -24px) rotate(13deg)'; 240 void card.offsetWidth; 241 card.classList.remove('no-trans'); 242 layout(); // priletà na vrch, ostatné sa zasunú dozadu 243 setTimeout(() => { busy = false; }, 500); 244 } 245 246 /* drag vrchnej karty (pointer events: myÅ¡ aj dotyk) */ 247 let drag = null; 248 deck.addEventListener('pointerdown', e => { 249 if (reduced || busy) return; 250 const top = order[0]; 251 if (!top.contains(e.target)) return; 252 if (e.target.closest('button, a')) return; 253 drag = { id: e.pointerId, x0: e.clientX, y0: e.clientY, dx: 0, active: false }; 254 top.setPointerCapture(e.pointerId); 255 }); 256 deck.addEventListener('pointermove', e => { 257 if (!drag || e.pointerId !== drag.id) return; 258 const top = order[0]; 259 drag.dx = e.clientX - drag.x0; 260 const dy = e.clientY - drag.y0; 261 if (!drag.active && Math.abs(drag.dx) > 6) { 262 drag.active = true; 263 top.classList.add('dragging'); 264 } 265 if (drag.active) { 266 top.style.transform = `translate(${drag.dx}px, ${dy * 0.35}px) rotate(${drag.dx * 0.05}deg)`;
267 const k = Math.min(Math.abs(drag.dx) / deck.offsetWidth, 1); 268 top.style.boxShadow = `0 ${16 + 26 * k}px ${40 + 44 * k}px rgba(61,18,38,${(0.10 + 0.16 * k).toFixed(3)})`; 269 } 270 }); 271 function endDrag(e) { 272 if (!drag || e.pointerId !== drag.id) return; 273 const { dx, active } = drag; 274 drag = null; 275 if (!active) return; 276 const top = order[0]; 277 top.classList.remove('dragging'); 278 if (Math.abs(dx) > deck.offsetWidth * 0.28) { 279 next(Math.sign(dx) || -1); // za prahom: odlet a zaradenie na spodok 280 } else { 281 top.style.boxShadow = ''; 282 layout(); // pred prahom: pružný návrat 283 } 284 } 285 deck.addEventListener('pointerup', endDrag); 286 deck.addEventListener('pointercancel', endDrag); 287 288 /* Å¡Ãpky a klávesnica */ 289 $('[data-deck-next]')?.addEventListener('click', () => next(-1)); 290 $('[data-deck-prev]')?.addEventListener('click', prev); 291 addEventListener('keydown', e => { 292 if (e.key !== 'ArrowLeft' && e.key !== 'ArrowRight') return; 293 const t = document.activeElement; 294 if (t && /^(input|textarea|select)$/i.test(t.tagName)) return; 295 if (document.body.classList.contains('player-open') || document.body.classList.contains('menu-open')) return; 296 const r = deck.getBoundingClientRect(); 297 if (r.bottom < 0 || r.top > innerHeight) return; 298 e.key === 'ArrowRight' ? next(-1) : prev(); 299 }); 300 301 layout(); 302 })(); 303 304 /* ============================================================ 305 STACK LIST · podcasty (accordion, naraz jedna otvorená) 306 ============================================================ */ 307 $$('.stackl-card').forEach(card => { 308 const head = $('.stackl-head', card); 309 const body = $('.stackl-body', card); 310 head.addEventListener('click', () => { 311 const isOpen = card.classList.contains('open'); 312 $$('.stackl-card.open').forEach(c => { 313 c.classList.remove('open'); 314 $('.stackl-body', c).style.maxHeight = '0px'; 315 $('.stackl-head', c).setAttribute('aria-expanded', 'false'); 316 }); 317 if (!isOpen) { 318 card.classList.add('open'); 319 body.style.maxHeight = body.scrollHeight + 'px'; 320 head.setAttribute('aria-expanded', 'true'); 321 } 322 }); 323 }); 324 addEventListener('resize', () => { 325 const open = $('.stackl-card.open .stackl-body'); 326 if (open) open.style.maxHeight = open.scrollHeight + 'px'; 327 }, { passive: true }); 328 329 /* ============================================================ 330 MARQUEE · zduplikovanie obsahu pre nekoneÄnú sluÄku 331 ============================================================ */ 332 const mqTrack = $('.marquee-track'); 333 if (mqTrack) mqTrack.innerHTML += mqTrack.innerHTML; 334 335 /* ============================================================ 336 MAGNETICKà CTA · len desktop (pointer:fine), jemný Å¥ah max 4 px 337 translate je samostatná vlastnosÅ¥ â nekoliduje s :hover transform 338 ============================================================ */ 339 if (!reduced && matchMedia('(pointer: fine)').matches) { 340 $$('[data-magnetic]').forEach(el => { 341 el.addEventListener('pointermove', e => { 342 const r = el.getBoundingClientRect(); 343 const mx = (e.clientX - (r.left + r.width / 2)) / ((r.width / 2) || 1); 344 const my = (e.clientY - (r.top + r.height / 2)) / ((r.height / 2) || 1); 345 el.style.translate = clamp(mx * 4, -4, 4).toFixed(1) + 'px ' + clamp(my * 4, -4, 4).toFixed(1) + 'px'; 346 }); 347 el.addEventListener('pointerleave', () => { el.style.translate = '0 0'; }); 348 }); 349 } 350 351 /* ============================================================ 352 PREHRÃVAÄ Â· jeden Audio element pre celý web 353 ============================================================ */ 354 const AVATAR_ZAKLAD = '/assets/img/funradio-ikona.png'; 355 const STATIONS = [ 356 { id: 'fun', name: 'Fun Rádio', sub: 'Hity, ktoré milujete', show: 'Hity, ktoré milujete', img: null, orb: 'magenta', tone: 'blush', url: 'https://stream.funradio.sk:8000/fun128.mp3' }, 357 { id: 'dance', name: 'Fun Dance', sub: 'TaneÄné hity nonstop', show: 'Fun Dance', img: null, orb: 'magenta', tone: 'peri', url: 'https://stream.funradio.sk:8000/dance128.mp3' }, 358 { id: 'chill', name: 'Fun Chill', sub: 'Pohodové tempo na oddych', show: 'Fun Chill', img: null, orb: 'mint', tone: 'mint-soft', url: 'https://stream.funradio.sk:8000/chill128.mp3' }, 359 { id: '8090', name: 'Fun 80s a 90s', sub: 'Legendy dvoch deká
359d', show: 'Fun 80s a 90s', img: null, orb: 'peri', tone: 'peri-soft', url: 'https://stream.funradio.sk:8000/80-90-128.mp3' }, 360 { id: 'summer', name: 'Fun Summer', sub: 'Letný soundtrack', show: 'Fun Summer', img: null, orb: 'tang', tone: 'tang-soft', url: 'https://stream.funradio.sk:8000/summer128.mp3' }, 361 { id: 'cs', name: 'Fun CS', sub: 'Äesko a Slovensko v hitoch', show: 'Fun CS', img: null, orb: 'blush', tone: 'blush-soft', url: 'https://stream.funradio.sk:8000/cs128.mp3' }, 362 { id: 'mile', name: 'Fun Mileniálky', sub: 'Hity generácie Y', show: 'Fun Mileniálky', img: null, orb: 'butter', tone: 'butter-soft', url: 'https://stream.funradio.sk:8000/milenialky128.mp3' }, 363 ]; 364 const EPISODES = [ 365 { id: "ep1667", name: "NauÄila som sa, že život sa nemusà prežiÅ¥ v tlaku, ale dá saâ¦", sub: "hereÄka a speváÄka Nela Pocisková · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/naucila-som-sa-ze-zivot-sa-nemusi-prezit-v-tlaku-ale-da-sa-aj-tesit-herecka-a-spevacka-nela-pociskova/audio.mp3" }, 366 { id: "ep1664", name: "Kavej nie je \"crazy\" komédia, je hraná na vážno", sub: "producent Dano Dangl a režisér Lukáš ZednikoviÄ Â· Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/kavej-nie-je-crazy-komedia-je-hrana-na-vazno-producent-dano-dangl-a-reziser-lukas-zednikovic/audio.mp3" }, 367 { id: "ep1661", name: "Najdôležitejšà je pocit. Älovek sa v Å¡atách musà páÄiÅ¥ sám sebe", sub: "návrhárka Lýdia Eckhardt · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/najdolezitejsi-je-pocit-clovek-sa-v-satach-musi-pacit-sam-sebe-navrharka-lydia-eckhardt/audio.mp3" }, 368 { id: "ep1657", name: "Neurochirurgovia sa dotýkajú mozgu veľmi málo", sub: "neurochirurg Svorad Trnovec · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/neurochirurgovia-sa-dotykaju-mozgu-velmi-malo-neurochirurg-svorad-trnovec/audio.mp3" }, 369 { id: "ep1653", name: "Vzory zo slovenských krojov premieÅa na Å¡perky. Neexistuje jeâ¦", sub: "Å¡perkárka Petra Tóth · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/vzory-zo-slovenskych-krojov-premiena-na-sperky-neexistuje-jeden-slovensky-kroj-sperkarka-petra-toth/audio.mp3" }, 370 { id: "ep1650", name: "Po prvej epizóde Milujem Slovensko som dostal veľkú výtku, žeâ¦", sub: "režisér Rado Å tefanov · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/po-prvej-epizode-milujem-slovensko-som-dostal-velku-vytku-ze-to-je-vela-reziser-rado-stefanov/audio.mp3" }, 371 { id: "ep1645", name: "Kde inde sa majú deti nauÄiÅ¥ spolu fungovaÅ¥ aj keÄ sú rozdielâ¦", sub: "pedagogiÄka Anna Symington-Maar · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/kde-inde-sa-maju-deti-naucit-spolu-fungovat-aj-ked-su-rozdielne-ak-nie-v-skole-pedagogicka-anna-symington-maar/audio.mp3" }, 372 { id: "ep1644", name: "K foteniu známych ľudà som sa dostala postupne, vÅ¡etko má svoâ¦", sub: "fotografka KatarÃna Hoffman Å lesárová · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/k-foteniu-znamych-ludi-som-sa-dostala-postupne-vsetko-ma-svoj-cas-fotografka-katarina-hoffman-slesarova/audio.mp3" }, 373 { id: "ep1638", name: "S pokroÄilosÅ¥ou umelej inteligencie sa to preháÅa. Filozofkaâ¦", sub: "Podcast · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/s-pokrocilostou-umelej-inteligencie-sa-to-prehana-filozofka-daniela-vacek/audio.mp3" }, 374 { id: "ep1637", name: "Jedno dieÅ¥a alebo dve? Q&A Part 4", sub: "Podcast · Adela a Sajfa", show: "Adela a Sajfa", img: "/assets/img/show-adela-a-sajfa.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/adela-a-sajfa/jedno-dieta-alebo-dve-qa-part-4/audio.mp3" }, 375 { id: "ep1585", name: "ÄUÄOVI RODIÄIA VYSLALI SIGNÃL feat. Hex", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/dudovi-rodicia-vyslali-signal-feat-hex/audio.mp3" }, 376 { id: "ep1584", name: "MILUJEME PIKAJDY feat. Evelyn", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/milujeme-pikajdy-feat-evelyn/audio.mp3" }, 377 { id: "ep1388", name: "\"Je fér, že spievaÅ¡ 40 rokov a futbalista za kratšà Äas zarobâ¦", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/je-fer-ze-spievas-40-rokov-a-futbalista-za-kratsi-cas-zarobi-viac-feat-dara-rolins/audio.mp3" }, 378 { id: "ep1387", name: "\"Raz ma na festivale nepustili na stage,\" feat. Tina", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/raz-ma-na-festivale-nepustili-na-stage-feat-tina/audio.mp3" }, 379 { id: "ep1386", name: "\"Bol si už niekedy celý deÅ v pyžame?\" feat. Marián GáborÃk", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/bol-si-uz-niekedy-cely-den-v-pyzame-feat-marian-gaborik/audio.mp3" }, 380 { id: "ep1385", name: "\"Ty si sa mýlila, ten prÃbeh pokraÄuje Äalej,\" feat. Sandra zâ¦", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/ty-si-sa-mylila-ten-pribeh-pokracuje-dalej-feat-sandra-z-dejepis-inak/audio.mp3" }, 381 { id: "ep1384", name: "\"KeÄ si hladný, je dobrý aj spotený,\" feat. Peter BiÄ Project", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/ked-si-hladny-je-dobry-aj-spoteny-feat-peter-bic-project/audio.mp3" }, 382 { id: "ep1344", name: "âPovedz vtip nie je stand-up,â feat. Joe Trendy", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/povedz-vtip-nie-je-stand-up-feat-joe-trendy/audio.mp3" }, 383 { id: "ep1345", name: "âRiÅ¡o, ty máš fest veľkú hlavu,â feat. Zuzka Smatanová a Richâ¦", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/riso-ty-mas-fest-velku-hlavu-feat-zuzka-smatanova-a-richard-autner/audio.mp3" }, 384 { id: "ep1343", name: "âPrdla si si poÄas koncertu?â feat. Emma Drobná", sub: "Podcast · Sajfa Å¡ou", show: "Sajfa Å¡ou", img: "/assets/img/show-sajfa-sou.webp", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/sajfa-sou/prdla-si-si-pocas-koncertu-feat-emma-drobna/audio.mp3" },
385 { id: "ep1663", name: "Môj prvý bikepark? Lopata a igelit na teniskách â staviteľ trâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/moj-prvy-bikepark-lopata-a-igelit-na-teniskach-stavitel-trailov-jaroslav-lucan/audio.mp3" }, 386 { id: "ep1660", name: "V pesniÄke \"Nikdy nebolo lepÅ¡ie\" je v úvode nieÄo, o Äom niktâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/v-pesnicke-nikdy-nebolo-lepsie-je-v-uvode-nieco-o-com-nikto-nevie-producent-tomas-sloboda/audio.mp3" }, 387 { id: "ep1658", name: "V naÅ¡ej práci neexistuje home office ani poloviÄný úväzok â Mâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/v-nasej-praci-neexistuje-home-office-ani-polovicny-uvazok-martin-bandzak-z-organizacie-magna/audio.mp3" }, 388 { id: "ep1654", name: "Máš v Japonsku stretko o deviatej. PrÃdeÅ¡ na deviatu? MeÅ¡káš!â¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/mas-v-japonsku-stretko-o-deviatej-prides-na-deviatu-meskas-japonologicka-nina-frankova/audio.mp3" }, 389 { id: "ep1651", name: "âDoplnky stravy dnes berieme ako Lentilky,â holistická mentorâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/doplnky-stravy-dnes-berieme-ako-lentilky-holisticka-mentorka-zdravia-mirka-janigova/audio.mp3" }, 390 { id: "ep1646", name: "OÅ¡kvarky sú najlepÅ¡ou potravou na ultratrailové behy â ultramâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/oskvarky-su-najlepsou-potravou-na-ultratrailove-behy-ultramaratonec-jaroslav-dej/audio.mp3" }, 391 { id: "ep1641", name: "Na svojej prvej opere som zaspal â operný spevák Pavol KubáÅ", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/na-svojej-prvej-opere-som-zaspal-operny-spevak-pavol-kuban/audio.mp3" }, 392 { id: "ep1639", name: "Do jaskynnej kancelárie šľape hodiny a drepuje, aby tam nezamâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/do-jaskynnej-kancelarie-slape-hodiny-a-drepuje-aby-tam-nezamrzol-svetelny-dizajner-jan-ptacin/audio.mp3" }, 393 { id: "ep1634", name: "Zvieratá v ZOO jedia kvalitnejÅ¡ie jedlo ako my â hovorkyÅa ZOâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/zvierata-v-zoo-jedia-kvalitnejsie-jedlo-ako-my-hovorkyna-zoo-bratislava-zuzana-dzurdzikova/audio.mp3" }, 394 { id: "ep1632", name: "Bola na konzultácii vo Viedni a pozvali ju na stáž do New Yorâ¦", sub: "Podcast · Talkshow so Å arkanom", show: "Talkshow so Å arkanom", img: "/assets/img/show-nedelna-talkshow.webp", tone: "peri", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/talkshow-so-sarkanom/bola-na-konzultacii-vo-viedni-a-pozvali-ju-na-staz-do-new-yorku-architektka-lenka-petrakova/audio.mp3" }, 395 { id: "ep1668", name: "175 NÃRI PREPISJà HISTÃRIU, NEYMAR SA LÃÄIL S REPRE", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/175-nori-prepisju-historiu-neymar-sa-lucil-s-repre/audio.mp3" }, 396 { id: "ep1665", name: "174 MS VO FUTBALE S REKORDAMI A PLAYOFF", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/174-ms-vo-futbale-s-rekordami-a-playoff/audio.mp3" }, 397 { id: "ep1662", name: "173 RONALDO A MESSI Sà AKO ANDROID A iPHONE", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/173-ronaldo-a-messi-su-ako-android-a-iphone/audio.mp3" }, 398 { id: "ep1659", name: "172 Å TYRI ROKY SOM NEVIDEL FUTBAL feat. Å tevo Eisele", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/172-styri-roky-som-nevidel-futbal-feat-stevo-eisele/audio.mp3" }, 399 { id: "ep1655", name: "171 NA MS VO FUTBALE SI NIKOHO NEVIEM PREDSTAVIŤ AKO VÃŤAZA", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/171-na-ms-vo-futbale-si-nikoho-neviem-predstavit-ako-vitaza/audio.mp3" }, 400 { id: "ep1652", name: "170 GENERÃCIA VICEMAJSTROV SVETA", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/170-generacia-vicemajstrov-sveta/audio.mp3" }, 401 { id: "ep1649", name: "169 VEĽKà TIPOVAÄKA NA VÃŤAZOV A CYKLISTA NA \"SEX\"", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/169-velka-tipovacka-na-vitazov-a-cyklista-na-sex/audio.mp3" }, 402 { id: "ep1643", name: "168 ARSENAL TO DAL, AJ MONTREAL TO DAL", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/168-arsenal-to-dal-aj-montreal-to-dal/audio.mp3" }, 403 { id: "ep1640", name: "167 KIMI VEDEL \"VYPIC\" feat. Å tevo Eisele", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/167-kimi-vedel-vypic-feat-stevo-eisele/audio.mp3" }, 404 { id: "ep1635", name: "166 STRIEBRO JE OBROVSKà ÃSPECH, AJ KEÄ BOĽAVÃ", sub: "Podcast · FUNúšik", show: "FUNúšik", img: "/assets/img/pod-funusik.png", tone: "mint", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/funusik/166-striebro-je-obrovsky-uspech-aj-ked-bolavy/audio.mp3" }, 405 { id: "ep1666", name: "festival season", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/festival-season/audio.mp3" }, 406 { id: "ep1656", name: "podľa Michaela Jacksona", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/podla-michaela-jacksona/audio.mp3" }, 407 { id: "ep1647", name: "cafe del fun", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/cafe-del-fun/audio.mp3" }, 408 { id: "ep1636", name: "afrobeat", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/afrobeat/audio.mp3" }, 409 { id: "ep1619", name: "leto podľa nás", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/leto-podla-nas/audio.mp3" }, 410 { id: "ep1611", name: "podľa Harryho Stylesa", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/podla-harryho-stylesa/audio.mp3" }, 411 { id: "ep1604", name: "5 pesniÄiek 5 krajÃn", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/5-pesniciek-5-krajin/audio.mp3" }, 412 { id: "ep1593", name: "hudobné udalosti", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/hudobne-udalosti/audio.mp3" }, 413 { id: "ep1583", name: "podľa Bruna Marsa", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/podla-bruna-marsa/audio.mp3" }, 414 { id: "ep1579", name: "Silvester 2025", sub: "Podcast · PODĽA MÅA", show: "PODĽA MÅA", img: "/assets/img/pod-podla-mna.png", tone: "butter", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/podla-mna/silvester-2025/audio.mp3" }, 415 { id: "ep1534", name: "KERKA NA NEPODSTATNOM MIESTE. V SOBRANCIACH?", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/kerka-na-nepodstatnom-mieste-v-sobranciach/audio.mp3" }, 416 { id: "ep1508", name: "MIKROZVUK TÃŽDÅA UCHOP ZO SPODU", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/mikrozvuk-tyzdna-uchop-zo-spodu/audio.mp3" },
417 { id: "ep1507", name: "VIEME AKO DRUHÃ", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/vieme-ako-druhi/audio.mp3" }, 418 { id: "ep1506", name: "JE TU AJ INà HAJZEL OKREM VÃS DVOCH?", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/je-tu-aj-iny-hajzel-okrem-vas-dvoch/audio.mp3" }, 419 { id: "ep1505", name: "ZOMRIEŤ S BUNDÃÅ KEÅÃROM V RUKE", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/zomriet-s-bundas-kenyrom-v-ruke/audio.mp3" }, 420 { id: "ep1504", name: "DÃM TO PSOVI? ZJEÅ TO TY? feat. Celeste Buckingham", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/dam-to-psovi-zjes-to-ty-feat-celeste-buckingham/audio.mp3" }, 421 { id: "ep1503", name: "BOH MESTSKEJ HROMADNEJ DOPRAVY EMHÃDES feat. Jozef Vajda", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/boh-mestskej-hromadnej-dopravy-emhades-feat-jozef-vajda/audio.mp3" }, 422 { id: "ep1502", name: "TOTO JE EXKLUZÃVNA RANNà ŠOU Z NÃRODNEJ KEKSIKÃRNE", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/toto-je-exkluzivna-ranna-sou-z-narodnej-keksikarne/audio.mp3" }, 423 { id: "ep1501", name: "DOÅ IEL BENZÃN. TAK HO PRIVÃTAJ.", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/dosiel-benzin-tak-ho-privitaj/audio.mp3" }, 424 { id: "ep1500", name: "MY NIE SME NORMÃLNI, ALE TOTO AJ MÅA ZASKOÄILO", sub: "Podcast · Ranná Å¡ou s Juniorom a Marcelom", show: "Ranná Å¡ou s Juniorom a Marcelom", img: "/assets/img/show-ranna-sou.webp", tone: "blush", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/ranna-sou-s-juniorom-a-marcelom/my-nie-sme-normalni-ale-toto-aj-mna-zaskocilo/audio.mp3" }, 425 { id: "ep1536", name: "UrobiÅ¥ poctu Erikovi a Barbare bol môj sen, vÄaka nim som oveâ¦", sub: "MIRO JAROŠ· Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/urobit-poctu-erikovi-a-barbare-bol-moj-sen-vdaka-nim-som-ovela-viac-snival-miro-jaros/audio.mp3" }, 426 { id: "ep1431", name: "Cesta nie je byÅ¥ sám proti vÅ¡etkým", sub: "MOMO & ERIC TRESOR · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/cesta-nie-je-byt-sam-proti-vsetkym-momo-eric-tresor/audio.mp3" }, 427 { id: "ep1330", name: "FIS Å PECIÃL", sub: "VypoÄuj si nový album IMT Smile Exoticana, s Älenmi skupiny IMT Smile · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/fis-special-vypocuj-si-novy-album-imt-smile-exoticana-s-clenmi-skupiny-imt-smile/audio.mp3" }, 428 { id: "ep1028", name: "\"KeÄ chceÅ¡ nieÄo vytvoriÅ¥, musÃÅ¡ si sadnúť na zadok.\"", sub: "MARTIN HARICH · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/ked-chces-nieco-vytvorit-musis-si-sadnut-na-zadok-martin-harich/audio.mp3" }, 429 { id: "ep754", name: "\"Kali nám dal energiu, keÄ sme to najviac potrebovali,\" Ivanâ¦", sub: "FIS Å¡peciál · Fu
429n in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/kali-nam-dal-energiu-ked-sme-to-najviac-potrebovali-ivan-tasler-a-kali-fis-special/audio.mp3" }, 430 { id: "ep716", name: "Vojtik: âĽudà ako som ja si veľa ľudà nevypoÄuje. Som rád, žeâ¦", sub: "Podcast · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/vojtik-ludi-ako-som-ja-si-vela-ludi-nevypocuje-som-rad-ze-objavili-detviansky-sen/audio.mp3" }, 431 { id: "ep686", name: "FIS", sub: "Popstar proti svojej vôli | Zuzana Smatanová vo FUN live unplugged · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/fis-popstar-proti-svojej-voli-zuzana-smatanova-vo-fun-live-unplugged/audio.mp3" }, 432 { id: "ep664", name: "\"Dusil som sa pri gitarovom sóle,\" Peter BiÄ Project", sub: "FUN LIVE UNPLUGGED · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/dusil-som-sa-pri-gitarovom-sole-peter-bic-project-fun-live-unplugged/audio.mp3" }, 433 { id: "ep638", name: "FIS Å PECIÃL", sub: "VypoÄuj si nový album Celeste Buckingham so Celeste Buckigham · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/fis-special-vypocuj-si-novy-album-celeste-buckingham-so-celeste-buckigham/audio.mp3" }, 434 { id: "ep571", name: "FIS Å PECIÃL", sub: "Hity, ktoré preslávili skupinu Hex · Fun in Slovakia", show: "Fun in Slovakia", img: "/assets/img/pod-fun-in-slovakia.jpg", tone: "tang", url: "https://op3.dev/e//podcast.sepia.sk/episode/jasomfunradio/fun-in-slovakia/fis-special-hity-ktore-preslavili-skupinu-hex/audio.mp3" }, 435 { id: "ep688", name: "Strýco Filip v #lvllama: âTýždeÅ som nejedol mäso. Bolo to stâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/stryco-filip-v-lvllama-tyzden-som-nejedol-maso-bolo-to-strasne-cely-som-sa-trasol/audio.mp3" }, 436 { id: "ep680", name: "âMôj otec hral vo Fontáne pre Zuzanu,â herec a Å¡tudent medicÃâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/moj-otec-hral-vo-fontane-pre-zuzanu-herec-a-student-mediciny-victor-ibara-v-lvllama/audio.mp3" }, 437 { id: "ep677", name: "\"Odkedy nemám vlasy, viacej hrám!\" Herec Daniel Fischer v #lvâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/odkedy-nemam-vlasy-viacej-hram-herec-daniel-fischer-v-lvllama/audio.mp3" }, 438 { id: "ep675", name: "Najsledovanejšà Slovák na TikToku, Valden, v #lvllama: âS priâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/najsledovanejsi-slovak-na-tiktoku-valden-v-lvllama-s-priatelkou-sarou-sa-vo-videach-plieskame-po-zadku/audio.mp3" }, 439 { id: "ep604", name: "Rytmus v Å¡peciálnej #lvllama: âVianoce mám rád odkedy som spoâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/rytmus-v-specialnej-lvllama-vianoce-mam-rad-odkedy-som-spoznal-jasminu-dovtedy-som-bol-sebec-tradicna-cast-tvojej-oblubenej-sou/audio.mp3" }, 440 { id: "ep603", name: "Koľko hodÃn spávajú raperi? Aj to prezradil Pil C v novej #lvâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/kolko-hodin-spavaju-raperi-aj-to-prezradil-pil-c-v-novej-lvllama/audio.mp3" }, 441 { id: "ep599", name: "Å tevo Eisele v #lvllama: âElektrické formule? Tak to nie. Hraâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/stevo-eisele-v-lvllama-elektricke-formule-tak-to-nie-hrackarske-haraburdy/audio.mp3" }, 442 { id: "ep594", name: "âMám kontakt aj na Dana Bilzeriana!â Krásna Kika ÄuriÅ¡ová v #â¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/mam-kontakt-aj-na-dana-bilzeriana-krasna-kika-durisova-v-lvllama/audio.mp3" }, 443 { id: "ep589", name: "BojovnÃk UFC Martin Buday v #lvllama: âCÃtim sa jak dôchodcaâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/bojovnik-ufc-martin-buday-v-lvllama-citim
443-sa-jak-dochodca-krize-ma-bola-uz-teraz/audio.mp3" }, 444 { id: "ep539", name: "âSvoj rómsky pôvod som vedela využiÅ¥ vo svoj prospech.â Speváâ¦", sub: "Podcast · Level Lama", show: "Level Lama", img: "/assets/img/pod-level-lama.jpg", tone: "peri", url: "https://podcast.sepia.sk/episode/jasomfunradio/level-lama/svoj-romsky-povod-som-vedela-vyuzit-vo-svoj-prospech-spevacka-zea-hostkou-v-lvllama/audio.mp3" }, 445 { id: "ep1078", name: "MICHAELA TLAPOVÃ", sub: "Å kolský projekt ju priviedol k Instagramu, kde spravuje najväÄšà úÄet o Bratislave · Slováci sú prima", show: "Slováci sú prima", img: "/assets/img/funradio-logo.svg", tone: "mint", url: "https://podcast.sepia.sk/episode/jasomfunradio/slovaci-su-prima/michaela-tlapova-skolsky-projekt-ju-priviedol-k-instagramu-kde-spravuje-najvacsi-ucet-o-bratislave/audio.mp3" }, 446 { id: "ep997", name: "DENIS KOVÃÄ", sub: "\"ÄÃtaÅ¥ motivaÄné knihy a niÄ z toho nerobiÅ¥ je strata Äasu.\" · Slováci sú prima", show: "Slováci sú prima", img: "/assets/img/funradio-logo.svg", tone: "mint", url: "https://podcast.sepia.sk/episode/jasomfunradio/slovaci-su-prima/denis-kovac-citat-motivacne-knihy-a-nic-z-toho-nerobit-je-strata-casu/audio.mp3" }, 447 { id: "ep947", name: "SLOVÃCI Sà PRIMA", sub: "Mirka cez úspeÅ¡ný profil Viete po slovensky popularizuje slovenÄinu · Slováci sú prima", show: "Slováci sú prima", img: "/assets/img/funradio-logo.svg", tone: "mint", url: "https://podcast.sepia.sk/episode/jasomfunradio/slovaci-su-prima/slovaci-su-prima-mirka-cez-uspesny-profil-viete-po-slovensky-popularizuje-slovencinu/audio.mp3" }, 448 { id: "ep948", name: "SLOVÃCI Sà PRIMA", sub: "Daj Marekovi päť slov, napÃÅ¡e ti poviedku DO STO SLOV · Slováci sú prima", show: "Slováci sú prima", img: "/assets/img/funradio-logo.svg", tone: "mint", url: "https://podcast.sepia.sk/episode/jasomfunradio/slovaci-su-prima/slovaci-su-prima-daj-marekovi-pat-slov-napise-ti-poviedku-do-sto-slov/audio.mp3" }, 449 { id: "ep949", name: "SLOVÃCI Sà PRIMA", sub: "Jakub Nagy vyhral spolu s tÃmom súťaž americkej armády · Slováci sú prima", show: "Slováci sú prima", img: "/assets/img/funradio-logo.svg", tone: "mint", url: "https://podcast.sepia.sk/episode/jasomfunradio/slovaci-su-prima/slovaci-su-prima-jakub-nagy-vyhral-spolu-s-timom-sutaz-americkej-armady/audio.mp3" }, 450 { id: "ep1398", name: "4 \"KeÄ som poÄul KeÄ sme sami, neveril som, že ste to vy,\" feâ¦", sub: "Podcast · VÅ ETKO, ÄO MÃM RÃD #HEX35", show: "VÅ ETKO, ÄO MÃM RÃD #HEX35", img: "/assets/img/funradio-logo.svg", tone: "butter", url: "https://podcast.sepia.sk/episode/jasomfunradio/vsetko-co-mam-rad-hex35/4-ked-som-pocul-ked-sme-sami-neveril-som-ze-ste-to-vy-feat-lukas-latinak/audio.mp3" }, 451 { id: "ep1397", name: "3 \"Na koncerty so sebou nosÃte obrovský aparát spomienok,\" feâ¦", sub: "Podcast · VÅ ETKO, ÄO MÃM RÃD #HEX35", show: "VÅ ETKO, ÄO MÃM RÃD #HEX35", img: "/assets/img/funradio-logo.svg", tone: "butter", url: "https://podcast.sepia.sk/episode/jasomfunradio/vsetko-co-mam-rad-hex35/3-na-koncerty-so-sebou-nosite-obrovsky-aparat-spomienok-feat-tina-a-emma-drobna/audio.mp3" }, 452 { id: "ep1377", name: "2 \"Älovek na vás aj ide zabudnúť a potom vás zahrajú na hokejâ¦", sub: "Podcast · VÅ ETKO, ÄO MÃM RÃD #HEX35", show: "VÅ ETKO, ÄO MÃM RÃD #HEX35", img: "/assets/img/funradio-logo.svg", tone: "butter", url: "https://podcast.sepia.sk/episode/jasomfunradio/vsetko-co-mam-rad-hex35/2-clovek-na-vas-aj-ide-zabudnut-a-potom-vas-zahraju-na-hokeji-feat-jano-gordulic/audio.mp3" }, 453 { id: "ep1376", name: "1 \"Pre mÅa ste najstarÅ¡ia mladá kapela,\" feat. Junior a Marcel", sub: "Podcast · VÅ ETKO, ÄO MÃM RÃD #HEX35", show: "VÅ ETKO, ÄO MÃM RÃD #HEX35", img: "/assets/img/funradio-logo.svg", tone: "butter", url: "https://podcast.sepia.sk/episode/jasomfunradio/vsetko-co-mam-rad-hex35/1-pre-mna-ste-najstarsia-mlada-kapela-feat-junior-a-marcel/audio.mp3" }, 454 ]; 455 const TRACKS = [...STATIONS, ...EPISODES]; 456 const isStation = t => STATIONS.some(s => s.id === t.id); 457 458 const audio = new Audio(); 459 /* ---------- hlasitosÅ¥ (posuvnÃk vo veľkom prehrávaÄi) ---------- 460 PoslucháÄka hlásila, že hlasitosÅ¥ na webe neÅ¡la ovládaÅ¥: doteraz tu 461 nebol žiadny ovládaÄ. Hodnota sa pamätá. Na iPhone hlasitosÅ¥ stránka 462 ovládaÅ¥ nemôže (systémové obmedzenie), tam je posuvnÃk sc
462hovaný v CSS. */ 463 const vol = document.querySelector('.player-vol-range'); 464 if (vol) { 465 let ulozena = 1; 466 try { ulozena = Math.min(1, Math.max(0, parseFloat(localStorage.getItem('rg-hlasitost') || '1'))); } catch (e) {} 467 if (isFinite(ulozena)) { audio.volume = ulozena; vol.value = String(ulozena); } 468 vol.addEventListener('input', () => { 469 const v = Math.min(1, Math.max(0, parseFloat(vol.value) || 0)); 470 audio.volume = v; 471 audio.muted = v === 0; 472 try { localStorage.setItem('rg-hlasitost', String(v)); } catch (e) {} 473 }); 474 } 475 476 /* softnav needs to know whether sound is running, so it can swap <main> instead of 477 doing a full page load (which would tear this element down mid-track) */ 478 window.__frAudio = audio; 479 480 /* Naraz smie hraÅ¥ len jedna karta prehliadaÄa. PosluchÃ¡Ä mal bežne 481 pustený stream v jednej karte a podcast v druhej â hralo oboje cez 482 seba a refresh nepomohol, lebo zvuk Å¡iel z inej karty. Každé 483 spustenie sa ohlási ostatným kartám a tie sa stÃÅ¡ia. */ 484 let kanalKariet = null; 485 try { kanalKariet = new BroadcastChannel('rg-prehravac'); } catch (e) {} 486 const tatoKarta = Math.random().toString(36).slice(2); 487 if (kanalKariet) { 488 kanalKariet.onmessage = (e) => { 489 if (e && e.data && e.data.hra && e.data.kto !== tatoKarta && !audio.paused) pause(); 490 }; 491 audio.addEventListener('play', () => { 492 try { kanalKariet.postMessage({ hra: true, kto: tatoKarta }); } catch (e) {} 493 }); 494 } 495 496 audio.preload = 'none'; 497 let current = STATIONS[0]; 498 let playing = false; 499 let loading = false; 500 let listenMs = 0; 501 let lastTick = 0; 502 503 /* dock */ 504 const dock = $('.dock'); 505 const dockBtn = $('.dock-btn'); 506 const dockTitle = $('.dock-meta strong'); 507 const dockSub = $('.dock-meta span'); 508 const dockArtImg = $('.dock-art img'); 509 const dockArtOrb = $('.dock-art .orb'); 510 511 /* fullscreen prehrávaÄ */ 512 const player = $('.player'); 513 const playerTitle = $('.player-title'); 514 const playerAvatarImg = $('.player-avatar img'); 515 const playerCard = $('.player-card'); 516 const playerArtImg = $('.player-art-img'); 517 const playerArtOrb = $('.player-art-orb'); 518 const playerPlay = $('.player-play'); 519 const playerState = $('.player-state'); 520 const playerClock = $('.player-clock'); 521 const playerProgress = $('.player-progress'); 522 const playerBar = $('.player-progress .bar'); 523 const playerStations = $('.player-stations'); 524 const stationsToggle = $('.player-stations-toggle'); 525 526 function setArt(imgEl, orbEl, track) { 527 if (!imgEl || !orbEl) return; 528 if (track.img) { 529 imgEl.src = track.img; 530 imgEl.alt = isStation(track) ? (track.show || track.name) : track.name; 531 imgEl.hidden = false; 532 orbEl.hidden = true; 533 } else { 534 imgEl.hidden = true; 535 orbEl.hidden = false; 536 orbEl.className = orbEl.className.replace(/orb--\S+/, 'orb--' + (track.orb || 'magenta')); 537 } 538 } 539 540 function paint() { 541 /* dock */ 542 if (dockBtn) dockBtn.innerHTML = size(playing ? ICON_PAUSE : ICON_PLAY, 18); 543 if (dockTitle) dockTitle.textContent = current.name; 544 if (dockSub) dockSub.textContent = loading ? 'NaÄÃtavam prenos' : (playing ? (isStation(current) ? 'Naživo' : 'Prehráva sa') : current.sub); 545 dock?.classList.toggle('playing', playing); 546 setArt(dockArtImg, dockArtOrb, current); 547 548 /* fullscreen prehrávaÄ */ 549 if (playerTitle) playerTitle.textContent = isStation(current) ? current.show : current.name; 550 if (playerAvatarImg) playerAvatarImg.src = current.img || AVATAR_ZAKLAD; 551 if (playerCard) playerCard.className = 'player-card pastel-card tone-' + (current.tone || 'blush'); 552 setArt(playerArtImg, playerArtOrb, current); 553 if (playerArtImg && current.img) playerArtImg.alt = (isStation(current) ? current.show : current.name); 554 if (playerPlay) { 555 $('.pp-ic', playerPlay).innerHTML = size(playing ? ICON_PAUSE : ICON_PLAY, 18); 556 $('.pp-label', playerPlay).textContent = playing ? 'Pauza' : 'HraÅ¥'; 557 playerPlay.classList.toggle('playing', playing); 558 } 559 if (playerState) playerState.textContent = isStation(current) ? 'NAŽIVO' : 'EPIZÃDA'; 560 playerProgress?.classList.toggle('live', isStation(current)); 561 $$('.player-stations button').forEach(b => b.classList.toggle('active', b.dataset.track === current.id)); 562 563 /* staniÄné karty */ 564 $$('.station-card').forEach(el => { 565 const active = playing && el.dataset.track === current.id; 566 el.classList.toggle('playing', active); 567 const ic = $('.st-disc', el); 568 if (ic) ic.innerHTML = size(active ? ICON_PAUSE : ICON_PLAY, 16); 569 }); 570 571 /* play pilulky (deck, epizódy) */ 572 $$('.play-pill').forEach(el => { 573 if (el === playerPlay) return; 574 const active = playing && el.dataset.track === current.id; 575 el.classList.toggle('playing', active); 576 const ic = $('.pp-ic', el); 577 if (ic) ic.innerHTML = size(active ? ICON_PAUSE : ICON_PLAY, 13); 578 const label = $('.pp-label', el); 579 if (label) label.textContent = active ? 'Pauza' : (el.dataset.label || 'PrehraÅ¥'); 580 }); 581 582 /* hero CTA */ 583 $$('[data-play-live]').forEach(b => { 584 const live = playing && isStation(current); 585 b.innerHTML = size(live ? ICON_PAUSE : ICON_PLAY, 18) + '<span>' + (live ? 'PozastaviÅ¥' : 'PustiÅ¥ naživo') + '</span>'; 586 }); 587 } 588 589 /* Epizód je vyÅ¡e 3400, takže sa nedajú do skriptu. Stránka, ktorá epizódu 590 vykresľuje, jej údaje pripne priamo na tlaÄidlo a prehrávaÄ si ich prevezme. */ 591 function adopt(el) { 592 const id = el.dataset.track; 593 if (!id || !el.dataset.audio || TRACKS.some(x => x.id === id)) return; 594 TRACKS.push({ 595 id, 596 name: el.dataset.title || 'Epizóda', 597 sub: el.dataset.sub || '', 598 show: el.dataset.show || '', 599 img: el.dataset.img || '/assets/img/funradio-logo.svg', 600 tone: el.dataset.tone || 'blush', 601 url: el.dataset.audio, 602 }); 603 } 604 605 /* ââ OdolnosÅ¥ streamu na mobile (poslucháÄka Vlny, Android, 16. 9. 2026) ââ
606 Chrome na Androide stream pri výpadku siete, po hovore alebo po prepnutà 607 Wi-Fi na dáta zastavà a sám ho neobnovÃ. Kým posluchÃ¡Ä sám nestlaÄil 608 pauzu, prehrávaÄ sa o obnovenie pokúša sám: pri chybe alebo zaseknutà 609 znova otvorà stream (s rastúcim odstupom), po preruÅ¡enà systémom skúša 610 každých pár sekúnd, najviac 30 minút. Epizód podcastov sa to netýka. */ 611 let chcemHrat = false; /* úmysel poslucháÄa: hraÅ¥ */ 612 let pauzaOdPouzivatela = false; /* pauzu stlaÄil poslucháÄ, nie systém */ 613 let pokusov = 0; 614 let casovacObnovy = null; 615 let obnovaOd = 0; 616 const OBNOVA_MAX_MS = 30 * 60 * 1000; 617 function zrusObnovu() { if (casovacObnovy) { clearTimeout(casovacObnovy); casovacObnovy = null; } } 618 function naplanujObnovu(dovod) { 619 if (!chcemHrat || !isStation(current)) return; 620 if (!obnovaOd) obnovaOd = Date.now(); 621 if (Date.now() - obnovaOd > OBNOVA_MAX_MS) { zrusObnovu(); return; } 622 zrusObnovu(); 623 /* preruÅ¡enie systémom (hovor, vytiahnuté slúchadlá, iná aplikácia so zvukom): 624 žiadny slepý ÄasovaÄ, ten by kradol zvuk inej aplikácii alebo pustil rádio 625 z reproduktora; obnovà sa až pri návrate na stránku, po obnove siete alebo 626 tlaÄidlom HraÅ¥ v notifikácii */ 627 if (dovod === 'prerusenie') return; 628 /* chyba spojenia: 2 s, 4 s, 8 s⦠po 30 s */ 629 const odstup = Math.min(30000, 2000 * Math.pow(2, Math.min(pokusov, 4))); 630 pokusov++; 631 casovacObnovy = setTimeout(() => { casovacObnovy = null; obnovStream(dovod); }, odstup); 632 } 633 function obnovStream(dovod) { 634 if (!chcemHrat || !isStation(current)) return; 635 if (playing && !audio.paused && !audio.ended && audio.readyState >= 3 && dovod === 'zaseknutie') { pokusov = 0; return; } 636 if (dovod !== 'prerusenie') { 637 /* nové spojenie: to isté URL, load() zahodà zaseknutý buffer */ 638 audio.src = current.url; 639 audio.load(); 640 } 641 if (playing) { loading = true; paint(); } 642 audio.play().then(() => { loading = false; playing = true; obnovaOd = 0; paint(); }) 643 .catch(() => { loading = false; playing = false; paint(); naplanujObnovu(dovod); }); 644 } 645 /* po návrate na stránku (ukonÄený hovor, odomknutý telefón) sa neÄaká na ÄasovaÄ */ 646 document.addEventListener('visibilitychange', () => { 647 if (document.visibilityState === 'visible' && chcemHrat && isStation(current) && audio.paused && !pauzaOdPouzivatela) obnovStream('prerusenie'); 648 }); 649 window.addEventListener('online', () => { if (chcemHrat && isStation(current) && audio.paused && !pauzaOdPouzivatela) obnovStream('chyba'); }); 650 651 /* Media Session: ovládanie zo zamknutej obrazovky a notifikácie; systém 652 potom berie stránku ako prehrávaÄ a menej ju uspáva na pozadÃ. */ 653 function nastavMediaSession() { 654 if (!('mediaSession' in navigator)) return; 655 try { 656 const obrazok = new URL(current.img || AVATAR_ZAKLAD, location.href).href; 657 navigator.mediaSession.metadata = new MediaMetadata({ 658 title: isStation(current) ? (current.show || current.name) : current.name, 659 artist: isStation(current) ? current.name : (current.show || current.sub || ''), 660 artwork: [{ src: obrazok, sizes: '512x512' }], 661 }); 662 navigator.mediaSession.playbackState = 'playing'; 663 } catch (e) {} 664 } 665 if ('mediaSession' in navigator) { 666 try { 667 navigator.mediaSession.setActionHandler('play', () => { if (!playing) playTrack(current.id); }); 668 navigator.mediaSession.setActionHandler('pause', () => pause()); 669 navigator.mediaSession.setActionHandler('stop', () => pause()); 670 } catch (e) {} 671 audio.addEventListener('pause', () => { try { navigator.mediaSession.playbackState = 'paused'; } catch (e) {} }); 672 } 673 674 function playTrack(id) { 675 if (!id) return; /* featured karta v link móde nemá track */ 676 const t = TRACKS.find(x => x.id === id); 677 /* Neznáme id znamená rozbité tlaÄidlo â NIKDY nie prepnutie na stanicu. 678 Pôvodný fallback na STATIONS[0] spôsoboval, že pokazená karta epizódy 679 zapÃnala a vypÃnala ŽIVà STREAM (presne hlásená chyba z testovania). */ 680 if (!t) { console.warn('PrehrávaÄ: neznáma stopa', id); return; } 681 if (current.id === t.id && playing) { pause(); return; } 682 if (current.id !== t.id) { listenMs = 0; audio.src = t.url; } 683 else if (!audio.src) audio.src = t.url; 684 current = t; 685 loading = true; 686 lastTick = Date.now(); 687 chcemHrat = true; pauzaOdPouzivatela = false; pokusov = 0; obnovaOd = 0; zrusObnovu(); 688 audio.play().then(() => { loading = false; playing = true;
688 paint(); }).catch(() => { 689 loading = false; playing = false; 690 if (dockSub) dockSub.textContent = 'Prenos sa nepodarilo spustiÅ¥'; 691 paint(); 692 }); 693 playing = true; 694 paint(); 695 } 696 function pause() { pauzaOdPouzivatela = true; chcemHrat = false; zrusObnovu(); audio.pause(); playing = false; paint(); } 697 function toggle() { playing ? pause() : playTrack(current.id); } 698 699 audio.addEventListener('playing', () => { loading = false; playing = true; lastTick = Date.now(); pokusov = 0; obnovaOd = 0; zrusObnovu(); nastavMediaSession(); paint(); }); 700 audio.addEventListener('pause', () => { 701 playing = false; paint(); 702 /* pauza, ktorú nestlaÄil posluchÃ¡Ä (hovor, slúchadlá, iná aplikácia, 703 úsporný režim): stream sa obnovà sám, kým ho posluchÃ¡Ä nezastavà */ 704 if (!pauzaOdPouzivatela && !loading && chcemHrat && isStation(current)) naplanujObnovu('prerusenie'); 705 }); 706 audio.addEventListener('ended', () => { 707 playing = false; paint(); 708 /* živý stream nekonÄÃ; koniec znamená, že spojenie spadlo */ 709 if (chcemHrat && isStation(current)) naplanujObnovu('koniec'); 710 }); 711 audio.addEventListener('error', () => { if (chcemHrat && isStation(current)) naplanujObnovu('chyba'); }); 712 audio.addEventListener('stalled', () => { if (chcemHrat && isStation(current) && !audio.paused) naplanujObnovu('zaseknutie'); }); 713 /* 'waiting' prÃde, keÄ sa buffer vyprázdni (mÅtve spojenie bez zmeny siete); pri úvodnom 714 bufferovanà po play() je loading true a niÄ sa neplánuje */ 715 audio.addEventListener('waiting', () => { if (chcemHrat && isStation(current) && !audio.paused && !loading) naplanujObnovu('zaseknutie'); }); 716 /* zvuk vložený v Älánku (<audio controls>) nesmie hraÅ¥ cez stream: 717 keÄ sa spustà Äokoľvek iné než náš prehrávaÄ, stream sa zastavà */ 718 document.addEventListener('play', (e) => { 719 if (e.target !== audio && playing) pause(); 720 }, true); 721 722 dockBtn?.addEventListener('click', toggle); 723 playerPlay?.addEventListener('click', toggle); 724 725 /* Äas poÄúvania a progres */ 726 const fmtTime = ms => { 727 const s = Math.floor(ms / 1000); 728 return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); 729 }; 730 setInterval(() => { 731 if (playing) { 732 const now = Date.now(); 733 listenMs += now - lastTick; 734 lastTick = now; 735 } 736 if (!document.body.classList.contains('player-open')) return; 737 if (isStation(current)) { 738 if (playerClock) playerClock.textContent = fmtTime(listenMs); 739 } else { 740 if (playerClock) playerClock.textContent = fmtTime(audio.currentTime * 1000); 741 if (playerBar && audio.duration) playerBar.style.width = (audio.currentTime / audio.duration * 100) + '%'; 742 } 743 }, 500); 744 745 /* ââ posúvanie epizódy po liÅ¡te (živý prenos sa posúvaÅ¥ nedá) ââ */ 746 if (playerProgress) { 747 playerProgress.setAttribute('role', 'slider'); 748 playerProgress.setAttribute('tabindex', '0'); 749 playerProgress.setAttribute('aria-label', 'PozÃcia v epizóde'); 750 const seekable = () => !isStation(current) && isFinite(audio.duration) && audio.duration > 0; 751 const seekTo = (clientX) => { 752 const r = playerProgress.getBoundingClientRect(); 753 const p = Math.max(0, Math.min(1, (clientX - r.left) / r.width)); 754 audio.currentTime = p * audio.duration; 755 if (playerBar) playerBar.style.width = (p * 100) + '%'; 756 playerProgress.setAttribute('aria-valuenow', Math.round(p * 100)); 757 }; 758 let scrubbing = false; 759 playerProgress.addEventListener('pointerdown', (e) => { 760 if (!seekable()) return; 761 scrubbing = true; 762 playerProgress.classList.add('scrubbing'); 763 try { playerProgress.setPointerCapture(e.pointerId); } catch (_) {} 764 seekTo(e.clientX); 765 }); 766 playerProgress.addEventListener('pointermove', (e) => { if (scrubbing) seekTo(e.clientX); }); 767 const endScrub = (e) => { 768 if (!scrubbing) return; 769 scrubbing = false; 770 playerProgress.classList.remove('scrubbing'); 771 try { playerProgress.releasePointerCapture(e.pointerId); } catch (_) {} 772 }; 773 playerProgress.addEventListener('pointerup', endScrub); 774 playerProgress.addEventListener('pointercancel', endScrub); 775 playerProgress.addEventListener('keydown', (e) => { 776 if (!seekable()) return; 777 const step = e.shiftKey ? 60 : 15; 778 if (e.key === 'ArrowRight') { e.preventDefault(); audio.currentTime = Math.min(audio.duration, audio.currentTime + step); } 779 else if (e.key === 'ArrowLeft') { e.preventDefault(); audio.currentTime = Math.max(0, audio.currentTime - step); } 780 }); 781 } 782 783 /* ââ návrat k živému vysielaniu ââ 784 Po pustenà epizódy sa k rádiu nedalo vrátiÅ¥ inak než cez tlaÄidlo v hlaviÄke â 785 zoznam stanÃc je schovaný za ikonou. Preto vlastné tlaÄidlo v prehrávaÄi aj v docku. */ 786 const LIVE_ID = STATIONS[0].id;
787 const LIVE_NAME = STATIONS[0].name; 788 /* Samotná bodka v krúžku vyzerala ako tlaÄidlo nahrávania. Vysielacia vlna 789 plus slovo "Naživo" povie rovno, Äo sa stane po stlaÄenÃ. */ 790 const ICON_LIVE = 791 '<svg viewBox="0 0 24 24" width="15" height="15" fill="none" stroke="currentColor"' + 792 ' stroke-width="2" stroke-linecap="round" aria-hidden="true">' + 793 '<circle cx="12" cy="12" r="2.4" fill="currentColor" stroke="none"/>' + 794 '<path d="M8.1 15.9a5.5 5.5 0 0 1 0-7.8M15.9 8.1a5.5 5.5 0 0 1 0 7.8"/>' + 795 '<path d="M5.3 18.7a9.5 9.5 0 0 1 0-13.4M18.7 5.3a9.5 9.5 0 0 1 0 13.4"/>' + 796 '</svg>'; 797 function makeLiveBtn(cls, label) { 798 const b = document.createElement('button'); 799 b.type = 'button'; 800 b.className = cls; 801 b.innerHTML = ICON_LIVE + '<span class="bl-t">' + label + '</span>'; 802 b.addEventListener('click', (e) => { e.stopPropagation(); playTrack(LIVE_ID); }); 803 return b; 804 } 805 const backLive = makeLiveBtn('back-live', 'Späť na ' + LIVE_NAME + ' naživo'); 806 $('.player-controls')?.appendChild(backLive); 807 const dockLive = makeLiveBtn('dock-live', 'Naživo'); 808 dockLive.title = 'Prepnúť na živé vysielanie ' + LIVE_NAME; 809 dockLive.setAttribute('aria-label', 'Prepnúť na živé vysielanie ' + LIVE_NAME); 810 if (dock && dockBtn) dock.insertBefore(dockLive, dockBtn); 811 812 const syncLive = () => { 813 const onEpisode = !isStation(current); 814 backLive.hidden = !onEpisode; 815 dockLive.hidden = !onEpisode; 816 }; 817 syncLive(); 818 const _paint = paint; 819 paint = function () { _paint.apply(this, arguments); syncLive(); }; 820 821 /* otvorenie a zatvorenie fullscreen prehrávaÄa â GSAP "vyrastie z docku" 822 (clip-path reveal z obrysu docku do celej obrazovky), fallback CSS slide */ 823 const playerParts = () => ['.player-top', '.player-card', '.player-controls', '.player-stations'] 824 .map(sel => player && player.querySelector(sel)).filter(Boolean); 825 const dockClip = () => { 826 const d = dock ? dock.getBoundingClientRect() : null; 827 if (!d) return 'inset(78% 30% 4% 30% round 34px)'; 828 const vw = innerWidth, vh = innerHeight; 829 return `inset(${(d.top / vh * 100).toFixed(1)}% ${((vw - d.right) / vw * 100).toFixed(1)}% ${((vh - d.bottom) / vh * 100).toFixed(1)}% ${(d.left / vw * 100).toFixed(1)}% round 34px)`; 830 }; 831 let playerTl = null; 832 const playerOpen = () => { 833 document.body.classList.add('player-open');
834 paint(); 835 if (window.gsap && !reduced && player) { 836 if (playerTl) playerTl.kill(); 837 player.classList.add('player--anim'); 838 playerTl = gsap.timeline({ 839 onComplete() { 840 player.classList.remove('player--anim'); 841 gsap.set(player, { clearProps: 'clipPath,visibility,opacity' }); 842 }, 843 }) 844 .fromTo(player, 845 { clipPath: dockClip(), visibility: 'visible', autoAlpha: 1 }, 846 { clipPath: 'inset(0% 0% 0% 0% round 0px)', duration: .62, ease: 'power4.inOut' }) 847 .fromTo(playerParts(), 848 { y: 26, autoAlpha: 0 }, 849 { y: 0, autoAlpha: 1, duration: .5, ease: 'power3.out', stagger: .07, clearProps: 'transform,opacity,visibility' }, 850 '-=0.22'); 851 } 852 }; 853 const playerClose = () => { 854 if (window.gsap && !reduced && player && document.body.classList.contains('player-open')) { 855 if (playerTl) playerTl.kill(); 856 player.classList.add('player--anim'); 857 playerTl = gsap.timeline({ 858 onComplete() { 859 document.body.classList.remove('player-open'); 860 player.classList.remove('player--anim'); 861 gsap.set(player, { clearProps: 'clipPath,visibility,opacity' }); 862 }, 863 }) 864 .to(player, { clipPath: dockClip(), duration: .4, ease: 'power3.in' }); 865 } else { 866 document.body.classList.remove('player-open'); 867 } 868 }; 869 $('.dock-expand')?.addEventListener('click', playerOpen); 870 document.querySelectorAll('.js-open-player').forEach(el => el.addEventListener('click', playerOpen)); 871 $('.dock-meta')?.addEventListener('click', playerOpen); 872 $('.dock-art')?.addEventListener('click', playerOpen); 873 $('.player-close')?.addEventListener('click', playerClose); 874 addEventListener('keydown', e => { 875 if (e.key === 'Escape') { playerClose(); menuClose(); } 876 }); 877 878 /* staniÄné chipy v prehrávaÄi */ 879 if (playerStations) { 880 STATIONS.forEach(s => { 881 const b = document.createElement('button'); 882 b.dataset.track = s.id; 883 b.textContent = s.name; 884 b.addEventListener('click', () => playTrack(s.id)); 885 playerStations.appendChild(b); 886 }); 887 } 888 stationsToggle?.addEventListener('click', () => { 889 const open = playerStations.classList.toggle('open'); 890 stationsToggle.setAttribute('aria-expanded', String(open)); 891 }); 892 893 /* vÅ¡etky prvky s data-track spúšťajú prehrávanie */ 894 document.addEventListener('click', e => { 895 const el = e.target.closest('[data-track]'); 896 if (!el || el.closest('.player-stations')) return; 897 e.preventDefault(); adopt(el); playTrack(el.dataset.track); 898 }); 899 $$('[data-play-live]').forEach(el => el.addEventListener('click', e => { e.preventDefault(); playTrack('fun'); })); 900 901 /* Karty epizód na úvodnej stránke: statické HTML nieslo zástupné id 902 (pod1, pod2, pod3), ktoré prehrávaÄ nepozná â klik potom nerobil niÄ, 903 prÃpadne prepÃnal živý stream. Tu sa každá takáto karta napojà na 904 skutoÄnú epizódu: najprv podľa titulku, inak na najnovÅ¡iu voľnú. */ 905 (() => { 906 const karty = document.querySelectorAll('.pod-ep .pe-play'); 907 if (!karty.length || !EPISODES.length) return; 908 const norm = (t) => String(t || '').toLowerCase().replace(/[â¦\.]+$/, '').replace(/\s+/g, ' ').trim().slice(0, 30); 909 const pouzite = new Set(); 910 karty.forEach((btn) => { 911 const id = btn.dataset.track; 912 if (id && TRACKS.some((t) => t.id === id)) { pouzite.add(id); return; } 913 const titulok = norm(btn.closest('.pod-ep')?.querySelector('.pe-title')?.textContent); 914 const ep = titulok ? EPISODES.find((e) => norm(e.name).startsWith(titulok) || titulok.startsWith(norm(e.name))) : null; 915 /* Bez istej zhody kartu radÅ¡ej nechaj tak. Pôvodný fallback bral epizódy 916 v globálnom poradà podľa dátumu a karte inej relácie podsunul najnovÅ¡iu 917 Adelu a Sajfu (presne nahlásená chyba: FUNúšik hral Lýdiu Eckhardt). */ 918 if (!ep || pouzite.has(ep.id)) return; 919 pouzite.add(ep.id); 920 btn.dataset.track = ep.id; 921 }); 922 })(); 923 924 /* ---------- živá pesniÄka zo Sepie (plnà v2/live.js) ---------- 925 text nahradà názov v hlaviÄke prehrávaÄa, cover (keÄ existuje) 926 generický vizuál; null vÅ¡etko vráti na základ. Cover sa najprv 927 potichu prednaÄÃta, aby nikdy nebliklo rozbité <img>. */ 928 /* platà pre každú stanicu (hlavné vysielanie aj internetové kanály), 929 preto základný text aj žiadaný cover držà každá zvlášť */ 930 const zakladShow = {}; 931 const coverZiadany = {}; 932 STATIONS.forEach(s => { zakladShow[s.id] = s.show; coverZiadany[s.id] = ''; }); 933 window.__zivaSkladba = (id, text, cover) => { 934 const s = STATIONS.find(x => x.id === id); 935 if (!s) return; 936 let zmena = false;
937 const novyShow = text || zakladShow[id]; 938 if (s.show !== novyShow) { s.show = novyShow; zmena = true; } 939 const chcem = (text && cover) ? cover : ''; 940 if (chcem !== coverZiadany[id]) { 941 coverZiadany[id] = chcem; 942 /* cover starej pesniÄky dole HNEÄ (orb sa ukáže), nový nabehne po 943 onload â inak by pri pomalom preloade svietil obrázok starej */ 944 if (s.img) { s.img = null; zmena = true; } 945 if (chcem) { 946 const im = new Image(); 947 im.onload = () => { 948 if (coverZiadany[id] === chcem && s.img !== chcem) { s.img = chcem; if (current === s) paint(); } 949 }; 950 im.onerror = () => { 951 /* reset, aby Äalšà 30 s poll preload zopakoval â JPG môže na 952 server doraziÅ¥ o pár sekúnd neskôr než push */ 953 if (coverZiadany[id] === chcem) coverZiadany[id] = ''; 954 }; 955 im.src = chcem; 956 } 957 } 958 if (zmena && current === s) { paint(); if (playing) nastavMediaSession(); } 959 }; 960 961 paint(); 962})(); 963 964/* ============================================================ 965 PODCASTY · filter cez avatarov + prepÃnanie featured karty 966 ============================================================ */ 967(() => { 968 const chips = document.querySelectorAll('.pf-chip'); 969 const eps = document.querySelectorAll('.pod-ep'); 970 const feat = document.querySelector('.pod-feat'); 971 if (!chips.length || !feat) return; 972 973 const featImg = feat.querySelector('.pf-img'); 974 const featShow = feat.querySelector('.pf-chip-show'); 975 const featHost = feat.querySelector('.pf-host'); 976 const featTitle = feat.querySelector('.pf-title'); 977 const featPlay = feat.querySelector('.pf-play'); 978 979 /* featured obsah pre každú reláciu (all = Adela a Sajfa ep. 1) */ 980 const FEAT = { 981 "all": { img: "/assets/img/show-adela-a-sajfa.webp", pos: 'center', show: "Adela a Sajfa", host: "hereÄka a speváÄka Nela Pocisková · Adela a Sajfa", title: "NauÄila som sa, že život sa nemusà prežiÅ¥ v tlaku, ale dá saâ¦", track: "ep1667" }, 982 "adela-a-sajfa": { img: "/assets/img/show-adela-a-sajfa.webp", pos: 'center', show: "Adela a Sajfa", host: "hereÄka a speváÄka Nela Pocisková · Adela a Sajfa", title: "NauÄila som sa, že život sa nemusà prežiÅ¥ v tlaku, ale dá saâ¦", track: "ep1667" }, 983 "sajfa-sou": { img: "/assets/img/show-sajfa-sou.webp", pos: 'center', show: "Sajfa Å¡ou", host: "Sajfa Å¡ou", title: "ÄUÄOVI RODIÄIA VYSLALI SIGNÃL feat. Hex", track: "ep1585" }, 984 "talkshow-so-sarkanom": { img: "/assets/img/show-nedelna-talkshow.webp", pos: 'center', show: "Talkshow so Å arkanom", host: "Talkshow so Å arkanom", title: "Môj prvý bikepark? Lopata a igelit na teniskách â staviteľ trâ¦", track: "ep1663" }, 985 "funusik": { img: "/assets/img/pod-funusik.png", pos: 'center', show: "FUNúšik", host: "FUNúšik", title: "175 NÃRI PREPISJà HISTÃRIU, NEYMAR SA LÃÄIL S REPRE", track: "ep1668" }, 986 "podla-mna": { img: "/assets/img/pod-podla-mna.png", pos: 'center', show: "PODĽA MÅA", host: "PODĽA MÅA", title: "festival season", track: "ep1666" }, 987 "ranna-sou-s-juniorom-a-marcelom": { img: "/assets/img/show-ranna-sou.webp", pos: 'center', show: "Ranná Å¡ou s Juniorom a Marcelom", host: "Ranná Å¡ou s Juniorom a Marcelom", title: "KERKA NA NEPODSTATNOM MIESTE. V SOBRANCIACH?", track: "ep1534" }, 988 "fun-in-slovakia": { img: "/assets/img/pod-fun-in-slovakia.jpg", pos: 'center', show: "Fun in Slovakia", host: "MIRO JAROŠ· Fun in Slovakia", title: "UrobiÅ¥ poctu Erikovi a Barbare bol môj sen, vÄaka nim som oveâ¦", track: "ep1536" }, 989 "level-lama": { img: "/assets/img/pod-level-lama.jpg", pos: 'center', show: "Level Lama", host: "Level Lama", title: "Strýco Filip v #lvllama: âTýždeÅ som nejedol mäso. Bolo to stâ¦", track: "ep688" }, 990 "slovaci-su-prima": { img: "/assets/img/funradio-logo.svg", pos: 'center', show: "Slováci sú prima", host: "Å kolský projekt ju priviedol k Instagramu, kde spravuje najväÄšà úÄet o Bratislave · Slováci sú prima", title: "MICHAELA TLAPOVÃ", track: "ep1078" }, 991 "vsetko-co-mam-rad-hex35": { img: "/assets/img/funradio-logo.svg", pos: 'center', show: "VÅ ETKO, ÄO MÃM RÃD #HEX35", host: "VÅ ETKO, ÄO MÃM RÃD #HEX35", title: "4 \"KeÄ som poÄul KeÄ sme sami, neveril som, že ste to vy,\" feâ¦", track: "ep1398" }, 992 }; 993 994 function setFeat(key) { 995 const f = FEAT[key] || FEAT.all; 996 featImg.src = f.img; 997 featImg.style.objectPosition = f.pos; 998 if (featShow) featShow.textContent = f.show; 999 if (featHost) featHost.textContent = f.host; 1000 if (featTitle) featTitle.textContent = f.title; 1001 if (featPlay) { 1002 if (f.track) { 1003 featPlay.dataset.track = f.track; 1004 featPlay.removeAttribute('href'); 1005 featPlay.querySelector('.pe-ic').nextSibling.textContent = ''; 1006 featPlay.lastChild.textContent = 'PustiÅ¥ epizódu'; 1007 } else { 1008 delete featPlay.dataset.track; 1009 featPlay.lastChild.textContent = 'VypoÄuÅ¥ na webe'; 1010 } 1011 } 1012 } 1013 1014 chips.forEach(chip => chip.addEventListener('click', () => {
1015 const key = chip.dataset.filter; 1016 chips.forEach(c => { 1017 c.classList.toggle('is-active', c === chip); 1018 c.setAttribute('aria-selected', c === chip ? 'true' : 'false'); 1019 }); 1020 eps.forEach(ep => { ep.hidden = key !== 'all' && ep.dataset.show !== key; }); 1021 setFeat(key); 1022 })); 1023 1024 /* klik na featured play: ak nemá track, otvor odkaz */ 1025 if (featPlay) featPlay.addEventListener('click', () => { 1026 if (!featPlay.dataset.track) { 1027 const f = FEAT[document.querySelector('.pf-chip.is-active')?.dataset.filter || 'all']; 1028 if (f && f.href) window.open(f.href, '_blank', 'noopener'); 1029 } 1030 }); 1031})();
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.