1// Theme strings when the theme provides them, Portuguese defaults otherwise 2function T(key){ 3 var fallback = { 'with': 'com', no_program: 'Nenhum programa disponÃvel no momento', online_now: 'Online agora', no_host: 'Nenhum locutor está online neste momento' }; 4 return (window.BRAS_T && window.BRAS_T[key]) || fallback[key] || ''; 5} 6 7let ajaxify = new ReqBras({ elements: '#pContent', selector: 'a:not(.ext-link)', scrolltop: true, forms: false, prefetchoff: true, memoryoff: true, inlineskip: '' }); 8window.addEventListener('pronto.request', () => { 9 NProgress.start(); 10}); 11window.addEventListener('pronto.render', () => { 12 renderGoogleReCaptcha(); 13 renderMap(); 14 NProgress.done(); 15 16 const currentURL = window.location.pathname; 17 const pathParts = currentURL.split('/').filter(Boolean); 18 const pageName = pathParts[pathParts.length - 1] || 'home'; 19 20 21 22const sliderModule = document.querySelector('.modules .module-21'); 23 24if (sliderModule) { 25 if (pageName !== 'home') { 26 sliderModule.classList.add('hideSlider'); 27 } else { 28 sliderModule.classList.remove('hideSlider'); 29 } 30} 31 32 33}); 34window.addEventListener('pronto.error', () => { 35 NProgress.done(true); 36}); 37 38 39document.addEventListener('pronto.load', function () { 40 if (typeof renderMap === 'function') { 41 renderMap(); 42 } 43}); 44 45// The router fires pronto.load while booting, before this file finishes: on a 46// direct hit the map only draws from here 47document.addEventListener('DOMContentLoaded', function () { 48 if (typeof renderMap === 'function') { 49 renderMap(); 50 } 51}); 52 53var MAP_CONTAINERS = {}; 54var GRECAPTCHA_CONTAINERS = GRECAPTCHA_CONTAINERS || {}; 55 56function renderGoogleReCaptcha() { 57 // Verifica se o grecaptcha está disponÃvel 58 if (typeof grecaptcha !== 'undefined' && typeof grecaptcha.render !== 'undefined') { 59 var recaptchaContainers = document.querySelectorAll('.g-recaptcha-container:not(.recaptcha-initialized)'); 60 61 if (recaptchaContainers.length > 0) { 62 recaptchaContainers.forEach(function(container) { 63 // Obtém o ID único do captcha do atributo data-captcha-id 64 var captchaId = container.dataset.captchaId; 65 66 // Marcar o container como inicializado para evitar renderizações duplicadas 67 container.classList.add('recaptcha-initialized'); 68 69 // Renderiza o reCAPTCHA no container 70 var renderedCaptchaId = grecaptcha.render(container, { 71 sitekey: G_RECAPTCHA_KEY, // Chave do reCAPTCHA 72 'expired-callback': function() { 73 // Reseta o reCAPTCHA quando ele expirar 74 if (typeof GRECAPTCHA_CONTAINERS[captchaId] !== 'undefined') { 75 grecaptcha.reset(GRECAPTCHA_CONTAINERS[captchaId]); 76 } 77 }, 78 'callback': function(response) { 79 // Pode adicionar lógica aqui para o callback 80 console.log('reCAPTCHA respondido para o captcha ID:', captchaId); 81 } 82 }); 83 84 // Armazenar a instância do reCAPTCHA usando o data-captcha-id como chave 85 GRECAPTCHA_CONTAINERS[captchaId] = renderedCaptchaId; 86 }); 87 } 88 } 89} 90 91function contentCheck(limitField,limit){ 92 if (limitField.value.length > (limit ? limit : 250)) { 93 limitField.value = limitField.value.substring(0, (limit ? limit : 250)); 94 } 95} 96 97function renderMap() { 98 var mapsContainers = document.querySelectorAll('.map-container'); 99 100 if (mapsContainers.length > 0) { 101 mapsContainers.forEach(function (mapContainer, mapIndex) { 102 // Two entry points call this: a second run would stack maps 103 if (mapContainer.dataset.mapReady) { return; } 104 mapContainer.dataset.mapReady = '1'; 105 106 var mapId = mapContainer.dataset.mapId || mapIndex; 107 var centerLatLng = mapContainer.dataset.centerLatLng; 108 var zoom = parseInt(mapContainer.dataset.zoom, 10) || 14; 109 var useMarker = ['1', 'true'].indexOf(String(mapContainer.dataset.useMarker)) !== -1; 110 var displayDirectionsSelector = mapContainer.dataset.displayDirectionsSelector; 111 112 var lat = NaN; 113 var lng = NaN; 114 115 if (centerLatLng) { 116 var latLng = centerLatLng.split(','); 117 118 lat = parseFloat(String(latLng[0]).trim()); 119 lng = parseFloat(String(latLng[1]).trim()); 120 } 121 122 if (isNaN(lat) || isNaN(lng)) { 123 lat = -14.2392976; 124 lng = -53.1805017; 125 zoom = 4; 126 useMarker = false; 127 } 128 129 var initCoordinate = ol.proj.fromLonLat([lng, lat]); 130 var markers = []; 131 132 if (useMarker) { 133 markers.push(new ol.Feature(new ol.geom.Point(initCoordinate))); 134 } 135 136 var vectorSource = new ol.source.Vector({ 137 features: markers, 138 }); 139 140 var map = new ol.Map({ 141 layers: [ 142 new ol.layer.Tile({ 143 source: new ol.source.OSM({ 144 attributions: '<span style="font-size: 12px;">© <a href="https://www.openstreetmap.org/copyright" target="_blank">OpenStreetMap</a> contributors.</span>', 145 }), 146 }), 147 new ol.layer.Vector({ 148 source: vectorSource, 149 style: new ol.style.Style({ 150 image: new ol.style.Icon({ 151 anchor: [18, 48], 152 anchorXUnits: 'pixels', 153 anchorYUnits: 'pixels', 154 src: '/assets/img/marker-pin-single-user.png',
155 }), 156 }), 157 }), 158 ], 159 target: mapContainer, 160 view: new ol.View({ 161 center: initCoordinate, 162 zoom: zoom, 163 }), 164 interactions: ol.interaction.defaults({ 165 mouseWheelZoom: false, 166 }), 167 }); 168 169 if (displayDirectionsSelector) { 170 document.querySelectorAll(displayDirectionsSelector + ' form').forEach(function (form) { 171 form.addEventListener('submit', function (e) { 172 e.stopPropagation(); 173 e.preventDefault(); 174 175 var field = form.querySelector('input[type=text]'); 176 var origin = (field ? field.value : '').replace(/\//g, ' ').trim().replace(/\s+/g, ' '); 177 var originLocation = encodeURIComponent(origin).replace(/%20/g, '+'); 178 179 if (originLocation.length) { 180 window.open('https://www.google.com/maps/dir/' + originLocation + '/' + centerLatLng, '_blank'); 181 } 182 }); 183 }); 184 } 185 186 MAP_CONTAINERS[mapId] = { 187 map: map, 188 markersSource: vectorSource, 189 }; 190 }); 191 } 192} 193 194// Modules that refresh on their own: one endpoint, the markup goes to every 195// copy of the module on the page 196function brasFill(selector, html) { 197 document.querySelectorAll(selector).forEach(function (el) { el.innerHTML = html; }); 198} 199 200function brasFetch(url, type, done) { 201 fetch(url, { credentials: 'same-origin' }) 202 .then(function (response) { return response.ok ? (type === 'json' ? response.json() : response.text()) : null; }) 203 .then(function (data) { if (data !== null) done(data); }) 204 .catch(function () {}); 205} 206 207// On air and host on air read the same answer: asking twice per page meant 208// two round trips for the same row 209var brasOnairWait = null; 210 211function brasOnair(id, done){ 212 if(!brasOnairWait){ 213 brasOnairWait = fetch("/current-data/" + id, { credentials: 'same-origin' }) 214 .then(function (response) { return response.ok ? response.json() : null; }) 215 .catch(function () { return null; }); 216 217 setTimeout(function () { brasOnairWait = null; }, 30000); 218 } 219 220 brasOnairWait.then(function (data) { if (data) done(data); }); 221} 222 223function getOnairData(id){ 224 brasOnair(id, function(data){ 225 brasFill('.onAir', (data.current.program ? '<div class="d-flex align-items-center mt-2"> <div class="flex-shrink-0"> <img src="'+(data.current.program_img ? data.current.program_img : (data.current.user_img ? data.current.user_img : '/assets/img/noavatar.jpg'))+'" class="current-img rounded" style="max-height: 80px; max-width: 80px"> </div> <div class="flex-grow-1 overflow-hidden ms-2"> <small>'+data.current.start_time+' - '+data.current.end_time+'</small> <h6 class="mb-0 mt-0 text-nowrap text-truncate fw-bold color2">'+data.current.program+'</h6> '+(data.current.user ? '<p class="text-nowrap text-truncate mb-0 color2"><small>' + T('with') + '</small> '+data.current.user+'</p>' : '')+' </div> </div>' : '<div class="mt-3 text-center bg-block rounded p-2">' + T('no_program') + '</div>')); 226 }); 227} 228 229function getLocData(id){ 230 brasOnair(id, function(data){ 231 brasFill('.locOnline', (data.current.user ? '<div class="d-flex align-items-center mt-3"> <div class="flex-shrink-0"><div class="outer-circle border-broadcaster"> <div class="inner-circle color1"><img class="loc-img" src="'+(data.current.user_img ? data.current.user_img : '/assets/img/noavatar.jpg')+'"></div></div></div><div class="flex-grow-1 ms-2"><p class="mb-0 color3">' + T('online_now') + '</p><h5 class="mb-0 color2 ">'+data.current.user+'</h5></div></div>' : '<div class="mt-3 text-center bg-block rounded p-2">' + T('no_host') + '</div>')); 232 }); 233} 234 235// Studio camera. It used to ride on Clappr, half a megabyte loaded through 236// three script tags printed inside the module: on a router navigation those 237// tags stop blocking, so the init ran before the library existed and the box 238// stayed empty. The stream is video only, so a muted <video> is enough, with 239// hls.js brought in only where the browser cannot read the playlist by itself. 240var brasHlsReady = null; 241 242function brasHlsLib() { 243 if (brasHlsReady) { return brasHlsReady; } 244 245 brasHlsReady = new Promise(function (done, fail) { 246 if (window.Hls) { done(); return; } 247 248 var tag = document.createElement("script"); 249 250 tag.src = "/assets/js/hls.min.js"; 251 tag.onload = done; 252 tag.onerror = fail; 253 document.head.appendChild(tag); 254 }); 255 256 return brasHlsReady; 257} 258 259// Play, stop and fullscreen over the picture. The native controls would bring 260// a volume slider along, and this stream carries no sound at all. 261function brasCameraControls(box, video) { 262 var bar = document.createElement("div"); 263 var play = document.createElement("button"); 264 var full = document.createElement("button"); 265 266 var icons = { 267 play: '<svg viewBox="0 0 24 24" aria-hidden="true">
267<path fill="currentColor" d="M8 5v14l11-7z"/></svg>', 268 stop: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect fill="currentColor" x="5" y="5" width="14" height="14" rx="3"/></svg>', 269 full: '<svg viewBox="0 0 24 24" aria-hidden="true"><path fill="currentColor" d="M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z"/></svg>' 270 }; 271 272 bar.className = "camera-ctrl"; 273 play.type = "button"; 274 play.className = "camera-ctrl__btn"; 275 full.type = "button"; 276 full.className = "camera-ctrl__btn"; 277 full.innerHTML = icons.full; 278 full.setAttribute("aria-label", T("cam_full") || "Tela cheia"); 279 280 function paint() { 281 var on = !video.paused; 282 283 play.innerHTML = (on ? icons.stop : icons.play); 284 play.setAttribute("aria-label", (on ? T("cam_stop") : T("cam_play")) || ""); 285 box.classList.toggle("is-playing", on); 286 } 287 288 play.addEventListener("click", function () { 289 if (video.paused) { video.play().catch(function () {}); return; } 290 291 video.pause(); 292 }); 293 294 full.addEventListener("click", function () { 295 var target = box.parentElement || box; 296 297 if (document.fullscreenElement) { document.exitFullscreen(); return; } 298 if (target.requestFullscreen) { target.requestFullscreen().catch(function () {}); } 299 }); 300 301 video.addEventListener("play", paint); 302 video.addEventListener("pause", paint); 303 304 bar.appendChild(play); 305 bar.appendChild(full); 306 box.appendChild(bar); 307 paint(); 308} 309 310function brasCamera(port, target) { 311 var box = document.getElementById(target); 312 313 if (!box || box.dataset.ready) { return; } 314 315 box.dataset.ready = "1"; 316 317 var url = "https://brascam.brascast.com/brascam/" + port + "/playlist.m3u8"; 318 var video = document.createElement("video"); 319 320 video.className = "camera-video"; 321 video.muted = true; 322 video.defaultMuted = true; 323 video.autoplay = true; 324 video.playsInline = true; 325 video.setAttribute("playsinline", ""); 326 video.setAttribute("muted", ""); 327 video.poster = "/assets/img/camera-online-bg.png"; 328 329 box.appendChild(video); 330 brasCameraControls(box, video); 331 332 // Chrome answers "maybe" to a playlist it cannot actually read, so native 333 // playback is only trusted where the library has nothing to offer, which in 334 // practice means Safari and iOS 335 336 function nativo() { 337 if (!video.canPlayType("application/vnd.apple.mpegurl")) { box.dataset.ready = ""; return; } 338 339 video.src = url; 340 video.play().catch(function () {}); 341 } 342 343 brasHlsLib().then(function () { 344 if (!window.Hls || !Hls.isSupported()) { nativo(); return; } 345 346 // Default settings hold three segments before the first frame, which on a 347 // studio camera is a long wait staring at the poster. One segment is 348 // enough to start, and the buffer stays short so it keeps up with live. 349 var hls = new Hls({ 350 liveDurationInfinity: true, 351 liveSyncDurationCount: 1, 352 maxBufferLength: 6, 353 backBufferLength: 30, 354 manifestLoadingMaxRetry: 6, 355 levelLoadingMaxRetry: 6, 356 fragLoadingMaxRetry: 6 357 }); 358 359 hls.loadSource(url); 360 hls.attachMedia(video); 361 hls.on(Hls.Events.MANIFEST_PARSED, function () { video.play().catch(function () {}); }); 362 363 // The camera comes and goes: a broken pipe is picked up again instead of 364 // leaving the poster there forever 365 hls.on(Hls.Events.ERROR, function (event, data) { 366 if (!data.fatal) { return; } 367 368 if (data.type === Hls.ErrorTypes.MEDIA_ERROR) { hls.recoverMediaError(); return; } 369 if (data.type === Hls.ErrorTypes.NETWORK_ERROR) { window.setTimeout(function () { hls.startLoad(); }, 3000); return; } 370 371 hls.destroy(); 372 box.dataset.ready = ""; 373 }); 374 }).catch(nativo); 375} 376 377function getHistory(limit = null){ 378 brasFetch("/history"+(limit ? '/'+limit : ''), 'html', function(data){ 379 brasFill('.module-23 .played-list', data); 380 }); 381} 382 383 384var docCookies = { 385 getItem: function (sKey) { 386 if (!sKey) { return null; } 387 return decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null; 388 }, 389 setItem: function (sKey, sValue, vEnd, sPath, sDomain, bSecure) { 390 if (!sKey || /^(?:expires|max\-age|path|domain|secure)$/i.test(sKey)) { return false; } 391 var sExpires = ""; 392 if (vEnd) { 393 switch (vEnd.constructor) { 394 case Number: 395 sExpires = vEnd === Infinity ? "; expires=Fri, 31 Dec 9999 23:59:59 GMT" : "; max-age=" + vEnd; 396 break; 397 case String: 398 sExpires = "; expires=" + vEnd; 399 break; 400 case Date: 401 sExpires = "; expires=" + vEnd.toUTCString(); 402 break; 403 } 404 } 405 document.cookie = encodeURIComponent(sKey) + "=" + encodeURIComponent(sValue) + sExpires + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : "") + (bSecure ? "; secure" : ""); 406 return true; 407 }, 408 removeItem: function (sKey, sPath, sDomain) { 409 if (!this.hasItem(sKey)) { return false; } 410 document.cookie = encodeURIComponent(sKey) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT" + (sDomain ? "; domain=" + sDomain : "") + (sPath ? "; path=" + sPath : ""); 411 return true; 412 }, 413 hasItem: function (sKey) { 414 if (!sKey) { return false; } 415 return (new RegExp("(?:^|;\\s*)" + encodeURIComponent(sKey).replace(/[\-\.\+\*]/g, "\\$&") + "\\s*\\=")).test(document.cookie); 416 }, 417 keys: function () { 418 var aKeys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, "").split(/\s*(?:\=[^;]*)?;\s*/); 419 for (var nLen = aKeys.length, nIdx = 0; nIdx < nLen; nIdx++) { aKeys[nIdx] = decodeURIComponent(aKeys[nIdx]); } 420 return aKeys; 421 } 422}; 423document.addEventListener("DOMContentLoaded", function () { document.body.addEventListener("click", function(event) { if (event.target.closest(".
423navbar-toggler")) { const navbarCollapse = document.querySelector(".navbar-collapse"); if (navbarCollapse) { if (!navbarCollapse.classList.contains("show")) { navbarCollapse.classList.add("show"); } else { navbarCollapse.classList.remove("show"); } } } }); 424 425 const toggleButton = document.querySelector('.navbar-togPost'); 426 const menuList = document.getElementById('menuList'); 427 428 if (toggleButton && menuList) { 429 toggleButton.addEventListener('click', function () { 430 menuList.classList.toggle('d-none'); // mostra ou esconde 431 }); 432 } 433 434var cookieAlert = document.querySelector("#cookie-banner-lgpd"); if (cookieAlert) { cookieAlert.offsetHeight; var acceptCookies = document.querySelector(".button-accept-terms"); acceptCookies.addEventListener("click", function () { docCookies.setItem('cookie_consent', (new Date()).getTime()); cookieAlert.classList.remove("d-flex"); cookieAlert.classList.add("d-none"); }); } });
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.