/* widget: Hakken & plakken */ /* Veelvoorkomende spellingpatronen blijven als herkenbare klankgroep bij elkaar. De langste groep wint, zodat bijvoorbeeld 'sch' niet als 's' + 'ch' eindigt. */ function soundChunks(word, lang=LANG){ const patterns=(lang==='nl' ? ['sch','aai','ooi','oei','eeuw','ieuw','ng','nk','ch','aa','ee','oo','uu','oe','ie','ei','ij','ou','au','ui','eu'] : ['tch','igh','sh','ch','th','ph','ng','ee','oo','ea','ou','ow','ai','ay','oi']).sort((a,b)=>b.length-a.length); const chunks=[]; for(let i=0;irest.startsWith(pattern)); const length=match?match.length:1; chunks.push(word.slice(i,i+length)); i+=length; } return chunks; } /* ========================================================= Chop & blend widget — words in sounds, with read-aloud ==========================================================*/ function mountHak(root, initState){ const S = { level:1, yearGroup:null, managedLearningLevel:false, card:0, idx:0, order:[], chopped:false, assignmentWords:null, assignmentThemeName:"" }; if(initState){S.level=initState.level||1;S.yearGroup=initState.yearGroup||null;S.managedLearningLevel=!!initState.managedLearningLevel;S.card = initState.card ?? 0;S.assignmentWords=Array.isArray(initState.assignmentWords)?initState.assignmentWords:null;S.assignmentThemeName=typeof initState.assignmentThemeName==="string"?initState.assignmentThemeName:"";} 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(){ if(S.assignmentWords?.length)return languageWidgetWords(S.assignmentWords,S.yearGroup,S.level,"hak"); S.card = normWordSource(S.card);const selected=languageWidgetWords(wordSourceList(S.card),S.yearGroup,S.level,"hak"); if(selected.length)return selected;S.card="g";return languageWidgetWords(wordSourceList("g"),S.yearGroup,S.level,"hak"); } function buildSrc(){if(S.assignmentWords?.length){srcSel.innerHTML="";srcSel.appendChild(new Option("🎯 "+(S.assignmentThemeName||T("castTheme")),"assignment"));srcSel.disabled=true;}else{srcSel.disabled=false;fillWordSourceSel(srcSel,S.card,S.yearGroup,S.level);S.card=srcSel.value||"g";}} 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,, anchor] = cur(); const letters=languageTaskRules(S.yearGroup,S.level)?.literacyMode==="letters"; wordEl.innerHTML = ""; if(letters&&!S.chopped){ const s=document.createElement("span");s.className="hk-whole";s.textContent="?";wordEl.appendChild(s); }else if(S.chopped){ soundChunks(w).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 " + (/[aeiouyáéíóúäëïöü]/i.test(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); picEl.title = anchor||w; 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", ()=>{ const [word,,,anchor]=cur(),letters=languageTaskRules(S.yearGroup,S.level)?.literacyMode==="letters"; if(letters)speak(anchor||word,0.75); else if(S.chopped)speak(soundChunks(word).join(", "),0.7); else speak(word,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(){ const letters=languageTaskRules(S.yearGroup,S.level)?.literacyMode==="letters"; el(".hk-say").textContent = T("hakSay"); el(".hk-chop").textContent = letters?(LANG==="nl"?"Toon letter":"Show letter"):T("hakChop"); el(".hk-glue").textContent = letters?(LANG==="nl"?"Verberg letter":"Hide letter"):T("hakGlue"); el(".hk-hint").textContent = letters?(LANG==="nl"?"Luister naar het woord. Welke letter hoor je vooraan?":"Listen to the word. Which letter do you hear first?"):T("hakHint"); buildSrc(); } document.addEventListener("langchange", ()=>{ if(!S.assignmentWords)S.card=0; labels(); newRound(); S.chopped=false; display(); }); document.addEventListener("wordschange", ()=>{ buildSrc(); newRound(); S.chopped=false; display(); }); labels(); newRound(); display(); root.classList.toggle("managed-learning-level",S.managedLearningLevel); return { getState: ()=>({card:S.card}) }; }