PageSourceSearch

https://www.funradio.sk/assets/js/softnav.js?v=8

js funradio.sk collected 2026-10-02 05:31:58 UTC 8,085 bytes, 178 lines download raw bytes

1/* Fun Rádio — seamless navigation while a podcast is playing.
2   A normal page load tears down the <audio> element, which is audible as a gap.
3   While the mini-player has audio loaded we therefore swap only <main> via fetch +
4   History API, so the audio element (which lives on <body>) keeps playing untouched.
5   When nothing is playing we stay out of the way completely: full page loads, so the
6   GSAP entry animations behave exactly as designed. */
7(function () {
8  'use strict'
9  var KEY = 'fr_pod_state'
10  var busy = false
11
12  /* The site player lives inside v3/app.js (a closure, not a DOM node), so ask it directly.
13     `#frPodAudio` was an earlier, separate player that no longer exists — checking for it
14     meant this never activated and every click tore the audio down. */
15  function podcastActive() {
16    var a = window.__frAudio
17    if (a && a.src && !a.paused) return true
18    var dock = document.querySelector('.dock')
19    if (dock && dock.classList.contains('playing')) return true
20    try { var s = JSON.parse(sessionStorage.getItem(KEY) || 'null'); return !!(s && s.src && s.playing) } catch (e) { return false }
21  }
22
23  function sameOrigin(href) {
24    try { var u = new URL(href, location.href); return u.origin === location.origin } catch (e) { return false }
25  }
26
27  function killMotion() {
28    try {
29      if (window.ScrollTrigger && window.ScrollTrigger.getAll) window.ScrollTrigger.getAll().forEach(function (t) { try { t.kill() } catch (e) {} })
30      if (window.gsap && window.gsap.globalTimeline) window.gsap.globalTimeline.clear()
31    } catch (e) {}
32  }
33
34  // Collect page-specific inline scripts BEFORE the new <main> is moved into the live
35  // document. replaceWith() adopts the node, so anything inside <main> — including the
36  // schedule and filter scripts — disappears from the parsed doc and would be missed.
37  function collectScripts(doc) {
38    return [].slice.call(doc.querySelectorAll('body script:not([src])'))
39      // Stránky z redakčného systému nesú vnútorné skripty Next.js
40      // (self.__next_f). Ich opätovné spustenie v už bežiacom dokumente
41      // mieša dva rôzne behy aplikácie — preskakujú sa.
42      .filter(function (s) { return !/self\.__next_f|__NEXT_DATA__/.test(s.textContent || '') })
43      .map(function (s) { return s.textContent })
44      .filter(function (t) { return t && !/Reveal priming/.test(t) })
45  }
46
47  // re-execute the scripts the new page needs (page-specific inline logic + motion module)
48  function rerunScripts(sources) {
49    sources.forEach(function (text) {
50      var n = document.createElement('script')
51      n.textContent = text
52      document.body.appendChild(n)
53      n.remove()
54    })
55    // motion module re-registers reveals/parallax for the freshly injected DOM
56    var m = document.createElement('script')
57    m.src = '/v3/motion.js?soft=' + Date.now()
58    document.body.appendChild(m)
59  }
60
61  /* Stránky si nesú vlastné štýly v <head> (napr. /reklama/), ktoré výmena
62     samotného <main> neprinesie — stránka by ostala rozbitá až do plného
63     načítania. Chýbajúce štýly a odkazované CSS sa preto preberajú z hlavičky
64     novej stránky; už prítomné sa nezdvojujú. */
65  function adoptHeadStyles(doc, url) {
66    var mame = [].slice.call(document.querySelectorAll('link[rel="stylesheet"]'))
67      .map(function (l) { return l.href })
68    ;[].slice.call(doc.querySelectorAll('head link[rel="stylesheet"]')).forEach(function (l) {
69      try {
70        var abs = new URL(l.getAttribute('href'), location.origin + url).href
71        if (mame.indexOf(abs) !== -1) return
72        var n = document.createElement('link')
73        n.rel = 'stylesheet'
74        n.href = abs
75        document.head.appendChild(n)
76      } catch (e) {}
77    })
78    var texty = [].slice.call(document.querySelectorAll('style'))
79      .map(function (s) { return s.textContent })
80    ;[].slice.call(doc.querySelectorAll('head style')).forEach(function (s) {
81      if (texty.indexOf(s.textContent) !== -1) return
82      var n = document.createElement('style')
83      n.textContent = s.textContent
84      document.head.appendChild(n)
85    })
86  }
87
88  function swap(html, url, push) {
89    var doc = new DOMParser().parseFromString(html, 'text/html')
90    var next = doc.querySelector('main')
91    var cur = document.querySelector('main')
92    if (!next || !cur) { location.href = location.origin + url; return }
93    var scripts = collectScripts(doc)      // musí byť pred replaceWith
94
95    killMotion()
96    adoptHeadStyles(doc, url)
97    cur.replaceWith(next)
98    document.title = doc.title
99    // keep content visible even if motion.js fails to re-run
100    document.documentElement.classList.remove('reveal-primed')
101    if (push) history.pushState({ soft: true }, '', url)
102    /* html má scroll-behavior: smooth — obyčajné scrollTo by cestu na vrch
103       animovalo a dohrávané obrázky či motion animáciu prerušia v strede;
104       čitateľ by ostal skrolnutý dole. Skok musí byť okamžitý a po
105       prekreslení sa poistí ešte raz. */
106    var de = document.documentElement
107    var povodne = de.style.scrollBehavior
108    de.style.scrollBehavior = 'auto'
109    window.scrollTo(0, 0)
110    rerunScripts(scripts)
111    requestAnimationFrame(function () {
112      window.scrollTo(0, 0)
113      de.style.scrollBehavior = povodne
114    })
115    document.dispatchEvent(new CustomEvent('fr:navigated', { detail: { url: url } }))
116  }
117
118  function go(url, push) {
119    if (busy) return
120    busy = true
121    /* Keď je stránka otvorená s prihlasovacími údajmi v adrese (heslo@domén
121a),
122       relatívna adresa ich zdedí a prehliadač potom odmietne fetch() AJ tiché
123       presmerovanie cez location.href — klik skončí v ničom. location.origin
124       prihlasovacie údaje nenesie, preto sa všetko skladá z neho. */
125    var cista = location.origin + url
126    try {
127      fetch(cista, { credentials: 'same-origin' })
128        .then(function (r) { if (!r.ok) throw new Error(r.status); return r.text() })
129        .then(function (h) { swap(h, url, push) })
130        .catch(function () { location.href = cista })
131        .finally(function () { busy = false })
132    } catch (e) {
133      busy = false
134      location.href = cista
135    }
136  }
137
138  document.addEventListener('click', function (ev) {
139    if (!podcastActive()) return                       // not listening → normal navigation
140    if (ev.defaultPrevented || ev.button !== 0) return
141    if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.altKey) return
142    var a = ev.target.closest('a[href]')
143    if (!a || a.target === '_blank' || a.hasAttribute('download')) return
144    var href = a.getAttribute('href')
145    if (!href || href.startsWith('#') || href.startsWith('mailto:') || href.startsWith('tel:')) return
146    if (!sameOrigin(href)) return
147    var url = new URL(href, location.href)
148    if (url.pathname === location.pathname && url.hash) return   // in-page anchor
149    ev.preventDefault()
150    go(url.pathname + url.search, true)
151  })
152
153  window.addEventListener('popstate', function (e) {
154    if (e.state && e.state.soft && podcastActive()) go(location.pathname + location.search, false)
155  })
156
157  // pre obsluhu rozbaľovacích položiek menu nižšie — tá žije mimo tohto uzáveru
158  window.__frSoftGo = function (url) {
159    if (!podcastActive()) return false
160    go(url, true)
161    return true
162  }
163})()
164
165/* The "Vysielanie" nav item is a dropdown button, so it could not be opened as a page.
166   Clicking its label now navigates; the caret still just opens the menu. */
167;(function () {
168  document.addEventListener('click', function (ev) {
169    var b = ev.target.closest('.nav-drop-btn[data-nav-href]')
170    if (!b) return
171    if (ev.target.closest('svg')) return              // caret → keep the dropdown behaviour
172    ev.preventDefault()
173    // Počas prehrávania ide aj štítok rozbaľovacej položky mäkkým prechodom —
174    // plné načítanie by prerušilo zvuk (Vlna má takto Podcasty aj Vysielanie).
175    if (window.__frSoftGo && window.__frSoftGo(b.dataset.navHref)) return
176    location.href = b.dataset.navHref
177  })
178})()

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.