Código HTML do Conteúdo
Post: Delivery of €13 million in submarine cables for the Shanhaiguan offshore project
<div>
<div data-elementor-id="27685" data-elementor-post-type="elementor_library" data-elementor-type="single-post">
<section data-element_type="section" data-id="597ed679">
<div>
<p>
document.addEventListener(‘DOMContentLoaded’, function() {<br />
const buttons = document.querySelectorAll(‘.wpcode-tts-button’);<br />
if (!buttons.length || !(‘speechSynthesis’ in window)) return;</p>
<p> const ajaxUrl = ‘https://www.redhotcyber.com/wp-admin/admin-ajax.php’;</p>
<p> function recordAudioView(postId) {<br />
fetch(ajaxUrl, {<br />
method: ‘POST’,<br />
headers: {<br />
‘Content-Type’: ‘application/x-www-form-urlencoded’<br />
},<br />
body: ‘action=wpcode_tts_view&post_id=’ + encodeURIComponent(postId)<br />
})<br />
.then(response => response.json())<br />
.then(data => {<br />
if (!data.success) {<br />
console.error(‘Errore logico AJAX:’, data.data);<br />
}<br />
})<br />
.catch(error => console.error(‘Errore di rete AJAX:’, error));<br />
}</p>
<p> let voices = [];<br />
let voicesLoaded = false;</p>
<p> function loadVoices() {<br />
voices = speechSynthesis.getVoices();<br />
if (voices.length > 0) {<br />
voicesLoaded = true;<br />
}<br />
}</p>
<p> // Tentativo di caricamento iniziale<br />
loadVoices();</p>
<p> // Assicurati che le voci vengano ricaricate quando l’elenco cambia (evento cruciale)<br />
if (speechSynthesis.onvoiceschanged !== undefined) {<br />
speechSynthesis.onvoiceschanged = loadVoices;<br />
}</p>
<p> /**<br />
* Funzione principale per avviare la sintesi vocale, con logica di retry.<br />
* @param {HTMLElement} button L’elemento del pulsante.<br />
* @param {number} attempt Contatore dei tentativi (max 2).<br />
*/<br />
function startSpeaking(button, attempt = 1) {<br />
const text = button.getAttribute(‘data-article’);<br />
const lang = button.getAttribute(‘data-lang’) || ‘it-IT’;<br />
const pref = button.getAttribute(‘data-voice-pref’) || ”;<br />
const rate = parseFloat(button.getAttribute(‘data-rate’)) || 1;<br />
const pitch = parseFloat(button.getAttribute(‘data-pitch’)) || 1;</p>
<p> if (!text) { console.error(‘Testo mancante o vuoto.’); return; }</p>
<p> // *** LOGICA DI RETRY PER IL CARICAMENTO DELLE VOCI ***<br />
if (!voicesLoaded && attempt === 1) {<br />
loadVoices(); // Riprova a caricare<br />
if (!voicesLoaded) {<br />
console.warn(“Voci non caricate al 1° tentativo. Riprovo in 500ms.”);<br />
button.textContent = “⏳ Caricamento vocale… (Riprovo)”;</p>
<p> // Riprova dopo 500ms<br />
setTimeout(() => startSpeaking(button, 2), 500);<br />
return;<br />
}<br />
} else if (!voicesLoaded && attempt === 2) {<br />
console.error(“Voci non caricate al 2° tentativo. L’API TTS non è disponibile.”);<br />
button.textContent = “🔊 API vocale non disponibile ❌”;<br />
setTimeout(() => {<br />
if (button.getAttribute(‘data-speaking’) !== ‘true’) {<br />
button.textContent=”🔊 Ascolta l’articolo”;<br />
}<br />
}, 3000);<br />
return;<br />
}<br />
// *** FINE LOGICA DI RETRY ***</p>
<p> // Cancella eventuali letture precedenti e setta lo stato<br />
speechSynthesis.cancel();<br />
document.querySelectorAll(‘.wpcode-tts-button’).forEach(b => {<br />
if (b !== button) {<br />
b.setAttribute(‘data-speaking’, ‘false’);<br />
b.textContent = “🔊 Ascolta l’articolo”;<br />
}<br />
});</p>
<p> const utter = new SpeechSynthesisUtterance(text);<br />
utter.lang = lang;<br />
utter.rate = rate;<br />
utter.pitch = pitch;</p>
<p> // Selezione voce migliorata<br />
let selectedVoice = null;<br />
if (voices.length) {<br />
const italianVoices = voices.filter(v => v.lang.startsWith(“it”));</p>
<p> // Priorità 1: Voci di alta qualità<br />
if (pref === ‘female’) {<br />
selectedVoice = italianVoices.find(v => v.name.includes(“Google”) && /(it-IT|Italian)/i.test(v.lang) && /(female|femmina|alessia|lucia|carla)/i.test(v.name)) ||<br />
italianVoices.find(v => v.name.includes(“Microsoft”) && /(female|femmina)/i.test(v.name));<br />
} else if (pref === ‘male’) {<br />
selectedVoice = italianVoices.find(v => v.name.includes(“Google”) && /(it-IT|Italian)/i.test(v.lang) && /(male|maschio|luca|paolo)/i.test(v.name)) ||<br />
italianVoices.find(v => v.name.includes(“Microsoft”) && /(male|maschio)/i.test(v.name));<br />
}</p>
<p> // Priorità 2 & 3: Fallback<br />
if (!selectedVoice) {<br />
if (pref === ‘female’) selectedVoice = italianVoices.find(v => /(female|femmina|alessia|lucia|carla)/i.test(v.name));<br />
else if (pref === ‘male’) selectedVoice = italianVoices.find(v => /(male|maschio|luca|paolo|giorgio)/i.test(v.name));<br />
}<br />
if (!selectedVoice && italianVoices.length) selectedVoice = italianVoices[0];<br />
}<br />
if (selectedVoice) utter.voice = selectedVoice;</p>
<p> // Handlers<br />
utter.onstart = function(){ button.setAttribute(‘data-speaking’,’true’); button.textContent=”⏹️ Ferma la lettura”; };<br />
utter.onend = function(){ button.setAttribute(‘data-speaking’,’false’); button.textContent=”🔊 Ascolta l’articolo”; };<br />
utter.onerror = function(e){<br />
console.error(‘TTS error – Codice:’, e.error, ‘dettaglio:’, e);<br />
button.setAttribute(‘data-speaking’,’false’);<br />
//button.textContent=”🔊 Errore di lettura 😞”;<br />
setTimeout(() => {<br />
if (button.getAttribute(‘data-speaking’) !== ‘true’) {<br />
button.textContent=”🔊 Ascolta l’articolo”;<br />
}<br />
}, 3000);<br />
};</p>
<p> speechSynthesis.speak(utter);<br />
button.setAttribute(‘data-speaking’,’true’); // Setta lo stato qui per evitare click multipli<br />
}</p>
<p> buttons.forEach(function(button) {<br />
button.addEventListener(‘click’, function() {<br />
const speaking = button.getAttribute(‘data-speaking’) === ‘true’;</p>
<p> // Se stiamo parlando, ferma e resetta. Non contare il click di stop.<br />
if (speaking) {<br />
speechSynthesis.cancel();<br />
button.setAttribute(‘data-speaking’,’false’);<br />
button.textContent=”🔊 Ascolta l’articolo”;<br />
return;<br />
}</p>
<p> // *** LOGICA DI CONTEGGIO E AVVIO ***</p>
<p> // 1. Registra la vista (se non stiamo già parlando)<br />
const postId = button.getAttribute(‘data-post-id’);<br />
if (postId) {<br />
recordAudioView(postId);<br />
}</p>
<p> // 2. Avvia la sintesi vocale con logica di retry<br />
startSpeaking(button);</p>
<p> // **********************************<br />
});<br />
});<br />
});</p>
<p> const lazyloadRunObserver = () => {<br />
const lazyloadBackgrounds = document.querySelectorAll( `.e-con.e-parent:not(.e-lazyloaded)` );<br />
const lazyloadBackgroundObserver = new IntersectionObserver( ( entries ) => {<br />
entries.forEach( ( entry ) => {<br />
if ( entry.isIntersecting ) {<br />
let lazyloadBackground = entry.target;<br />
if( lazyloadBackground ) {<br />
lazyloadBackground.classList.add( ‘e-lazyloaded’ );<br />
}<br />
lazyloadBackgroundObserver.unobserve( entry.target );<br />
}<br />
});<br />
}, { rootMargin: ‘200px 0px 200px 0px’ } );<br />
lazyloadBackgrounds.forEach( ( lazyloadBackground ) => {<br />
lazyloadBackgroundObserver.observe( lazyloadBackground );<br />
} );<br />
};<br />
const events = [<br />
‘DOMContentLoaded’,<br />
‘elementor/lazyload/observe’,<br />
];<br />
events.forEach( ( event ) => {<br />
document.addEventListener( event, lazyloadRunObserver );<br />
} );
</p>
</div>
</section>
</div></div>