PageSourceSearch

https://radiocidadeluz.com.br/assets/js/router.js?v=tjha6c

js radiocidadeluz.com.br collected 2026-09-24 14:34:55 UTC 12,518 bytes, 382 lines download raw bytes

1/*
2 *
3 * Only the target container is ever touched, so anything outside it keeps its
4 * DOM node. That is what stops the audio player from restarting on Safari:
5 * the <audio> element is never re-parented and its script never runs twice.
6 */
7(function (window, document) {
8  "use strict";
9
10  var SKIP_CLASSES = ["ext-link", "no-ajaxy", "no-ajax"];
11  var SKIP_PROTOCOLS = ["mailto:", "tel:", "javascript:", "sms:", "whatsapp:"];
12
13  var SKELETON =
14    '<div class="router-skeleton wrap">' +
15    '<div class="router-skeleton__bar skeleton"></div>' +
16    '<div class="router-skeleton__grid">' +
17    '<div class="router-skeleton__block skeleton"></div>' +
18    '<div class="router-skeleton__block skeleton"></div>' +
19    '<div class="router-skeleton__block skeleton"></div>' +
20    '</div></div>';
21
22  function ReqBras(options) {
23    // Single instance: this file self-starts early, and utils.js constructs it
24    // again later. Two instances would mean two click handlers.
25    if (ReqBras.instance) return ReqBras.instance;
26
27    var settings = options || {};
28
29    this.container = settings.elements || "#pContent";
30    this.scrolltop = settings.scrolltop !== false;
31    this.timeout = settings.timeout || 15000;
32    this.skeletonDelay = settings.skeletonDelay || 180;
33    // A theme opts out with data-skeleton="off" on <html>
34    this.skeleton = document.documentElement.dataset.skeleton !== "off";
35    this.prefetch = settings.prefetchoff !== true;
36
37    this.loadedScripts = new Set();
38    this.prefetched = new Set();
39    this.pageTimers = [];
40    this.cache = new Map();
41    this.scrollPositions = {};
42    this.controller = null;
43    this.skeletonTimer = null;
44    this.booted = false;
45
46    this.boot();
47  }
48
49  // Events keep the old names so existing listeners keep working
50  ReqBras.prototype.emit = function (name, detail) {
51    [window, document].forEach(function (node) {
52      var event = document.createEvent("HTMLEvents");
53      event.initEvent("pronto." + name, false, false);
54      event.data = detail || {};
55      node.dispatchEvent(event);
56    });
57  };
58
59  ReqBras.prototype.target = function () {
60    return document.querySelector(this.container);
61  };
62
63  ReqBras.prototype.boot = function () {
64    if (this.booted) return;
65    this.booted = true;
66
67    var self = this;
68
69    ReqBras.instance = this;
70
71    // Everything already on the page counts as loaded, so it never runs twice.
72    // Runs again on DOMContentLoaded because this file starts before the rest
73    // of the head has been parsed.
74    this.indexScripts();
75    document.addEventListener("DOMContentLoaded", function () { self.indexScripts(); });
76
77    if ("scrollRestoration" in window.history) {
78      window.history.scrollRestoration = "manual";
79    }
80
81    document.addEventListener("click", function (e) { self.onClick(e); }, false);
82    window.addEventListener("popstate", function (e) { self.onPopState(e); }, false);
83
84    if (this.prefetch) {
85      document.addEventListener("mouseover", function (e) { self.onHover(e); }, { passive: true });
86      document.addEventListener("touchstart", function (e) { self.onHover(e); }, { passive: true });
87    }
88
89    if (window.history.replaceState) {
90      window.history.replaceState({ reqbras: true, key: this.key() }, document.title, window.location.href);
91    }
92
93    this.emit("load");
94  };
95
96  ReqBras.prototype.key = function () {
97    return window.location.pathname + window.location.search;
98  };
99
100  ReqBras.prototype.indexScripts = function () {
101    var self = this;
102    document.querySelectorAll("script[src]").forEach(function (script) {
103      self.loadedScripts.add(script.src);
104    });
105  };
106
107  // Security: only same-origin http(s) URLs are ever fetched or injected
108  ReqBras.prototype.isInternal = function (url) {
109    try {
110      var parsed = new URL(url, window.location.href);
111      if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return false;
112      return parsed.origin === window.location.origin;
113    } catch (err) {
114      return false;
115    }
116  };
117
118  ReqBras.prototype.linkFrom = function (node) {
119    return node && node.closest ? node.closest("a[href]") : null;
120  };
121
122  // Returns the URL this link should navigate to, or null when it must be left alone
123  ReqBras.prototype.routable = function (link) {
124    if (!link) return null;
125
126    var href = link.getAttribute("href");
127    if (!href || href.charAt(0) === "#") return null;
128
129    var lower = href.toLowerCase();
130    if (SKIP_PROTOCOLS.some(function (p) { return lower.indexOf(p) === 0; })) return null;
131    if (link.hasAttribute("download") || link.hasAttribute("data-no-ajax")) return null;
132    if (SKIP_CLASSES.some(function (c) { return link.classList.contains(c); })) return null;
133    if (link.target && link.target !== "_self") return null;
134    if (!this.isInternal(href)) return null;
135
136    return new URL(href, window.location.href).href;
137  };
138
139  ReqBras.prototype.onHover = function (e) {
140    var link = this.linkFrom(e.target);
141    var url = this.routable(link);
142
143    if (!url || url === window.location.href || this.cache.has(url)) return;
144
145    // One try per address: a link that fails must not be fetched on every hover
146    if (this.prefetched.has(url)) return;
147    this.prefetched.add(url);
148
149    var self = this;
150
151    fetch(url, { credentials: "same-origin", headers: { "X-Requested-With": "XMLHttpRequest" } })
152      .then(function (response) {
153        if (!response.ok) return;
154        var type = response.headers.get("content-type") || "";
155        if (type.indexOf("text/html") === -1) return;
156        return response.text();
157      })
158      .then(function (html) {
159        if (!html) return;
160
161        // Small cache: enough to make the click feel instant, not to hold the site
162        if (self.cache.size > 8) self.cache.clear();
163        self.cache.set(url, html);
164      })
165      .catch(function () {});
166  };
167
168  ReqBras.prototype.onClick = function (e) {
169    if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
170
171    var link = this.linkFrom(e.target);
172    if (!link) return;
173
174    var href = link.getAttribute("href");
175    if (!href) return;
176
177    // Opt-out classes and other hosts open in a new tab
178    var flagged = SKIP_CLASSES.some(function (c) { return link.classList.contains(c); });
179    var external = !this.isInternal(href);
180    var special = SKIP_PROTOCOLS.some(function (p) { return href.toLowerCase().indexOf(p) === 0; });
181
182    if ((flagged || external) && !special && href.charAt(0) !== "#") {
183      if (!link.target && (external || link.classList.contains("ext-link"))) {
184        link.target = "_blank";
185        link.rel = "noopener noreferrer";
186      }
187      return;
188    }
189
190    var url = this.routable(link);
191    if (!url || !this.target()) return;
192
193    e.preventDefault();
194
195    if (url === window.location.href) return;
196
197    this.scrollPositions[this.key()] = window.pageYOffset;
198    this.go(url, true);
199  };
200
201  ReqBras.prototype.onPopState = function (e) {
202    if (!e.state || !e.state.reqbras) return;
203    this.go(window.location.href, false, e.state.key);
204  };
205
206  ReqBras.prototype.go = function (url, push, restoreKey) {
207    var self = this;
208
209    if (this.controller) this.controller.abort();
210
211    this.emit("request", { url: url });
212
213    var cached = this.cache.get(url);
214    if (cached) {
215      this.cache.delete(url);
216      this.render(cached, url, push, restoreKey);
217      return;
218    }
219
220    this.showSkeleton();
221
222    var controller = new AbortController();
223    var timer = window.setTimeout(function () { controller.abort(); }, this.timeout);
224    this.controller = controller;
225
226    fetch(url, {
227      credentials: "same-origin",
228      redirect: "follow",
229      headers: { "X-Requested-With": "XMLHttpRequest" },
230      signal: controller.signal
231    })
232      .then(function (response) {
233        if (!response.ok) throw new Error("HTTP " + response.status);
234
235        var type = response.headers.get("content-type") || "";
236        if (type.indexOf("text/html") === -1) throw new Error("Not HTML");
237
238        // A redirect to another origin must never be injected into the page
239        if (!self.isInternal(response.url)) throw new Error("Cross-origin response");
240
241        return response.text();
242      })
243      .then(function (html) {
244        window.clearTimeout(timer);
245        self.controller = null;
246        self.render(html, url, push, restoreKey);
247      })
248      .catch(function (err) {
249        window.clearTimeout(timer);
250        self.controller = null;
251        self.hideSkeleton();
252
253        if (err && err.name === "AbortError") return;
254
255        self.emit("error", { url: url, error: err });
256        window.location.href = url;
257      });
258  };
259
260  // Only shows up when the response is actually slow, so fast clicks do not flash
261  ReqBras.prototype.showSkeleton = function () {
262    var self = this;
263
264    this.hideSkeleton();
265
266    if (!this.skeleton) return;
267
268    this.skeletonTimer = window.setTimeout(function () {
269      var target = self.target();
270      if (target) target.innerHTML = SKELETON;
271    }, this.skeletonDelay);
272  };
273
274  ReqBras.prototype.hideSkeleton = function () {
275    if (!this.skeletonTimer) return;
276    window.clearTimeout(this.skeletonTimer);
277    this.skeletonTimer = null;
278  };
279
280  ReqBras.prototype.render = function (html, url, push, restoreKey) {
281    var target = this.target();
282    if (!target) { window.location.href = url; return; }
283
284    // DOMParser never executes anything while parsing
285    var doc = new DOMParser().parseFromString(html, "text/html");
286    var fresh = doc.querySelector(this.container);
287
288    if (!fresh) { window.location.href = url; return; }
289
290    this.hideSkeleton();
291    this.clearTimers();
292
293    var title = doc.querySelector("title");
294    if (title) document.title = title.textContent;
295
296    // A page with unbalanced markup makes the parser nest the footer, header or
297    // player inside the container. Dropping them here keeps those shells alive.
298    fresh.querySelectorAll("footer.foot, #pHeader, .bras-player, .bras-player-spacer").forEach(function (stray) {
299      stray.remove();
300    });
301
302    // Page CSS hangs on the class of the container, so it travels with the HTML
303    target.className = fresh.className;
304    target.innerHTML = fresh.innerHTML;
305    this.runScripts(target);
306
307    if (push && window.history.pushState) {
308      window.history.pushState({ reqbras: true, key: this.key() }, document.title, url);
309    }
310
311    var restore = restoreKey ? this.scrollPositions[restoreKey] : null;
312
313    if (restore != null) {
314      window.scrollTo(0, restore);
315    } else if (this.scrolltop) {
316      window.scrollTo(0, 0);
317    }
318
319    this.emit("render", { url: url });
320  };
321
322  // Intervals started by page scripts are dropped on the next navigation
323  ReqBras.prototype.clearTimers = function () {
324    this.pageTimers.forEach(function (id) { window.clearInterval(id); });
325    this.pageTimers = [];
326  };
327
328  ReqBras.prototype.runScripts = function (container) {
329    var self = this;
330    var scripts = Array.prototype.slice.call(container.querySelectorAll("script"));
331
332    var nativeInterval = window.setInterval;
333    var nativeTimeout = window.setTimeout;
334
335    window.setInterval = function () {
336      var id = nativeInterval.apply(window, arguments);
337      self.pageTimers.push(id);
338      return id;
339    };
340
341    window.setTimeout = function () {
342      var id = nativeTimeout.apply(window, arguments);
343      self.pageTimers.push(id);
344      return id;
345    };
346
347    try {
348      scripts.forEach(function (script) { self.runScript(script); });
349    } finally {
350      window.setInterval = nativeInterval;
351      window.setTimeout = nativeTimeout;
352    }
353  };
354
355  ReqBras.prototype.runScript = function (script) {
356    var src = script.getAttribute("src");
357
358    if (src) {
359      var absolute = new URL(src, window.location.href).href;
360
361      // Never load the same file twice: that is what duplicated the player handlers
362      if (this.loadedScripts.has(absolute)) { script.remove(); return; }
363      this.loadedScripts.add(absolute);
364    }
365
366    var fresh = document.createElement("script");
367
368    Array.prototype.forEach.call(script.attributes, function (attr) {
369      fresh.setAttribute(attr.name, attr.value);
370    });
371
372    if (!src) fresh.textContent = script.textContent;
373
374    script.parentNode.replaceChild(fresh, script);
375  };
376
377  window.ReqBras = ReqBras;
378
379  // Starts as soon as this file runs, before the heavy libraries in the head.
380  // A slow page still gets working navigation from the first paint.
381  new ReqBras({ elements: "#pContent", scrolltop: true });
382})(window, document);

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.