(function(){
'use strict';
function shuffle(arr){
const a=arr.slice();
for(let i=a.length-1;i>0;i--){
const j=Math.floor(Math.random()*(i+1));
[a[i],a[j]]=[a[j],a[i]];
}
return a;
}
function speakText(text){
if(!text||!('speechSynthesis' in window)) return;
window.speechSynthesis.cancel();
const utter=new SpeechSynthesisUtterance(text);
utter.lang='en-GB';
window.speechSynthesis.speak(utter);
}
document.querySelectorAll('[data-efz-app]').forEach(function(app){
let progress=0;
const progressBar=app.querySelector('.efz-progress-bar');
const progressText=app.querySelector('.efz-progress-text');
const panels=Array.from(app.querySelectorAll('[data-panel]'));
const select=app.querySelector('.efz-nav-select');
const pills=Array.from(app.querySelectorAll('.efz-topic-pill'));
const carouselTrack=app.querySelector('[data-efz-carousel-track]');
function setProgress(value){
progress=Math.max(progress, Math.min(100, value));
if(progressBar) progressBar.style.width=progress + '%';
if(progressText) progressText.textContent=Math.round(progress) + '%';
}
function bumpProgress(delta){ setProgress(progress + delta); }
function showPanel(id){
panels.forEach(function(panel){
const active=panel.id===id;
panel.hidden = !active;
panel.classList.toggle('is-visible', active);
});
pills.forEach(function(btn){
const active=btn.dataset.target===id;
btn.classList.toggle('is-active', active);
if(active&&carouselTrack){
btn.scrollIntoView({behavior:'smooth', inline:'center', block:'nearest'});
}});
if(select) select.value=id;
}
if(select) select.addEventListener('change', function(){ showPanel(this.value); });
pills.forEach(function(btn){ btn.addEventListener('click', function(){ showPanel(this.dataset.target); });});
app.querySelectorAll('.efz-speak').forEach(function(btn){
btn.addEventListener('click', function(){ speakText(this.getAttribute('data-speak')||''); });
});
app.querySelectorAll('.efz-carousel-prev').forEach(function(btn){
btn.addEventListener('click', function(){ if(carouselTrack) carouselTrack.scrollBy({left:-260, behavior:'smooth'});});
});
app.querySelectorAll('.efz-carousel-next').forEach(function(btn){
btn.addEventListener('click', function(){ if(carouselTrack) carouselTrack.scrollBy({left:260, behavior:'smooth'});});
});
app.querySelectorAll('[data-drag-activity]').forEach(function(activity){
const bank=activity.querySelector('.efz-token-bank');
const slots=Array.from(activity.querySelectorAll('.efz-drop-slot'));
const feedback=activity.querySelector('.efz-feedback');
const initialTokens=Array.from(bank.querySelectorAll('.efz-token')).map(btn=> btn.textContent);
let dragged=null;
function clearFeedback(){
feedback.textContent='';
feedback.classList.remove('is-good', 'is-bad');
}
function makeToken(text){
const btn=document.createElement('button');
btn.type='button';
btn.className='efz-token';
btn.draggable=true;
btn.textContent=text;
bindDrag(btn);
return btn;
}
function bindDrag(el){
el.addEventListener('dragstart', function(){ dragged=el; });
el.addEventListener('click', function(){ dragged=el; });
}
function placeToken(slot){
if(dragged&&!slot.querySelector('.efz-token')){
slot.appendChild(dragged);
dragged=null;
clearFeedback();
}}
bank.querySelectorAll('.efz-token').forEach(bindDrag);
slots.forEach(function(slot){
slot.addEventListener('dragover', function(e){ e.preventDefault(); });
slot.addEventListener('click', function(){ placeToken(slot); });
slot.addEventListener('drop', function(e){ e.preventDefault(); placeToken(slot); });
});
bank.addEventListener('dragover', function(e){ e.preventDefault(); });
bank.addEventListener('drop', function(e){
if(dragged){
e.preventDefault();
bank.appendChild(dragged);
dragged=null;
clearFeedback();
}});
activity.querySelector('.efz-check-order').addEventListener('click', function(){
const answer=slots.map(slot=> slot.textContent.trim());
let expected=[];
try { expected=JSON.parse(bank.getAttribute('data-answer')||'[]'); } catch(err){}
const ok=answer.join('|')===expected.join('|');
feedback.textContent=ok ? 'Great job! 🎉':'Try again 😊';
feedback.classList.toggle('is-good', ok);
feedback.classList.toggle('is-bad', !ok);
if(ok) bumpProgress(6);
});
activity.querySelector('.efz-reset-order').addEventListener('click', function(){
clearFeedback();
bank.innerHTML='';
shuffle(initialTokens).forEach(text=> bank.appendChild(makeToken(text)));
slots.forEach(slot=> { slot.innerHTML=''; });
dragged=null;
});
});
app.querySelectorAll('.efz-match-game').forEach(function(game){
const leftCol=game.querySelector('.efz-left-col');
const rightCol=game.querySelector('.efz-right-col');
const status=game.querySelector('.efz-match-status');
const feedback=app.querySelector('.efz-match-feedback');
const batchNum=game.querySelector('.efz-batch-number');
const matchedNow=game.querySelector('.efz-matched-now');
const matchedTotal=game.querySelector('.efz-matched-total');
const resetBtn=app.querySelector('.efz-reset-match');
let pairs=[];
try { pairs=JSON.parse(game.getAttribute('data-match')||'[]'); } catch(err){ pairs=[]; }
let currentBatch=0;
let selectedLeft=null;
let solvedInBatch=0;
let loadingNext=false;
function getBatch(){
return pairs.slice(currentBatch * 4, currentBatch * 4 + 4);
}
function renderBatch(){
const batch=getBatch();
selectedLeft=null;
solvedInBatch=0;
loadingNext=false;
leftCol.innerHTML='';
rightCol.innerHTML='';
if(!batch.length){
if(batchNum) batchNum.textContent=String(Math.max(1, Math.ceil(pairs.length / 4)));
if(matchedNow) matchedNow.textContent='0';
if(matchedTotal) matchedTotal.textContent='0';
leftCol.innerHTML='<div class="efz-end-message">Amazing! You finished all matching questions. 🏆</div>';
rightCol.innerHTML='<div class="efz-end-message">Fantastic work! 🌟</div>';
status.textContent='All sets completed.';
if(feedback) feedback.textContent='Wonderful work!';
setProgress(100);
return;
}
if(batchNum) batchNum.textContent=String(currentBatch + 1);
if(matchedNow) matchedNow.textContent='0';
if(matchedTotal) matchedTotal.textContent=String(batch.length);
status.textContent='Match all ' + batch.length + ' pairs.';
if(feedback) feedback.textContent='';
batch.forEach(function(pair, index){
const left=document.createElement('button');
left.type='button';
left.className='efz-match-item';
left.textContent=pair.left;
left.dataset.index=String(index);
left.dataset.side='left';
leftCol.appendChild(left);
});
shuffle(batch.map((pair, index)=> ({ pair, index }))).forEach(function(item){
const right=document.createElement('button');
right.type='button';
right.className='efz-match-item';
right.dataset.index=String(item.index);
right.dataset.side='right';
right.innerHTML='<span>' + item.pair.right + '</span>' + (item.pair.hint ? '<small>' + item.pair.hint + '</small>':'');
rightCol.appendChild(right);
});
}
game.addEventListener('click', function(e){
const btn=e.target.closest('.efz-match-item');
if(!btn||btn.classList.contains('is-done')||loadingNext) return;
if(btn.dataset.side==='left'){
game.querySelectorAll('.efz-match-item[data-side="left"]').forEach(x=> x.classList.remove('is-active'));
btn.classList.add('is-active');
selectedLeft=btn;
status.textContent='Now choose the matching answer on the right.';
return;
}
if(!selectedLeft){
status.textContent='Choose an item on the left first.';
return;
}
const ok=selectedLeft.dataset.index===btn.dataset.index;
if(!ok){
status.textContent='Not quite. Try another answer.';
btn.classList.add('is-shake');
setTimeout(function(){ btn.classList.remove('is-shake'); }, 350);
return;
}
selectedLeft.classList.remove('is-active');
selectedLeft.classList.add('is-done');
btn.classList.add('is-done');
selectedLeft.disabled=true;
btn.disabled=true;
selectedLeft=null;
solvedInBatch +=1;
if(matchedNow) matchedNow.textContent=String(solvedInBatch);
status.textContent='Correct!';
bumpProgress(4);
const batchSize=getBatch().length;
if(solvedInBatch >=batchSize){
loadingNext=true;
status.textContent='Great set! Loading the next 4...';
setTimeout(function(){
currentBatch +=1;
renderBatch();
}, 650);
}});
if(resetBtn){
resetBtn.addEventListener('click', function(){
currentBatch=0;
selectedLeft=null;
solvedInBatch=0;
loadingNext=false;
renderBatch();
});
}
renderBatch();
});
if(panels.length){
showPanel((select&&select.value)||panels[0].id);
}});
})();