1/** 2 * b247-header-button.js 3 * 4 * Os botões do header (inc/header-button.php, até quatro): manda cada clique 5 * para o GA4, abre e fecha as janelas da ação "janela", acende a bolinha do 6 * "Ao vivo" enquanto a TV 247 está no ar e refaz o ajuste dos textos quando 7 * a largura muda. A ação "newsletter" continua com o b247-newsletter.js, que 8 * abre o formulário. 9 */ 10(function (w, d) { 11 'use strict'; 12 13 // ââ Cliques: para o GA4 do site âââââââââââââââââââââââââââââââââââââââ 14 // Um evento header_button_click pela window.b247Ga4Event do b247-ga4.js 15 // (inc/ga4.php), procurada na hora do clique: sem ela (GA4 desligado), 16 // nada sai. content_type e content_id são dimensões prontas do GA4 17 // ("Content type", "Content ID"), então cada botão aparece nos relatórios 18 // sem criar dimensão; button_position só aparece se for registrada. 19 // "Apoie o 247" entra para comparar. 20 var envia = function (acao, pos, url) { 21 if (typeof w.b247Ga4Event !== 'function') { return; } 22 w.b247Ga4Event('header_button_click', { content_type: 'header_button', content_id: acao, button_position: pos, link_url: url }); 23 }; 24 d.addEventListener('click', function (e) { 25 var t = e.target; 26 if (!t || typeof t.closest !== 'function') { return; } 27 var b = t.closest('[data-hbtn]'); 28 if (b) { 29 envia(b.getAttribute('data-hbtn'), b.getAttribute('data-hbtn-pos') || '', b.getAttribute('href') || ''); 30 return; 31 } 32 var apoie = t.closest('.b247-header-right .b247-apoie-cta a'); 33 if (apoie) { envia('apoie', '', apoie.getAttribute('href') || ''); } 34 }); 35 36 // ââ As janelas: uma por botão "janela", ligada a ele pelo aria-controls â 37 var aberta = null; 38 var lastFocus = null; 39 40 // Mesma moldura do formulário da newsletter: só abre se o CSS dela 41 // carregou (position: fixed); sem ele, vai direto para o endereço do 42 // botão da janela em vez de travar a página. 43 var abrir = function (modal, trigger) { 44 var cta = modal.querySelector('.b247-hmodal__cta'); 45 if (w.getComputedStyle(modal).position !== 'fixed') { 46 if (cta) { w.location.assign(cta.href); } 47 return; 48 } 49 lastFocus = trigger || d.activeElement; 50 modal.hidden = false; 51 aberta = modal; 52 d.body.classList.add('b247-modal-open'); 53 if (cta) { cta.focus(); } 54 }; 55 var fechar = function () { 56 if (!aberta) { return; } 57 aberta.hidden = true; 58 aberta = null; 59 // A trava de rolagem é a mesma do formulário da newsletter: só sai se 60 // ele não estiver aberto também. 61 var nl = d.getElementById('b247-newsletter-modal'); 62 if (!nl || nl.hidden) { d.body.classList.remove('b247-modal-open'); } 63 if (lastFocus && typeof lastFocus.focus === 'function') { lastFocus.focus(); } 64 lastFocus = null; 65 }; 66 67 d.addEventListener('click', function (e) { 68 var t = e.target; 69 if (!t || typeof t.closest !== 'function') { return; } 70 var abre = t.closest('[data-action="open-header-modal"]'); 71 if (abre) { 72 var modal = d.getElementById(abre.getAttribute('aria-controls') || ''); 73 if (modal) { 74 e.preventDefault(); 75 abrir(modal, abre); 76 } 77 return; 78 } 79 if (t.closest('[data-action="close-header-modal"]')) { fechar(); } 80 }); 81 d.addEventListener('keydown', function (e) { 82 if (!aberta) { return; } 83 if ('Escape' === e.key) { 84 fechar(); 85 return; 86 } 87 // O foco não sai da janela: Tab e Shift+Tab giram entre o X e os 88 // botões, em vez de voltar ao header atrás do fundo escuro. 89 if ('Tab' !== e.key) { return; } 90 var itens = aberta.querySelectorAll('a[href], button:not([disabled])'); 91 if (!itens.length) { return; } 92 var primeiro = itens[0]; 93 var ultimo = itens[itens.length - 1]; 94 if (!aberta.contains(d.activeElement)) { 95 e.preventDefault(); 96 primeiro.focus(); 97 } else if (e.shiftKey && d.activeElement === primeiro) { 98 e.preventDefault(); 99 ultimo.focus(); 100 } else if (!e.shiftKey && d.activeElement === ultimo) { 101 e.preventDefault(); 102 primeiro.focus(); 103 } 104 }); 105 106 // ââ "Ao vivo": bolinha enquanto a TV está no ar âââââââââââââââââââââââ 107 // Mesma regra da grade na sidebar (b247-sidebar.js): um programa vai até 108 // o próximo e o último até a meia-noite, então a TV está no ar do primeiro 109 // programa até o fim do dia. Calculado aqui, no horário de BrasÃlia, 110 // porque o HTML sai do cache de borda. 111 // 112 // O link também: o botão abre o vÃdeo do programa no ar (cada programa da 113 // grade com o seu vÃdeo, em data-live-programs) e, quando o programa no ar 114 // não tem vÃdeo próprio ou a TV não está no ar, o canal (data-live- 115 // fallback). Sem data-live-programs o link está fixo no Gestor e fica como 116 // veio. 117 var fmt = null; 118 var agora = function () { 119 try { 120 fmt = fmt || new Intl.DateTimeFormat('en-CA', { 121 timeZone: 'America/Sao_Paulo', 122 year: 'numeric', month: '2-digit', day: '2-digit', 123 hour: '2-digit', minute: '2-digit', hour12: false 124 }); 125 var p = {};
126 fmt.formatToParts(new Date()).forEach(function (x) { p[x.type] = x.value; }); 127 return { dia: p.year + '-' + p.month + '-' + p.day, min: (parseInt(p.hour, 10) % 24) * 60 + parseInt(p.minute, 10) }; 128 } catch (e) { 129 var n = new Date(); 130 return { dia: '', min: n.getHours() * 60 + n.getMinutes() }; 131 } 132 }; 133 var vivos = []; 134 Array.prototype.forEach.call(d.querySelectorAll('.b247-nl-open--aovivo[data-live-start]'), function (live) { 135 var dot = live.querySelector('.b247-nl-open__dot'); 136 var inicio = parseInt(live.getAttribute('data-live-start'), 10); 137 var data = live.getAttribute('data-live-date') || ''; 138 var programas = []; 139 try { programas = JSON.parse(live.getAttribute('data-live-programs') || '[]') || []; } catch (e) { programas = []; } 140 var reserva = live.getAttribute('data-live-fallback') || ''; 141 var rotulo = live.getAttribute('aria-label') || ''; 142 vivos.push(function (a) { 143 // Uma grade de outro dia não acende nada: a página pode ter saÃdo 144 // do cache ontem. 145 var hoje = !data || !a.dia || a.dia === data; 146 var noAr = hoje && !isNaN(inicio) && a.min >= inicio; 147 if (dot) { dot.hidden = !noAr; } 148 live.classList.toggle('is-live', noAr); 149 // A bolinha é só visual; o nome do botão diz o mesmo para quem 150 // não a vê. 151 if (rotulo) { 152 live.setAttribute('aria-label', noAr ? rotulo + ' (no ar agora)' : rotulo); 153 live.setAttribute('title', noAr ? rotulo + ' (no ar agora)' : rotulo); 154 } 155 if (programas.length && reserva) { 156 var video = ''; 157 if (noAr) { 158 for (var i = 0; i < programas.length; i++) { 159 var ini = programas[i][0]; 160 var prox = i + 1 < programas.length ? programas[i + 1][0] : 24 * 60; 161 if (a.min >= ini && a.min < prox) { video = programas[i][1] || ''; } 162 } 163 } 164 live.setAttribute('href', video || reserva); 165 } 166 }); 167 }); 168 if (vivos.length) { 169 var atualiza = function () { 170 var a = agora(); 171 vivos.forEach(function (f) { f(a); }); 172 }; 173 atualiza(); 174 w.setInterval(atualiza, 60000); 175 } 176 177 // ââ O que cabe no header: os textos no computador, o 2º Ãcone no celular 178 // O ajuste (b247HbtnFit) já rodou no fim do header, antes de a página 179 // aparecer, e só existe se algum botão mostra texto ou se há dois no 180 // celular. Roda de novo quando a largura muda e quando as fontes terminam 181 // de carregar, porque as duas coisas mudam o que cabe. 182 if (typeof w.b247HbtnFit === 'function') { 183 var quadro = 0; 184 w.addEventListener('resize', function () { 185 if (quadro) { return; } 186 quadro = w.requestAnimationFrame(function () { 187 quadro = 0; 188 w.b247HbtnFit(); 189 }); 190 }); 191 if (d.fonts && d.fonts.ready) { 192 d.fonts.ready.then(function () { w.b247HbtnFit(); }); 193 } 194 } 195}(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.