/* widget: Hakken & plakken */ /* ========================================================= Chop & blend widget — words in sounds, with read-aloud ==========================================================*/ function mountHak(root, initState){ const S = { card:0, idx:0, order:[], chopped:false }; if(initState) S.card = initState.card ?? 0; const canSpeak = "speechSynthesis" in window; root.innerHTML = `
`; const el = s => root.querySelector(s); const srcSel = el(".hk-src"), wordEl = el(".hk-word"), picEl = el(".hk-pic"), cntEl = el(".hk-cnt"); function list(){ S.card = normWordSource(S.card); return wordSourceList(S.card); } function buildSrc(){ fillWordSourceSel(srcSel, S.card); } function cur(){ return list()[S.order[S.idx]]; } function newRound(){ S.order = shuffle(list().map((_,i)=>i)); S.idx = 0; } function display(){ const [w, pic] = cur(); wordEl.innerHTML = ""; if(S.chopped){ w.split("").forEach((l,i)=>{ if(i>0){ const sep = document.createElement("span"); sep.className = "hk-sep"; sep.textContent = "–"; wordEl.appendChild(sep); } const s = document.createElement("span"); s.className = "hk-l " + (VOWELS.includes(l) ? "v" : "c"); s.textContent = l; s.style.animationDelay = (i*0.08)+"s"; wordEl.appendChild(s); }); }else{ const s = document.createElement("span"); s.className = "hk-whole"; s.textContent = w; wordEl.appendChild(s); } setPic(picEl, pic); cntEl.textContent = `${T("word")} ${S.idx+1}/${S.order.length}`; } function speak(text, rate){ if(!canSpeak) return; speechSynthesis.cancel(); const u = new SpeechSynthesisUtterance(text); u.lang = LANG==="nl" ? "nl-NL" : "en-GB"; u.rate = rate; speechSynthesis.speak(u); } el(".hk-say").addEventListener("click", ()=>{ if(S.chopped) speak(cur()[0].split("").join(", "), 0.7); else speak(cur()[0], 0.85); }); el(".hk-chop").addEventListener("click", ()=>{ S.chopped = true; display(); play(sndGood); }); el(".hk-glue").addEventListener("click", ()=>{ S.chopped = false; display(); play(sndGood); }); el(".hk-next").addEventListener("click", ()=>{ S.idx++; if(S.idx >= S.order.length) newRound(); S.chopped = false; display(); }); const play = f=>f(); srcSel.addEventListener("change", ()=>{ S.card = srcSel.value; newRound(); S.chopped = false; display(); }); function labels(){ el(".hk-say").textContent = T("hakSay"); el(".hk-chop").textContent = T("hakChop"); el(".hk-glue").textContent = T("hakGlue"); buildSrc(); } document.addEventListener("langchange", ()=>{ S.card=0; labels(); newRound(); S.chopped=false; display(); }); document.addEventListener("wordschange", ()=>{ buildSrc(); newRound(); S.chopped=false; display(); }); labels(); newRound(); display(); return { getState: ()=>({card:S.card}) }; }