/* widget: Ballonnenwoord */ /* ========================================================= Balloon word widget — guess before the balloons pop ==========================================================*/ function mountBall(root, initState, opts){ const onProgress = opts && opts.onProgress; const S = { level:1, yearGroup:null, managedLearningLevel:false, card:0, snd:true, lives:6, guessed:new Set(), over:false, order:[], idx:0, stars:0 }; if(initState){S.level=initState.level||1;S.yearGroup=initState.yearGroup||null;S.managedLearningLevel=!!initState.managedLearningLevel;S.card = initState.card ?? 0; S.snd = initState.snd !== false;} const assignedRules=languageTaskRules(S.yearGroup,S.level),maxLives=assignedRules?.lives||6; const play = f=>{ if(S.snd) f(); }; root.innerHTML = `
`; const el = s => root.querySelector(s); const srcSel = el(".bl-src"), balloonsEl = el(".bl-balloons"), wordEl = el(".bl-word"), msgEl = el(".bl-msg"), nextBtn = el(".bl-next"), kbdEl = el(".bl-kbd"), scoreEl = el(".bl-score"), sndBtn = el(".lg-snd"); function list(){ S.card = normWordSource(S.card); return languageWordsForYear(wordSourceList(S.card),S.yearGroup,S.level); } function buildSrc(){ fillWordSourceSel(srcSel, S.card); } "abcdefghijklmnopqrstuvwxyz".split("").forEach(l=>{ const b = document.createElement("button"); b.textContent = l; b.dataset.l = l; b.className = VOWELS.includes(l) ? "v" : "c"; b.addEventListener("click", ()=>guess(l, b)); kbdEl.appendChild(b); }); function word(){ return list()[S.order[S.idx]][0]; } function drawWord(reveal){ wordEl.textContent = word().split("").map(l=>(reveal || S.guessed.has(l)) ? l : "_").join(" "); } function drawBalloons(){ balloonsEl.textContent = "🎈".repeat(S.lives) + "💥".repeat(maxLives-S.lives); } function newWord(){ if(!S.order.length || S.idx >= S.order.length){ S.order = shuffle(list().map((_,i)=>i)); S.idx = 0; } S.lives = maxLives; S.guessed = new Set(); S.over = false; msgEl.textContent = ""; msgEl.className = "bl-msg"; nextBtn.classList.remove("show"); kbdEl.querySelectorAll("button").forEach(b=>{ b.disabled = false; b.style.opacity = 1; }); drawWord(false); drawBalloons(); scoreEl.textContent = S.stars ? S.stars+" ★" : ""; } function guess(l, btn){ if(S.over || btn.disabled) return; btn.disabled = true; btn.style.opacity = .3; if(word().includes(l)){ S.guessed.add(l); play(sndGood); drawWord(false); if(word().split("").every(x=>S.guessed.has(x))){ S.over = true; S.stars++; msgEl.textContent = T("ballWin"); msgEl.classList.add("win"); play(sndWin); confetti(el(".mt-card")); nextBtn.classList.add("show"); scoreEl.textContent = S.stars+" ★"; if(onProgress) onProgress({ attempts: 1, correct: 1, stars: 1 }); } }else{ S.lives--; play(sndBad); drawBalloons(); if(S.lives <= 0){ S.over = true; drawWord(true); msgEl.textContent = `${T("ballLose")} ${word()}`; msgEl.classList.add("lose"); nextBtn.classList.add("show"); } } } nextBtn.addEventListener("click", ()=>{ S.idx++; newWord(); }); srcSel.addEventListener("change", ()=>{ S.card = srcSel.value; S.order = []; newWord(); }); sndBtn.addEventListener("click", ()=>{ S.snd = !S.snd; sndBtn.textContent = S.snd ? "🔔" : "🔕"; sndBtn.classList.toggle("off", !S.snd); }); function labels(){ sndBtn.textContent = S.snd ? "🔔" : "🔕"; sndBtn.classList.toggle("off", !S.snd); sndBtn.title = T("sndWidget"); nextBtn.textContent = T("next"); buildSrc(); } document.addEventListener("langchange", ()=>{ S.card=0; S.order=[]; labels(); newWord(); }); document.addEventListener("wordschange", ()=>{ buildSrc(); S.order=[]; newWord(); }); labels(); newWord(); root.classList.toggle("managed-learning-level",S.managedLearningLevel); return { getState: ()=>({card:S.card, snd:S.snd}) }; }