PageSourceSearch

https://arquidiocesebh.org.br/wp-content/themes/portal-arquidioces…/leitor-tela.js?ver=0.35279400%201790934711

js arquidiocesebh.org.br collected 2026-10-02 09:53:02 UTC 5,209 bytes, 181 lines download raw bytes

1document.addEventListener('DOMContentLoaded', function () {
2  'use strict';
3
4  const playButton = document.querySelector('.play-button');
5  const speedButtons = {
6    s05: document.querySelector('.s05'),
7    s10: document.querySelector('.s10'),
8    s15: document.querySelector('.s15'),
9    s20: document.querySelector('.s20')
10  };
11
12  if (!window.speechSynthesis) {
13    console.warn('API Speech não suportada.');
14    if (playButton) playButton.disabled = true;
15    return;
16  }
17
18  // --- Obtém e sanitiza o texto ---
19  function getTextToSpeak() {
20    const elements = document.querySelectorAll('.titulo-speak, .txt-speak');
21    let fullText = '';
22
23    elements.forEach(el => {
24      let text = el.innerText || '';
25      text = text.replace(/\s+/g, ' ').trim();
26      // Sanitização rigorosa (remove emojis, caracteres de controle, etc.)
27      text = text.replace(/[^a-zA-ZáàâãéèêíïóôõöúçñÁÀÂÃÉÈÊÍÏÓÔÕÖÚÇÑ0-9\s.,;:!?()\-"']/g, '');
28      text = text.replace(/\s+/g, ' ').trim();
29      if (text) {
30        fullText += text + ' ';
31      }
32    });
33
34    return fullText.trim();
35  }
36
37  // --- Estado ---
38  let isSpeaking = false;
39  let isPaused = false;
40  let currentRate = 1.0;
41  let currentChunks = [];
42  let currentChunkIndex = 0;
43  let currentUtterance = null;
44
45  // --- Obtém uma voz em português ---
46  function getPortugueseVoice() {
47    const voices = window.speechSynthesis.getVoices();
48    return voices.find(v => v.lang.startsWith('pt')) || null;
49  }
50
51  // --- Função para falar em chunks ---
52  function speakChunks(text, rate) {
53    const chunkSize = 250;
54    const chunks = [];
55    for (let i = 0; i < text.length; i += chunkSize) {
56      chunks.push(text.substring(i, i + chunkSize));
57    }
58    if (chunks.length === 0) return;
59
60    currentChunks = chunks;
61    currentChunkIndex = 0;
62    isSpeaking = true;
63    isPaused = false;
64    if (playButton) playButton.classList.add('pause');
65
66    speakNextChunk(rate);
67  }
68
69  function speakNextChunk(rate) {
70    if (currentChunkIndex >= currentChunks.length) {
71      // Finalizou
72      isSpeaking = false;
73      isPaused = false;
74      if (playButton) playButton.classList.remove('pause');
75      return;
76    }
77
78    const chunk = currentChunks[currentChunkIndex];
79    const utterance = new SpeechSynthesisUtterance(chunk);
80    utterance.rate = rate;
81    utterance.lang = 'pt-BR';
82
83    const voice = getPortugueseVoice();
84    if (voice) utterance.voice = voice;
85
86    utterance.onend = function () {
87      currentChunkIndex++;
88      speakNextChunk(rate);
89    };
90
91    utterance.onerror = function (event) {
92      if (event.error !== 'canceled') {
93        console.error('Erro na síntese:', event.error);
94      }
95      // Em caso de erro, paramos tudo
96      isSpeaking = false;
97      isPaused = false;
98      if (playButton) playButton.classList.remove('pause');
99    };
100
101    currentUtterance = utterance;
102    window.speechSynthesis.speak(utterance);
103  }
104
105  // --- Pausar / retomar ---
106  function pauseSpeech() {
107    if (isSpeaking && !isPaused) {
108      window.speechSynthesis.pause();
109      isPaused = true;
110    }
111  }
112
113  function resumeSpeech() {
114    if (isSpeaking && isPaused) {
115      window.speechSynthesis.resume();
116      isPaused = false;
117    }
118  }
119
120  function cancelSpeech() {
121    if (isSpeaking) {
122      window.speechSynthesis.cancel();
123      isSpeaking = false;
124      isPaused = false;
125      if (playButton) playButton.classList.remove('pause');
126      currentUtterance = null;
127    }
128  }
129
130  // --- Iniciar com nova velocidade ---
131  function restartWithRate(newRate) {
132    const text = getTextToSpeak();
133    if (!text) {
134      console.warn('Nenhum texto válido para ler.');
135      return;
136    }
137    currentRate = newRate;
138    cancelSpeech();
139    // Aguarda um tick para garantir que o cancel foi processado
140    setTimeout(() => {
141      speakChunks(text, newRate);
142    }, 50);
143  }
144
145  // --- Evento Play/Pause ---
146  if (playButton) {
147    playButton.addEventListener('click', function () {
148      const text = getTextToSpeak();
149      if (!text) {
150        console.warn('Nenhum texto válido para ler.');
151        return;
152      }
153
154      if (!isSpeaking) {
155        // Inicia nova leitura
156        speakChunks(text, currentRate);
157        return;
158      }
159
160      // Se já está falando
161      if (isPaused) {
162        resumeSpeech();
163      } else {
164        pauseSpeech();
165      }
166    });
167  }
168
169  // --- Botões de velocidade ---
170  if (speedButtons.s05) speedButtons.s05.addEventListener('click', () => restartWithRate(0.5));
171  if (speedButtons.s10) speedButtons.s10.addEventListener('click', () => restartWithRate(1.0));
172  if (speedButtons.s15) speedButtons.s15.addEventListener('click', () => restartWithRate(1.5));
173  if (speedButtons.s20) speedButtons.s20.addEventListener('click', () => restartWithRate(2.0));
174
175  // --- Limpeza ao sair ---
176  window.addEventListener('beforeunload', function () {
177    if (window.speechSynthesis.speaking) window.speechSynthesis.cancel();
178  });
179
180  console.log('Speech Synthesis carregada com sanitização e chunks.');
181});

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.