(function(){
'use strict';
var speechReady=false;
var cachedVoices=[];
function parseConfig(root){
var node=root.querySelector('.emh-wbg-config');
if(!node) return null;
try { return JSON.parse(node.textContent||'{}'); } catch(e){ return null; }}
function getVoices(){
try {
cachedVoices=window.speechSynthesis ? (window.speechSynthesis.getVoices()||[]):[];
} catch(e){
cachedVoices=[];
}
return cachedVoices;
}
function normalizeLang(lang){
return String(lang||'').toLowerCase().replace('_','-');
}
function pickVoice(voiceLang){
var voices=getVoices();
var wanted=normalizeLang(voiceLang||'en-GB');
var exact=null, partial=null, english=null;
for(var i=0;i<voices.length;i++){
var v=voices[i];
var lang=normalizeLang(v.lang);
if(lang===wanted){ exact=v; break; }
if(!partial&&lang.indexOf(wanted.split('-')[0])===0){ partial=v; }
if(!english&&lang.indexOf('en')===0){ english=v; }}
return exact||partial||english||null;
}
function unlockSpeech(){
if(!window.speechSynthesis) return;
speechReady=true;
getVoices();
try {
var probe=new SpeechSynthesisUtterance('');
probe.volume=0;
probe.rate=1;
probe.pitch=1;
window.speechSynthesis.speak(probe);
window.speechSynthesis.cancel();
} catch(e){}}
function stopSpeech(){
try {
if(window.speechSynthesis){
window.speechSynthesis.cancel();
}} catch(e){}}
function speak(text, voiceLang){
if(!window.speechSynthesis||!text) return;
getVoices();
stopSpeech();
var utter=new SpeechSynthesisUtterance(String(text));
utter.lang=voiceLang||'en-GB';
utter.rate=0.9;
utter.pitch=1;
var voice=pickVoice(utter.lang);
if(voice){
utter.voice=voice;
utter.lang=voice.lang||utter.lang;
}
var doSpeak=function(){
try { window.speechSynthesis.resume(); } catch(e){}
try { window.speechSynthesis.speak(utter); } catch(e){}};
if(!speechReady){
unlockSpeech();
setTimeout(doSpeak, 30);
return;
}
setTimeout(doSpeak, 30);
}
function shuffle(arr){
for(var i=arr.length-1;i>0;i--){
var j=Math.floor(Math.random()*(i+1));
var t=arr[i]; arr[i]=arr[j]; arr[j]=t;
}
return arr;
}
function WordBuilder(root){
this.root=root;
this.wrap=root.closest('.emh-wbg-wrap')||root.parentNode;
this.config=parseConfig(root)||{settings:{voice:'en-GB'}, initialGame:{ok:false,message:'Game data could not load.'}};
this.settings=this.config.settings||{};
this.game=this.config.initialGame||{ok:false,message:'No game data.'};
this.items=[];
this.index=0;
this.stars=0;
this.seenWords=[];
this.roundEl=root.querySelector('.emh-wbg-round');
this.totalEl=root.querySelector('.emh-wbg-total');
this.starsEl=root.querySelector('.emh-wbg-stars');
this.imageEl=root.querySelector('.emh-wbg-image');
this.emojiEl=root.querySelector('.emh-wbg-emoji');
this.categoryEl=root.querySelector('.emh-wbg-category');
this.dropzoneEl=root.querySelector('.emh-wbg-dropzone');
this.bankEl=root.querySelector('.emh-wbg-bank');
this.audioBtn=root.querySelector('.emh-wbg-audio');
this.fullscreenBtn=root.querySelector('.emh-wbg-fullscreen');
this.resetBtn=root.querySelector('.emh-wbg-reset');
this.checkBtn=root.querySelector('.emh-wbg-check');
this.nextBtn=root.querySelector('.emh-wbg-next');
this.playAgainBtn=root.querySelector('.emh-wbg-playagain');
this.messageEl=root.querySelector('.emh-wbg-message');
this.sentenceEl=root.querySelector('.emh-wbg-sentence');
this.confettiWrap=document.createElement('div');
this.confettiWrap.className='emh-wbg-confetti';
root.appendChild(this.confettiWrap);
if(this.imageEl){ this.imageEl.addEventListener('error', function(){ this.style.display='none'; this.removeAttribute('src'); });}
this.dragSource=null;
this.bind();
this.load(this.game);
this.syncFullscreenButton();
}
WordBuilder.prototype.bind=function(){
var self=this;
if(this.audioBtn) this.audioBtn.addEventListener('click', function(){ unlockSpeech(); self.playWord(); });
if(this.fullscreenBtn) this.fullscreenBtn.addEventListener('click', function(){ self.toggleFullscreen(); });
if(this.resetBtn) this.resetBtn.addEventListener('click', function(){ self.renderLetters(self.currentItem()); self.setMessage('Letters reset. Try again!', ''); });
if(this.checkBtn) this.checkBtn.addEventListener('click', function(){ self.checkAnswer(); });
if(this.nextBtn) this.nextBtn.addEventListener('click', function(){ self.next(); });
if(this.playAgainBtn) this.playAgainBtn.addEventListener('click', function(){ self.fetchFreshGame(); });
['fullscreenchange','webkitfullscreenchange','mozfullscreenchange','MSFullscreenChange'].forEach(function(evt){
document.addEventListener(evt, function(){ self.syncFullscreenButton(); });
});
['dragenter','dragover'].forEach(function(name){
self.dropzoneEl.addEventListener(name, function(e){ e.preventDefault(); self.dropzoneEl.classList.add('is-over'); });
self.bankEl.addEventListener(name, function(e){ e.preventDefault(); });
});
['dragleave','drop'].forEach(function(name){
self.dropzoneEl.addEventListener(name, function(){ self.dropzoneEl.classList.remove('is-over'); });
});
this.dropzoneEl.addEventListener('drop', function(e){ e.preventDefault(); self.moveDraggedTo(self.dropzoneEl); });
this.bankEl.addEventListener('drop', function(e){ e.preventDefault(); self.moveDraggedTo(self.bankEl); });
};
WordBuilder.prototype.currentItem=function(){ return this.items[this.index]||null; };
WordBuilder.prototype.load=function(game){
if(!game||!game.ok||!game.items||!game.items.length){
this.items=[];
this.roundEl.textContent='0';
this.totalEl.textContent='0';
this.imageEl.removeAttribute('src');
this.emojiEl.textContent='😕';
this.categoryEl.textContent='';
this.dropzoneEl.innerHTML='<div class="emh-wbg-placeholder">'+ (game&&game.message ? game.message:'No words found yet.') +'</div>';
this.bankEl.innerHTML='';
this.nextBtn.hidden=true;
this.playAgainBtn.hidden=false;
this.setMessage(game&&game.message ? game.message:'No words found yet.', 'is-wrong');
return;
}
this.items=game.items.slice();
for(var i=0;i<this.items.length;i++) this.seenWords.push(this.items[i].word);
this.index=0;
this.stars=0;
this.starsEl.textContent='0';
this.totalEl.textContent=String(this.items.length);
this.nextBtn.hidden=false;
this.playAgainBtn.hidden=true;
this.renderCurrent();
};
WordBuilder.prototype.renderCurrent=function(){
var item=this.currentItem();
if(!item){ this.finish(); return; }
this.roundEl.textContent=String(this.index + 1);
if(item.image){
this.imageEl.style.display='block';
this.imageEl.src=item.image;
}else{
this.imageEl.style.display='none';
this.imageEl.removeAttribute('src');
}
this.imageEl.alt='';
this.emojiEl.textContent=item.emoji||'✨';
this.categoryEl.textContent=item.category ? ('Category: ' + item.category):'Build the word';
this.renderLetters(item);
this.setMessage('Build the word from the letters!', '');
this.sentenceEl.textContent='';
this.nextBtn.hidden=false;
this.playAgainBtn.hidden=true;
};
WordBuilder.prototype.renderLetters=function(item){
var self=this;
this.dropzoneEl.innerHTML='<div class="emh-wbg-placeholder">Drop or tap letters here</div>';
this.bankEl.innerHTML='';
var letters=(item&&item.letters ? item.letters.slice():[]);
if(!letters.length&&item&&item.word){ letters=shuffle(item.word.split('')); }
letters.forEach(function(ch, idx){ self.bankEl.appendChild(self.makeTile(ch, idx)); });
};
WordBuilder.prototype.makeTile=function(ch, idx){
var self=this;
var btn=document.createElement('button');
btn.type='button';
btn.className='emh-wbg-letter';
btn.textContent=ch;
btn.setAttribute('draggable', 'true');
btn.dataset.letter=ch;
btn.dataset.key=String(Date.now()) + '_' + String(Math.random()).slice(2) + '_' + idx;
btn.addEventListener('click', function(){ self.toggleTile(btn); });
btn.addEventListener('dragstart', function(e){ self.dragSource=btn; try{ e.dataTransfer.setData('text/plain', btn.dataset.key||ch); }catch(err){}});
btn.addEventListener('dragend', function(){ self.dragSource=null; });
return btn;
};
WordBuilder.prototype.toggleTile=function(tile){
if(tile.parentNode===this.bankEl){
this.appendTile(this.dropzoneEl, tile);
}else{
this.appendTile(this.bankEl, tile);
}};
WordBuilder.prototype.appendTile=function(target, tile){
var placeholder=this.dropzoneEl.querySelector('.emh-wbg-placeholder');
if(target===this.dropzoneEl&&placeholder) placeholder.remove();
target.appendChild(tile);
tile.classList.toggle('in-dropzone', target===this.dropzoneEl);
if(target!==this.dropzoneEl&&!this.dropzoneEl.querySelector('.emh-wbg-letter')){
this.dropzoneEl.innerHTML='<div class="emh-wbg-placeholder">Drop or tap letters here</div>';
}};
WordBuilder.prototype.moveDraggedTo=function(target){
if(!this.dragSource) return;
this.appendTile(target, this.dragSource);
};
WordBuilder.prototype.answer=function(){
var letters=this.dropzoneEl.querySelectorAll('.emh-wbg-letter');
var str='';
for(var i=0;i<letters.length;i++) str +=(letters[i].dataset.letter||letters[i].textContent||'');
return str.toLowerCase();
};
WordBuilder.prototype.checkAnswer=function(){
var item=this.currentItem();
if(!item) return;
var answer=this.answer();
if(answer===item.word.toLowerCase()){
this.stars +=1;
this.starsEl.textContent=String(this.stars);
this.setMessage('Great job! You built ' + item.word.toUpperCase() + '!', 'is-correct');
if(this.settings.show_confetti){ this.confetti(); }
this.playWord();
this.sentenceEl.textContent='';
}else{
this.setMessage('Almost! Try again.', 'is-wrong');
}};
WordBuilder.prototype.setMessage=function(text, klass){
this.messageEl.className='emh-wbg-message' + (klass ? (' ' + klass):'');
this.messageEl.textContent=text||'';
};
WordBuilder.prototype.playWord=function(){
var item=this.currentItem();
if(item&&item.word) speak(item.word, this.settings.voice||'en-GB');
};
WordBuilder.prototype.toggleFullscreen=function(){
var el=this.wrap||this.root;
if(!el) return;
var fullscreenEl=document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement;
if(fullscreenEl){
if(document.exitFullscreen){ document.exitFullscreen(); }
else if(document.webkitExitFullscreen){ document.webkitExitFullscreen(); }
else if(document.mozCancelFullScreen){ document.mozCancelFullScreen(); }
else if(document.msExitFullscreen){ document.msExitFullscreen(); }
return;
}
if(el.requestFullscreen){ el.requestFullscreen(); }
else if(el.webkitRequestFullscreen){ el.webkitRequestFullscreen(); }
else if(el.mozRequestFullScreen){ el.mozRequestFullScreen(); }
else if(el.msRequestFullscreen){ el.msRequestFullscreen(); }};
WordBuilder.prototype.syncFullscreenButton=function(){
if(!this.fullscreenBtn) return;
var fullscreenEl=document.fullscreenElement||document.webkitFullscreenElement||document.mozFullScreenElement||document.msFullscreenElement;
var active = !!fullscreenEl;
if(this.wrap){ this.wrap.classList.toggle('is-fullscreen', active&&fullscreenEl===this.wrap); }
this.fullscreenBtn.textContent=active ? '🡼 Exit Fullscreen':'⛶ Fullscreen';
};
WordBuilder.prototype.next=function(){
if(this.index < this.items.length - 1){
this.index +=1;
this.renderCurrent();
}else{
this.finish();
}};
WordBuilder.prototype.finish=function(){
this.roundEl.textContent=String(this.items.length||0);
this.setMessage('Amazing! Game completed. Tap Play Again for new words!', 'is-done');
this.nextBtn.hidden=true;
this.playAgainBtn.hidden=false;
this.sentenceEl.textContent='';
};
WordBuilder.prototype.fetchFreshGame=function(){
var self=this;
this.setMessage('Loading a fresh game...', '');
stopSpeech();
var body=new URLSearchParams();
body.append('action', 'emh_wbg_new_game');
body.append('nonce', this.config.nonce||'');
for(var i=0;i<this.seenWords.length;i++) body.append('exclude[]', this.seenWords[i]);
fetch(this.config.ajax_url, {
method: 'POST',
credentials: 'same-origin',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Cache-Control': 'no-cache, no-store, must-revalidate',
'Pragma': 'no-cache'
},
body: body.toString(),
cache: 'no-store'
}).then(function(r){ return r.json(); }).then(function(data){
if(!data.ok&&self.seenWords.length){
self.seenWords=[];
return self.fetchFreshGame();
}
self.load(data);
}).catch(function(){
self.setMessage('Could not load a new game. Please try again.', 'is-wrong');
});
};
WordBuilder.prototype.confetti=function(){
var self=this;
this.confettiWrap.innerHTML='';
var shapes=['⭐','✨','🎉','🌈','💫'];
for(var i=0;i<14;i++){
var el=document.createElement('i');
el.textContent=shapes[i % shapes.length];
el.style.left=(5 + Math.random()*90) + '%';
el.style.animationDelay=(Math.random()*0.25) + 's';
this.confettiWrap.appendChild(el);
}
setTimeout(function(){ self.confettiWrap.innerHTML=''; }, 1500);
};
document.addEventListener('DOMContentLoaded', function(){
getVoices();
if(window.speechSynthesis&&typeof window.speechSynthesis.onvoiceschanged!=='undefined'){
window.speechSynthesis.onvoiceschanged=getVoices;
}
['touchstart','touchend','click'].forEach(function(evt){
document.addEventListener(evt, unlockSpeech, { once: true, passive: true });
});
var roots=document.querySelectorAll('[data-game-root="1"]');
for(var i=0;i<roots.length;i++) new WordBuilder(roots[i]);
});
})();