1/** 2 * JavaScript do tema: menu, carrossel, galeria, cookies e medição. 3 * Sem dependência de biblioteca. 4 */ 5(function () { 6 'use strict'; 7 8 var doc = document; 9 10 /* ------------------------------------------------------------------ Menu */ 11 12 function iniciaMenu() { 13 var botao = doc.querySelector('.menu-botao'); 14 var nav = doc.getElementById('menu-principal'); 15 16 if (botao && nav) { 17 botao.addEventListener('click', function () { 18 var aberto = nav.classList.toggle('aberto'); 19 botao.setAttribute('aria-expanded', aberto ? 'true' : 'false'); 20 }); 21 } 22 23 // Item com submenu: no celular o toque abre; no desktop o CSS resolve. 24 doc.querySelectorAll('.menu .tem-submenu').forEach(function (item) { 25 var gatilho = item.querySelector('.menu-gatilho, .menu-abrir'); 26 27 if (!gatilho) { 28 return; 29 } 30 31 gatilho.addEventListener('click', function (evento) { 32 evento.preventDefault(); 33 34 var aberto = item.classList.toggle('aberto'); 35 gatilho.setAttribute('aria-expanded', aberto ? 'true' : 'false'); 36 }); 37 }); 38 39 doc.addEventListener('keydown', function (evento) { 40 if ('Escape' !== evento.key) { 41 return; 42 } 43 44 doc.querySelectorAll('.menu .aberto').forEach(function (item) { 45 item.classList.remove('aberto'); 46 }); 47 }); 48 } 49 50 /* -------------------------------------------------------------- Carrossel */ 51 52 function iniciaCarrossel() { 53 var carrossel = doc.querySelector('[data-carrossel]'); 54 55 if (!carrossel) { 56 return; 57 } 58 59 var trilho = carrossel.querySelector('[data-carrossel-trilho]'); 60 var itens = carrossel.querySelectorAll('.carrossel-item'); 61 var pontos = carrossel.querySelector('[data-carrossel-pontos]'); 62 var atual = 0; 63 var timer = null; 64 65 if (itens.length < 2) { 66 return; 67 } 68 69 function vaiPara(indice) { 70 atual = (indice + itens.length) % itens.length; 71 trilho.scrollTo({ left: itens[atual].offsetLeft, behavior: 'smooth' }); 72 atualizaPontos(); 73 } 74 75 function atualizaPontos() { 76 if (!pontos) { 77 return; 78 } 79 80 pontos.querySelectorAll('button').forEach(function (botao, i) { 81 botao.setAttribute('aria-selected', i === atual ? 'true' : 'false'); 82 }); 83 } 84 85 if (pontos) { 86 itens.forEach(function (item, i) { 87 var botao = doc.createElement('button'); 88 botao.type = 'button'; 89 botao.setAttribute('role', 'tab'); 90 botao.setAttribute('aria-label', 'Destaque ' + (i + 1)); 91 botao.addEventListener('click', function () { 92 vaiPara(i); 93 reinicia(); 94 }); 95 pontos.appendChild(botao); 96 }); 97 } 98 99 var anterior = carrossel.querySelector('[data-carrossel-anterior]'); 100 var proximo = carrossel.querySelector('[data-carrossel-proximo]'); 101 102 if (anterior) { 103 anterior.addEventListener('click', function () { 104 vaiPara(atual - 1); 105 reinicia(); 106 }); 107 } 108 109 if (proximo) { 110 proximo.addEventListener('click', function () { 111 vaiPara(atual + 1); 112 reinicia(); 113 }); 114 } 115 116 // Arrastar com o dedo já funciona pelo scroll nativo; aqui só 117 // acompanhamos para os pontinhos não ficarem mentindo. 118 var esperando = null; 119 trilho.addEventListener('scroll', function () { 120 window.clearTimeout(esperando); 121 esperando = window.setTimeout(function () { 122 var indice = Math.round(trilho.scrollLeft / trilho.clientWidth); 123 124 if (indice !== atual) { 125 atual = Math.min(Math.max(indice, 0), itens.length - 1); 126 atualizaPontos(); 127 } 128 }, 120); 129 }); 130 131 function reinicia() { 132 window.clearInterval(timer); 133 comeca(); 134 } 135 136 function comeca() { 137 if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 138 return; 139 } 140 141 timer = window.setInterval(function () { 142 vaiPara(atual + 1); 143 }, 6500); 144 } 145 146 carrossel.addEventListener('mouseenter', function () { 147 window.clearInterval(timer); 148 }); 149 150 carrossel.addEventListener('mouseleave', comeca); 151 carrossel.addEventListener('focusin', function () { 152 window.clearInterval(timer); 153 }); 154 155 atualizaPontos(); 156 comeca(); 157 } 158 159 /* ------------------------------------------------------- Visor da galeria */ 160 161 function iniciaGaleria() { 162 var visor = doc.querySelector('[data-visor]'); 163 var todos = doc.querySelectorAll('.mosaico-item'); 164 165 if (!visor || !todos.length) { 166 return; 167 } 168 169 var imagem = visor.querySelector('[data-visor-imagem]'); 170 var legenda = visor.querySelector('[data-visor-legenda]'); 171 var atual = 0; 172 var ultimoFoco = null; 173 174 /* A coleção aberta é só a do mosaico que foi clicado. Na página de um 175 álbum existe um mosaico só e nada muda; na de "Nossos espaços" há um 176 por espaço, e sem isso as setas passeariam da biblioteca para a capela 177 sem aviso. */ 178 var itens = Array.prototype.slice.call(todos); 179 180 function colecaoDe(item) { 181 var mosaico = item.closest ? item.closest('.mosaico') : null; 182 183 return mosaico 184 ? Array.prototype.slice.call(mosaico.querySelectorAll('.mosaico-item')) 185 : Array.prototype.slice.call(todos); 186 } 187 188 function mostra(indice) { 189 atual = (indice + itens.length) % itens.length; 190 191 var item = itens[atual]; 192 imagem.src = item.getAttribute('data-foto'); 193 imagem.alt = item.getAttribute('data-legenda') || ''; 194 legenda.textContent = item.getAttribute('data-legenda') || ''; 195 } 196 197 function abre(item) { 198 itens = colecaoDe(item); 199 ultimoFoco = doc.activeElement; 200 mostra(itens.indexOf(item)); 201 visor.hidden = false;
202 doc.body.style.overflow = 'hidden'; 203 visor.querySelector('[data-visor-fechar]').focus(); 204 } 205 206 function fecha() { 207 visor.hidden = true; 208 doc.body.style.overflow = ''; 209 210 if (ultimoFoco) { 211 ultimoFoco.focus(); 212 } 213 } 214 215 Array.prototype.forEach.call(todos, function (item) { 216 item.addEventListener('click', function () { 217 abre(item); 218 }); 219 }); 220 221 visor.querySelector('[data-visor-fechar]').addEventListener('click', fecha); 222 visor.querySelector('[data-visor-anterior]').addEventListener('click', function () { 223 mostra(atual - 1); 224 }); 225 visor.querySelector('[data-visor-proximo]').addEventListener('click', function () { 226 mostra(atual + 1); 227 }); 228 229 visor.addEventListener('click', function (evento) { 230 if (evento.target === visor) { 231 fecha(); 232 } 233 }); 234 235 doc.addEventListener('keydown', function (evento) { 236 if (visor.hidden) { 237 return; 238 } 239 240 if ('Escape' === evento.key) { 241 fecha(); 242 } else if ('ArrowLeft' === evento.key) { 243 mostra(atual - 1); 244 } else if ('ArrowRight' === evento.key) { 245 mostra(atual + 1); 246 } 247 }); 248 } 249 250 /* ------------------------------------------------------- Modais da home */ 251 252 /* 253 * Serve aos espaços e aos nÃveis de ensino. Cada card tem 254 * data-modal-abre="id" apontando para um <template> com o conteúdo; a seção 255 * tem um <dialog data-modal> só, que recebe a cópia na hora de abrir. 256 * Navegador sem <dialog> segue o link do card, que leva à página própria. 257 */ 258 function ligaModal(secao) { 259 var modal = secao ? secao.querySelector('[data-modal]') : null; 260 var corpo = modal ? modal.querySelector('[data-modal-corpo]') : null; 261 262 if (!modal || !corpo || 'function' !== typeof modal.showModal) { 263 return; 264 } 265 266 // Galeria de fotos, quando o conteúdo tem uma (o do espaço tem). 267 var fotos = []; 268 var atual = 0; 269 270 function mostraFoto(indice) { 271 if (!fotos.length) { 272 return; 273 } 274 275 atual = (indice + fotos.length) % fotos.length; 276 277 var escolhida = fotos[atual]; 278 var palco = corpo.querySelector('[data-modal-palco]'); 279 var contador = corpo.querySelector('[data-modal-contador]'); 280 281 palco.src = escolhida.getAttribute('data-foto'); 282 palco.alt = escolhida.getAttribute('data-legenda') || ''; 283 284 if (contador) { 285 contador.textContent = atual + 1 + ' / ' + fotos.length; 286 } 287 288 fotos.forEach(function (botao, i) { 289 if (i === atual) { 290 botao.setAttribute('aria-current', 'true'); 291 botao.scrollIntoView({ block: 'nearest', inline: 'nearest' }); 292 } else { 293 botao.removeAttribute('aria-current'); 294 } 295 }); 296 } 297 298 function abre(id) { 299 var molde = doc.getElementById(id); 300 301 if (!molde) { 302 return false; 303 } 304 305 corpo.innerHTML = ''; 306 corpo.appendChild(molde.content.cloneNode(true)); 307 308 // A cor do nÃvel vem no próprio molde e vai para o <dialog>. 309 modal.setAttribute('data-tom', molde.getAttribute('data-modal-tom') || ''); 310 311 var titulo = corpo.querySelector('[data-modal-titulo]'); 312 313 if (titulo) { 314 titulo.id = modal.getAttribute('aria-labelledby'); 315 } 316 317 fotos = Array.prototype.slice.call(corpo.querySelectorAll('[data-modal-miniatura]')); 318 atual = 0; 319 320 fotos.forEach(function (botao, i) { 321 botao.addEventListener('click', function () { 322 mostraFoto(i); 323 }); 324 }); 325 326 var voltar = corpo.querySelector('[data-modal-anterior]'); 327 var avancar = corpo.querySelector('[data-modal-proximo]'); 328 329 if (voltar) { 330 voltar.addEventListener('click', function () { 331 mostraFoto(atual - 1); 332 }); 333 } 334 335 if (avancar) { 336 avancar.addEventListener('click', function () { 337 mostraFoto(atual + 1); 338 }); 339 } 340 341 mostraFoto(0); 342 modal.showModal(); 343 doc.body.style.overflow = 'hidden'; 344 345 return true; 346 } 347 348 secao.querySelectorAll('[data-modal-abre]').forEach(function (card) { 349 card.addEventListener('click', function (evento) { 350 if (abre(card.getAttribute('data-modal-abre'))) { 351 evento.preventDefault(); 352 } 353 }); 354 }); 355 356 // A limpeza roda na hora, e de novo no evento "close" (que é o caminho 357 // do Esc). Rodar duas vezes não faz mal. 358 function limpa() { 359 doc.body.style.overflow = ''; 360 corpo.innerHTML = ''; 361 fotos = []; 362 } 363 364 function fecha() { 365 modal.close(); 366 limpa(); 367 } 368 369 modal.querySelector('[data-modal-fechar]').addEventListener('click', fecha); 370 371 // Clique no fundo escuro: o alvo é o próprio <dialog>, fora do corpo. 372 modal.addEventListener('click', function (evento) { 373 if (evento.target === modal) { 374 fecha(); 375 } 376 }); 377 378 modal.addEventListener('keydown', function (evento) { 379 if ('ArrowLeft' === evento.key) { 380 mostraFoto(atual - 1); 381 } else if ('ArrowRight' === evento.key) { 382 mostraFoto(atual + 1); 383 } 384 }); 385 386 // O foco volta sozinho ao card que abriu. O evento chega depois do 387 // fechamento: se nesse meio-tempo outro card já foi aberto, não apaga. 388 modal.addEventListener('close', function () { 389 if (!modal.open) { 390 limpa(); 391 } 392 }); 393 } 394 395 /* ------------------------------------------------- Carrossel da home */ 396 397 /* Espaços e álbuns, quando passam de 4: as setas andam uma "página" do
398 trilho e se apagam nas pontas. Arrastar no celular é o scroll nativo. */ 399 function ligaTrilho(secao) { 400 var trilho = secao.querySelector('[data-trilho]'); 401 var anterior = secao.querySelector('[data-trilho-anterior]'); 402 var proximo = secao.querySelector('[data-trilho-proximo]'); 403 404 if (!trilho || !anterior || !proximo) { 405 return; 406 } 407 408 function confereSetas() { 409 var fim = trilho.scrollWidth - trilho.clientWidth - 2; 410 anterior.disabled = trilho.scrollLeft <= 2; 411 proximo.disabled = trilho.scrollLeft >= fim; 412 } 413 414 anterior.addEventListener('click', function () { 415 trilho.scrollBy({ left: -trilho.clientWidth, behavior: 'smooth' }); 416 }); 417 418 proximo.addEventListener('click', function () { 419 trilho.scrollBy({ left: trilho.clientWidth, behavior: 'smooth' }); 420 }); 421 422 trilho.addEventListener('scroll', confereSetas, { passive: true }); 423 window.addEventListener('resize', confereSetas); 424 confereSetas(); 425 } 426 427 /* ------------------------------------------ Espaços e álbuns na home */ 428 429 function iniciaSecoesComFotos() { 430 doc.querySelectorAll('[data-espacos], [data-albuns]').forEach(function (secao) { 431 ligaTrilho(secao); 432 ligaModal(secao); 433 }); 434 } 435 436 /* ------------------------------------------------ NÃveis de ensino na home */ 437 438 function iniciaNiveis() { 439 ligaModal(doc.querySelector('[data-niveis]')); 440 } 441 442 /* ---------------------------------------------------- Cookies e medição */ 443 444 function leCookie(nome) { 445 var partes = doc.cookie.split('; '); 446 447 for (var i = 0; i < partes.length; i++) { 448 var par = partes[i].split('='); 449 450 if (par[0] === nome) { 451 return decodeURIComponent(par.slice(1).join('=')); 452 } 453 } 454 455 return ''; 456 } 457 458 /* 459 * DomÃnio do site, sem o www. 460 * 461 * Sem isto o cookie fica preso ao endereço exato em que foi gravado: quem 462 * aceitou em www.colegio.com.br e depois entra em colegio.com.br leva o aviso 463 * de novo, porque para o navegador são dois lugares diferentes. Endereço por 464 * número (IP) e localhost não aceitam domÃnio, e ali fica sem. 465 */ 466 function dominioDoSite() { 467 var host = window.location.hostname; 468 469 if (!host || -1 === host.indexOf('.') || /^[0-9.]+$/.test(host)) { 470 return ''; 471 } 472 473 return host.replace(/^www\./, ''); 474 } 475 476 function gravaCookie(nome, valor) { 477 var validade = new Date(); 478 validade.setFullYear(validade.getFullYear() + 1); 479 480 var dominio = dominioDoSite(); 481 482 doc.cookie = 483 nome + 484 '=' + 485 encodeURIComponent(valor) + 486 '; expires=' + 487 validade.toUTCString() + 488 '; path=/' + 489 (dominio ? '; domain=.' + dominio : '') + 490 '; SameSite=Lax'; 491 } 492 493 function apagaCookie(nome) { 494 var dominio = dominioDoSite(); 495 var fim = '=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/'; 496 497 // Nos dois formatos: o antigo, preso ao endereço, e o novo, do domÃnio. 498 doc.cookie = nome + fim; 499 500 if (dominio) { 501 doc.cookie = nome + fim + '; domain=.' + dominio; 502 } 503 } 504 505 /* 506 * Cópia da escolha fora do cookie. 507 * 508 * Plugin de segurança, limpeza de cache e polÃtica do navegador apagam 509 * cookie com mais facilidade do que se imagina, e cada limpeza dessas traz o 510 * aviso de volta para quem já tinha respondido. A cópia serve de memória: se 511 * o cookie sumiu mas a resposta está guardada aqui, ela é regravada em 512 * silêncio e o visitante não é perguntado de novo. 513 */ 514 function leGuardado(chave) { 515 try { 516 return window.localStorage.getItem(chave) || ''; 517 } catch (e) { 518 return ''; 519 } 520 } 521 522 function guarda(chave, valor) { 523 try { 524 if (valor) { 525 window.localStorage.setItem(chave, valor); 526 } else { 527 window.localStorage.removeItem(chave); 528 } 529 } catch (e) { 530 /* Navegação anônima com armazenamento bloqueado: o cookie basta. */ 531 } 532 } 533 534 function carregaMedicao() { 535 var config = window.salMedicao; 536 537 if (!config || !config.id) { 538 return; 539 } 540 541 if ('gtm' === config.tipo) { 542 window.dataLayer = window.dataLayer || []; 543 window.dataLayer.push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); 544 545 var gtm = doc.createElement('script'); 546 gtm.async = true; 547 gtm.src = 'https://www.googletagmanager.com/gtm.js?id=' + config.id; 548 doc.head.appendChild(gtm); 549 return; 550 } 551 552 var ga = doc.createElement('script'); 553 ga.async = true;
554 ga.src = 'https://www.googletagmanager.com/gtag/js?id=' + config.id; 555 doc.head.appendChild(ga); 556 557 window.dataLayer = window.dataLayer || []; 558 window.gtag = function () { 559 window.dataLayer.push(arguments); 560 }; 561 window.gtag('js', new Date()); 562 window.gtag('config', config.id, { anonymize_ip: true }); 563 } 564 565 /* 566 * Resposta guardada. 567 * 568 * Formato: versao|data|grupos aceitos, separados por virgula. A versao faz a 569 * pergunta voltar quando muda o que se coleta, em vez de valer para sempre um 570 * sim dado a outra coisa. A data fica como registro de quando foi dito. 571 * 572 * "aceitou" e "recusou" sao as respostas do formato antigo, de quando a 573 * escolha era so sim ou nao. Continuam valendo, e viram tudo ou nada: quem ja 574 * respondeu nao e incomodado de novo por causa de uma mudanca nossa. 575 */ 576 function opcionais() { 577 return (window.salCookies && window.salCookies.opcionais) || []; 578 } 579 580 function versaoAtual() { 581 return (window.salCookies && window.salCookies.versao) || '1'; 582 } 583 584 function interpreta(bruto) { 585 if (!bruto) { 586 return null; 587 } 588 589 if ('aceitou' === bruto) { 590 return { versao: versaoAtual(), aceitos: opcionais() }; 591 } 592 593 if ('recusou' === bruto) { 594 return { versao: versaoAtual(), aceitos: [] }; 595 } 596 597 var partes = bruto.split('|'); 598 599 if (3 !== partes.length) { 600 return null; 601 } 602 603 return { 604 versao: partes[0], 605 data: partes[1], 606 aceitos: partes[2] ? partes[2].split(',') : [] 607 }; 608 } 609 610 function escreve(aceitos) { 611 var hoje = new Date(); 612 var data = 613 hoje.getFullYear() + 614 '-' + 615 ('0' + (hoje.getMonth() + 1)).slice(-2) + 616 '-' + 617 ('0' + hoje.getDate()).slice(-2); 618 619 return versaoAtual() + '|' + data + '|' + aceitos.join(','); 620 } 621 622 function aceitou(escolha, grupo) { 623 return !!escolha && -1 !== escolha.aceitos.indexOf(grupo); 624 } 625 626 function salva(chave, aceitos) { 627 var valor = escreve(aceitos); 628 629 gravaCookie(chave, valor); 630 guarda(chave, valor); 631 } 632 633 /* ----------------------------------------------- Conteudo vindo de fora */ 634 635 /* 636 * Troca as caixas de aviso pelo conteudo real. O trecho fica no atributo, e 637 * nao dentro da caixa: dentro, o navegador carregaria o iframe assim mesmo, e 638 * a permissao seria de fachada. 639 */ 640 function soltaIncorporados(escopo) { 641 var caixas = (escopo || doc).querySelectorAll('[data-incorporado]'); 642
643 caixas.forEach(function (caixa) { 644 var html = caixa.getAttribute('data-incorporado'); 645 646 if (!html) { 647 return; 648 } 649 650 var berco = doc.createElement('div'); 651 berco.innerHTML = html; 652 653 var pai = caixa.parentNode; 654 655 while (berco.firstChild) { 656 pai.insertBefore(berco.firstChild, caixa); 657 } 658 659 pai.removeChild(caixa); 660 }); 661 } 662 663 function iniciaIncorporados(chave) { 664 doc.querySelectorAll('[data-incorporado-liberar]').forEach(function (botao) { 665 botao.addEventListener('click', function () { 666 var caixa = botao.closest('[data-incorporado]'); 667 668 if (!caixa) { 669 return; 670 } 671 672 /* 673 * Carregar aqui e um sim a este conteudo. Guarda a resposta, senao a 674 * proxima pagina pergunta de novo e a permissao vira um pedagio a cada 675 * visita. 676 */ 677 var escolha = interpreta(leCookie(chave)) || { aceitos: [] }; 678 679 if (-1 === escolha.aceitos.indexOf('incorporado')) { 680 escolha.aceitos.push('incorporado'); 681 salva(chave, escolha.aceitos); 682 } 683 684 soltaIncorporados(caixa.parentNode); 685 }); 686 }); 687 } 688 689 /* -------------------------------------------------- Aviso e preferencias */ 690 691 function aplica(escolha) { 692 if (aceitou(escolha, 'medicao')) { 693 carregaMedicao(); 694 } 695 696 if (aceitou(escolha, 'incorporado')) { 697 soltaIncorporados(); 698 } 699 } 700 701 function iniciaCookies() { 702 var chave = (window.salDados && window.salDados.cookieChave) || 'sal_cookies'; 703 var aviso = doc.querySelector('[data-aviso-cookies]'); 704 var bruto = leCookie(chave); 705 706 // Cookie perdido, resposta guardada: regrava e segue sem perguntar. 707 if (!bruto) { 708 bruto = leGuardado(chave); 709 710 if (bruto) { 711 gravaCookie(chave, bruto); 712 } 713 } 714 715 var escolha = interpreta(bruto); 716 717 // Mudou o que se coleta: a resposta antiga nao serve, pergunta de novo. 718 if (escolha && escolha.versao !== versaoAtual()) { 719 escolha = null; 720 } 721 722 if (escolha) { 723 aplica(escolha); 724 } 725 726 iniciaIncorporados(chave); 727 728 if (!aviso) { 729 return; 730 } 731 732 var painel = aviso.querySelector('[data-cookies-painel]'); 733 var abrir = aviso.querySelector('[data-cookies-abrir]'); 734 var entradas = aviso.querySelectorAll('[data-cookies-grupo]'); 735 736 function marca(aceitos) { 737 entradas.forEach(function (entrada) { 738 entrada.checked = -1 !== aceitos.indexOf(entrada.getAttribute('data-cookies-grupo')); 739 }); 740 } 741 742 function responde(aceitos) { 743 salva(chave, aceitos); 744 aplica({ aceitos: aceitos }); 745 aviso.hidden = true; 746 } 747 748 if (abrir && painel) { 749 abrir.addEventListener('click', function () { 750 var abre = painel.hidden; 751 752 painel.hidden = !abre; 753 abrir.setAttribute('aria-expanded', abre ? 'true' : 'false'); 754 755 if (abre) { 756 var primeira = painel.querySelector('[data-cookies-grupo]'); 757 758 if (primeira) { 759 primeira.focus(); 760 } 761 } 762 }); 763 } 764 765 aviso.querySelectorAll('[data-cookies]').forEach(function (botao) { 766 botao.addEventListener('click', function () { 767 var acao = botao.getAttribute('data-cookies'); 768 769 if ('aceitar' === acao) { 770 responde(opcionais()); 771 return; 772 } 773 774 if ('recusar' === acao) { 775 responde([]); 776 return; 777 } 778 779 // Salvar: vale o que estiver marcado no painel. 780 var marcados = []; 781 782 entradas.forEach(function (entrada) { 783 if (entrada.checked) { 784 marcados.push(entrada.getAttribute('data-cookies-grupo')); 785 } 786 }); 787 788 responde(marcados); 789 }); 790 }); 791 792 if (escolha) { 793 marca(escolha.aceitos); 794 return; 795 } 796 797 marca([]); 798 aviso.hidden = false; 799 } 800 801 /* -------------------------------------------- Rever a escolha de cookies */ 802 803 function iniciaReverCookies() { 804 var chave = (window.salDados && window.salDados.cookieChave) || 'sal_cookies'; 805 var aviso = doc.querySelector('[data-aviso-cookies]'); 806 var links = doc.querySelectorAll('[data-cookies-rever]'); 807 808 if (!aviso || !links.length) { 809 return; 810 } 811 812 links.forEach(function (link) { 813 link.addEventListener('click', function () { 814 // Apaga a escolha e mostra o aviso de novo, no lugar de esconder o 815 // assunto depois do primeiro clique. 816 apagaCookie(chave); 817 guarda(chave, ''); 818 aviso.hidden = false;
819 820 // Volta com o painel fechado e as chaves zeradas: e uma pergunta nova, 821 // nao a antiga com a resposta velha ainda marcada. 822 var painel = aviso.querySelector('[data-cookies-painel]'); 823 var abrir = aviso.querySelector('[data-cookies-abrir]'); 824 825 if (painel) { 826 painel.hidden = true; 827 } 828 829 if (abrir) { 830 abrir.setAttribute('aria-expanded', 'false'); 831 } 832 833 aviso.querySelectorAll('[data-cookies-grupo]').forEach(function (entrada) { 834 entrada.checked = false; 835 }); 836 aviso.scrollIntoView({ block: 'nearest' }); 837 838 var primeiro = aviso.querySelector('[data-cookies]'); 839 840 if (primeiro) { 841 primeiro.focus(); 842 } 843 }); 844 }); 845 } 846 847 /* ------------------------------------------------------- Voltar ao topo */ 848 849 function iniciaTopo() { 850 var alvo = 400; 851 852 function confere() { 853 doc.body.classList.toggle('rolou', window.scrollY > alvo); 854 } 855 856 window.addEventListener('scroll', confere, { passive: true }); 857 confere(); 858 } 859 860 function iniciaBlog() { 861 var tira = doc.querySelector('[data-blog]'); 862 863 if (!tira) { 864 return; 865 } 866 867 var trilho = tira.querySelector('[data-blog-trilho]'); 868 var anterior = tira.querySelector('[data-blog-anterior]'); 869 var proximo = tira.querySelector('[data-blog-proximo]'); 870 871 if (!trilho || !anterior || !proximo) { 872 return; 873 } 874 875 function passo() { 876 var item = trilho.querySelector('.blog-item'); 877 878 return item ? item.getBoundingClientRect().width + 18 : trilho.clientWidth; 879 } 880 881 function confereSetas() { 882 var fim = trilho.scrollWidth - trilho.clientWidth - 2; 883 884 anterior.disabled = trilho.scrollLeft <= 2; 885 proximo.disabled = trilho.scrollLeft >= fim; 886 } 887 888 anterior.addEventListener('click', function () { 889 trilho.scrollLeft -= passo(); 890 }); 891 892 proximo.addEventListener('click', function () { 893 trilho.scrollLeft += passo(); 894 }); 895 896 trilho.addEventListener('scroll', confereSetas); 897 window.addEventListener('resize', confereSetas); 898 confereSetas(); 899 900 /* Passa sozinho. Ao chegar no fim volta ao começo, para a tira não parar 901 morta na última capa. Pausa com o ponteiro em cima e com o foco dentro, 902 senão quem lê com calma perde o item. */ 903 var timer = null; 904 905 function anda() { 906 var fim = trilho.scrollWidth - trilho.clientWidth - 2; 907 908 if (trilho.scrollLeft >= fim) { 909 trilho.scrollTo({ left: 0, behavior: 'smooth' }); 910 } else { 911 trilho.scrollBy({ left: passo(), behavior: 'smooth' }); 912 } 913 } 914 915 function comeca() { 916 if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) { 917 return; 918 } 919 920 window.clearInterval(timer); 921 timer = window.setInterval(anda, 5000); 922 } 923 924 function para() { 925 window.clearInterval(timer); 926 } 927 928 tira.addEventListener('mouseenter', para); 929 tira.addEventListener('mouseleave', comeca); 930 tira.addEventListener('focusin', para); 931 tira.addEventListener('focusout', comeca); 932 anterior.addEventListener('click', comeca); 933 proximo.addEventListener('click', comeca); 934 935 comeca(); 936 } 937 938 /* ------------------------------------------------------ Acessibilidade */ 939 940 /* Tamanho da letra e alto contraste. A escolha fica no navegador de quem 941 usa: refazer o ajuste a cada página é justamente o que cansa. */ 942 function iniciaAcessibilidade() { 943 var barra = doc.querySelector('[data-a11y]'); 944 945 if (!barra) { 946 return; 947 } 948 949 var raiz = doc.documentElement; 950 var abrir = barra.querySelector('[data-a11y-abrir]'); 951 var painel = barra.querySelector('[data-a11y-painel]'); 952 var limpar = barra.querySelector('[data-a11y-limpar]'); 953 var contraste = barra.querySelector('[data-a11y-contraste]'); 954 var LIMITE = 2; 955 956 function guarda(chave, valor) { 957 try { 958 if (valor === null) { 959 window.localStorage.removeItem('sal-' + chave); 960 } else { 961 window.localStorage.setItem('sal-' + chave, valor); 962 } 963 } catch (e) { 964 /* Navegador com armazenamento bloqueado: vale só nesta página. */ 965 } 966 } 967 968 function lido(chave) {
969 try { 970 return window.localStorage.getItem('sal-' + chave); 971 } catch (e) { 972 return null; 973 } 974 } 975 976 function atualiza() { 977 var mexido = raiz.getAttribute('data-fonte') || raiz.getAttribute('data-contraste'); 978 979 if (limpar) { 980 limpar.hidden = !mexido; 981 } 982 983 if (contraste) { 984 contraste.setAttribute('aria-pressed', 'alto' === raiz.getAttribute('data-contraste') ? 'true' : 'false'); 985 } 986 } 987 988 function fonte(passo) { 989 var atual = parseInt(raiz.getAttribute('data-fonte') || '0', 10); 990 var novo = Math.max(-1, Math.min(LIMITE, atual + passo)); 991 992 if (novo === 0) { 993 raiz.removeAttribute('data-fonte'); 994 guarda('fonte', null); 995 } else { 996 raiz.setAttribute('data-fonte', novo); 997 guarda('fonte', novo); 998 } 999 1000 atualiza(); 1001 } 1002 1003 Array.prototype.forEach.call(barra.querySelectorAll('[data-a11y-fonte]'), function (botao) { 1004 botao.addEventListener('click', function () { 1005 fonte('maior' === botao.getAttribute('data-a11y-fonte') ? 1 : -1); 1006 }); 1007 }); 1008 1009 if (contraste) { 1010 contraste.addEventListener('click', function () { 1011 if ('alto' === raiz.getAttribute('data-contraste')) { 1012 raiz.removeAttribute('data-contraste'); 1013 guarda('contraste', null); 1014 } else { 1015 raiz.setAttribute('data-contraste', 'alto'); 1016 guarda('contraste', 'alto'); 1017 } 1018 1019 atualiza(); 1020 }); 1021 } 1022 1023 if (limpar) { 1024 limpar.addEventListener('click', function () { 1025 raiz.removeAttribute('data-fonte'); 1026 raiz.removeAttribute('data-contraste'); 1027 guarda('fonte', null); 1028 guarda('contraste', null); 1029 atualiza(); 1030 }); 1031 } 1032 1033 /* Abrir e fechar o painel. Fechado por padrão para não cobrir o site. */ 1034 if ( abrir && painel ) { 1035 abrir.addEventListener('click', function () { 1036 var aberto = painel.hidden; 1037 painel.hidden = !aberto; 1038 abrir.setAttribute('aria-expanded', aberto ? 'true' : 'false'); 1039 }); 1040 1041 doc.addEventListener('click', function (evento) { 1042 if (!painel.hidden && !barra.contains(evento.target)) { 1043 painel.hidden = true; 1044 abrir.setAttribute('aria-expanded', 'false'); 1045 } 1046 }); 1047 1048 doc.addEventListener('keydown', function (evento) { 1049 if ('Escape' === evento.key && !painel.hidden) { 1050 painel.hidden = true; 1051 abrir.setAttribute('aria-expanded', 'false'); 1052 abrir.focus(); 1053 } 1054 }); 1055 1056 var pular = barra.querySelector('[data-a11y-fechar]'); 1057 1058 if (pular) { 1059 pular.addEventListener('click', function () { 1060 painel.hidden = true; 1061 abrir.setAttribute('aria-expanded', 'false'); 1062 }); 1063 } 1064 } 1065 1066 atualiza(); 1067 } 1068 1069 /* -------------------------------------------------------------- Arranque */ 1070 1071 function inicia() { 1072 iniciaAcessibilidade(); 1073 iniciaMenu(); 1074 iniciaCarrossel(); 1075 iniciaGaleria(); 1076 iniciaSecoesComFotos(); 1077 iniciaNiveis(); 1078 iniciaBlog(); 1079 iniciaCookies(); 1080 iniciaReverCookies(); 1081 iniciaTopo(); 1082 } 1083 1084 if ('loading' === doc.readyState) { 1085 doc.addEventListener('DOMContentLoaded', inicia); 1086 } else { 1087 inicia(); 1088 } 1089})();
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.