PageSourceSearch

https://salesianoportoalegre.com.br/wp-content/themes/salesiano-auxiliadora/assets/js/tema.js?ver=1.31.0

js salesianoportoalegre.com.br collected 2026-10-08 19:24:17 UTC 30,164 bytes, 1,089 lines download raw bytes

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.