(function (){
'use strict';
function $(sel, root){
return (root||document).querySelector(sel);
}
function createEl(tag, attrs, html){
var el=document.createElement(tag);
if(attrs){
Object.keys(attrs).forEach(function (k){
if(k==='class') el.className=attrs[k];
else if(k==='style') el.setAttribute('style', attrs[k]);
else el.setAttribute(k, attrs[k]);
});
}
if(typeof html==='string') el.innerHTML=html;
return el;
}
function isEditableTarget(target){
if(!target) return false;
var tag=(target.tagName||'').toLowerCase();
return (
tag==='input' ||
tag==='textarea' ||
target.isContentEditable===true ||
!!target.closest('[contenteditable="true"]')
);
}
function getSelectedText(){
var sel=window.getSelection&&window.getSelection();
if(!sel) return '';
var text=(sel.toString()||'').trim();
text=text.replace(/\s+/g, ' ');
return text;
}
function clearHighlights(){
var marks=document.querySelectorAll('span.emh-dict-highlight');
if(!marks||!marks.length) return;
Array.prototype.forEach.call(marks, function (m){
var parent=m.parentNode;
if(!parent) return;
while (m.firstChild) parent.insertBefore(m.firstChild, m);
parent.removeChild(m);
parent.normalize&&parent.normalize();
});
}
function highlightRange(range){
if(!range) return;
try {
var span=document.createElement('span');
span.className='emh-dict-highlight';
if(range.surroundContents){
range.surroundContents(span);
return;
}} catch (e){
}
try {
var span2=document.createElement('span');
span2.className='emh-dict-highlight';
var frag=range.extractContents();
span2.appendChild(frag);
range.insertNode(span2);
} catch (e2){}}
function getWordAndRangeAtPoint(x, y){
var caretRange;
if(document.caretRangeFromPoint){
caretRange=document.caretRangeFromPoint(x, y);
}else if(document.caretPositionFromPoint){
var pos=document.caretPositionFromPoint(x, y);
if(pos){
caretRange=document.createRange();
caretRange.setStart(pos.offsetNode, pos.offset);
caretRange.setEnd(pos.offsetNode, pos.offset);
}}
if(!caretRange) return { word: '', range: null };
var node=caretRange.startContainer;
if(!node||node.nodeType!==3) return { word: '', range: null };
var text=node.textContent||'';
var idx=caretRange.startOffset;
if(idx < 0||idx > text.length) return { word: '', range: null };
var isWordChar=function (ch){
return /[\p{L}\p{M}\p{N}'’-]/u.test(ch);
};
var left=idx;
var right=idx;
while (left > 0&&isWordChar(text.charAt(left - 1))) left--;
while (right < text.length&&isWordChar(text.charAt(right))) right++;
var word=text.slice(left, right).trim();
word=word.replace(/^[^\p{L}\p{M}\p{N}]+|[^\p{L}\p{M}\p{N}]+$/gu, '');
if(!word) return { word: '', range: null };
var r=document.createRange();
r.setStart(node, left);
r.setEnd(node, right);
return { word: word, range: r };}
function getWordAtPoint(x, y){
var range;
if(document.caretPositionFromPoint){
var pos=document.caretPositionFromPoint(x, y);
if(pos){
range=document.createRange();
range.setStart(pos.offsetNode, pos.offset);
}}else if(document.caretRangeFromPoint){
range=document.caretRangeFromPoint(x, y);
}
if(!range) return '';
var node=range.startContainer;
if(!node||node.nodeType!==3) return '';
var text=node.textContent||'';
var idx=range.startOffset;
if(idx < 0||idx > text.length) return '';
var left=idx;
var right=idx;
var isWordChar=function (ch){
return /[\p{L}\p{M}\p{N}'’-]/u.test(ch);
};
while (left > 0&&isWordChar(text.charAt(left - 1))) left--;
while (right < text.length&&isWordChar(text.charAt(right))) right++;
var word=text.slice(left, right).trim();
word=word.replace(/^[^\p{L}\p{M}\p{N}]+|[^\p{L}\p{M}\p{N}]+$/gu, '');
return word;
}
function getScriptAttributes(){
var script=document.querySelector('script.wlinit');
if(!script) return {};
var get=function (k){
return script.getAttribute(k);
};
return {
autoload: get('data-autoload')==='1',
enable: get('data-enable')==='1'||window.localStorage.getItem('wl_ctc_enabled')==='1',
renderButton: get('data-renderButton')==='1',
ajaxUrl: get('data-ajaxUrl')||'',
nonce: get('data-nonce')||'',
targetLang: (get('data-targetLang')||'en'),
maxWords: parseInt(get('data-maxWords')||'100', 10)||100,
rateLimitPerMinute: parseInt(get('data-rateLimitPerMinute')||'0', 10)||0,
};}
function rateLimitCheck(limitPerMin){
limitPerMin=parseInt(limitPerMin||0, 10)||0;
if(limitPerMin <=0) return { ok: true, count: 0, limit: 0 };
var now=Date.now();
var key='emhds_dict_rl_v1';
var arr=[];
try {
arr=JSON.parse(window.localStorage.getItem(key)||'[]');
if(!Array.isArray(arr)) arr=[];
} catch (e){
arr=[];
}
var cutoff=now - 60 * 1000;
arr=arr.filter(function (t){ return typeof t==='number'&&t >=cutoff; });
var nextCount=arr.length + 1;
if(nextCount > limitPerMin){
try { window.localStorage.setItem(key, JSON.stringify(arr.slice(-200))); } catch (e){}
return { ok: false, count: arr.length, limit: limitPerMin };}
arr.push(now);
try { window.localStorage.setItem(key, JSON.stringify(arr.slice(-200))); } catch (e){}
return { ok: true, count: nextCount, limit: limitPerMin };}
function countWords(text){
var cleaned=String(text||'').trim().replace(/\s+/g, ' ');
if(!cleaned) return 0;
return cleaned.split(' ').filter(Boolean).length;
}
var LANGS_FALLBACK=[
['en','English'],['af','Afrikaans'],['sq','Albanian'],['am','Amharic'],['ar','Arabic'],['hy','Armenian'],['as','Assamese'],['ay','Aymara'],['az','Azerbaijani'],
['bm','Bambara'],['eu','Basque'],['be','Belarusian'],['bn','Bengali'],['bho','Bhojpuri'],['bs','Bosnian'],['bg','Bulgarian'],['ca','Catalan'],['ceb','Cebuano'],
['ny','Chichewa'],['zh-CN','Chinese (Simplified)'],['zh-TW','Chinese (Traditional)'],['co','Corsican'],['hr','Croatian'],['cs','Czech'],['da','Danish'],['dv','Dhivehi'],
['doi','Dogri'],['nl','Dutch'],['eo','Esperanto'],['et','Estonian'],['ee','Ewe'],['fil','Filipino'],['tl','Tagalog'],['fi','Finnish'],['fr','French'],['fy','Frisian'],
['gl','Galician'],['ka','Georgian'],['de','German'],['el','Greek'],['gn','Guarani'],['gu','Gujarati'],['ht','Haitian Creole'],['ha','Hausa'],['haw','Hawaiian'],['he','Hebrew'],
['hi','Hindi'],['hmn','Hmong'],['hu','Hungarian'],['is','Icelandic'],['ig','Igbo'],['ilo','Ilocano'],['id','Indonesian'],['ga','Irish'],['it','Italian'],['ja','Japanese'],
['jv','Javanese'],['kn','Kannada'],['kk','Kazakh'],['km','Khmer'],['rw','Kinyarwanda'],['gom','Konkani'],['ko','Korean'],['kri','Krio'],['ku','Kurdish (Kurmanji)'],
['ckb','Kurdish (Sorani)'],['ky','Kyrgyz'],['lo','Lao'],['la','Latin'],['lv','Latvian'],['ln','Lingala'],['lt','Lithuanian'],['lg','Luganda'],['lb','Luxembourgish'],['mk','Macedonian'],
['mai','Maithili'],['mg','Malagasy'],['ms','Malay'],['ml','Malayalam'],['mt','Maltese'],['mi','Maori'],['mr','Marathi'],['mni-Mtei','Meiteilon (Manipuri)'],['lus','Mizo'],['mn','Mongolian'],
['my','Myanmar (Burmese)'],['ne','Nepali'],['no','Norwegian'],['or','Odia (Oriya)'],['om','Oromo'],['ps','Pashto'],['fa','Persian'],['pl','Polish'],['pt','Portuguese'],['pa','Punjabi'],
['qu','Quechua'],['ro','Romanian'],['ru','Russian'],['sm','Samoan'],['sa','Sanskrit'],['gd','Scots Gaelic'],['nso','Sepedi'],['sr','Serbian'],['st','Sesotho'],['sn','Shona'],
['sd','Sindhi'],['si','Sinhala'],['sk','Slovak'],['sl','Slovenian'],['so','Somali'],['es','Spanish'],['su','Sundanese'],['sw','Swahili'],['sv','Swedish'],['tg','Tajik'],
['ta','Tamil'],['tt','Tatar'],['te','Telugu'],['th','Thai'],['ti','Tigrinya'],['ts','Tsonga'],['tr','Turkish'],['tk','Turkmen'],['ak','Twi (Akan)'],['uk','Ukrainian'],['ur','Urdu'],
['ug','Uyghur'],['uz','Uzbek'],['vi','Vietnamese'],['cy','Welsh'],['xh','Xhosa'],['yi','Yiddish'],['yo','Yoruba'],['zu','Zulu']
];
function normLangCode(code){
code=String(code||'').trim();
if(!code) return 'en';
code=code.replace('_', '-');
var low=code.toLowerCase();
if(/^[a-z]{2,3}(-[a-z]{2,4})?$/.test(low)) return code;
return (low.split('-')[0]||'en');
}
function posClass(pos){
var p=String(pos||'').toLowerCase();
if(p.indexOf('noun')===0) return 'wl-pos--noun';
if(p.indexOf('verb')===0) return 'wl-pos--verb';
if(p.indexOf('adjective')===0||p.indexOf('adj')===0) return 'wl-pos--adj';
if(p.indexOf('adverb')===0||p.indexOf('adv')===0) return 'wl-pos--adv';
if(p.indexOf('pronoun')===0) return 'wl-pos--pron';
if(p.indexOf('preposition')===0||p.indexOf('prep')===0) return 'wl-pos--prep';
if(p.indexOf('conjunction')===0||p.indexOf('conj')===0) return 'wl-pos--conj';
if(p.indexOf('interjection')===0) return 'wl-pos--int';
return 'wl-pos--other';
}
function WLClickToTranslate(attrs){
this.attrs=attrs;
try {
var savedTL=window.localStorage.getItem('wl_ctc_tl');
if(savedTL) this.attrs.targetLang=savedTL;
} catch (e){}
this.attrs.targetLang=normLangCode(this.attrs.targetLang||'en');
this.attrs.pronAccents=Array.isArray(this.attrs.pronAccents)&&this.attrs.pronAccents.length ? this.attrs.pronAccents:['en-GB', 'en-US'];
this.attrs.pronDefaultAccent=(this.attrs.pronDefaultAccent==='en-US') ? 'en-US':'en-GB';
if(this.attrs.pronAccents.indexOf(this.attrs.pronDefaultAccent)===-1) this.attrs.pronDefaultAccent=this.attrs.pronAccents[0];
this.attrs.pronPreferredVoice=(this.attrs.pronPreferredVoice==='male') ? 'male':'female';
try {
var savedAccent=window.localStorage.getItem('wl_ctc_pron_accent');
if(savedAccent&&this.attrs.pronAccents.indexOf(savedAccent)!==-1){
this.attrs.pronDefaultAccent=savedAccent;
}} catch (e){}
this.enabled = !!attrs.enable;
this.popup=null;
this.lastRequestId=0;
this.languages=null;
this.languagesPromise=null;
this.init();
}
function pronunciationLabel(accent){
return accent==='en-US' ? '🇺🇸US':'🇬🇧UK';
}
function guessVoiceGender(voice){
var name=String((voice&&voice.name)||'').toLowerCase();
if(!name) return '';
var femaleHints=['female','woman','zira','samantha','victoria','karen','ava','aria','jenny','susan','hazel','serena'];
var maleHints=['male','man','david','mark','daniel','alex','george','fred','ryan','tom'];
for (var i=0; i < femaleHints.length; i++) if(name.indexOf(femaleHints[i])!==-1) return 'female';
for (var j=0; j < maleHints.length; j++) if(name.indexOf(maleHints[j])!==-1) return 'male';
return '';
}
function scoreVoice(voice, accent, preferredGender){
if(!voice) return -9999;
var name=String(voice.name||'').toLowerCase();
var lang=String(voice.lang||'').toLowerCase().replace('_', '-');
var score=0;
if(accent==='en-GB'){
if(lang.indexOf('en-gb')===0||lang.indexOf('en-uk')===0) score +=120;
else if(lang.indexOf('en')===0) score +=40;
}else{
if(lang.indexOf('en-us')===0) score +=120;
else if(lang.indexOf('en')===0) score +=40;
}
var g=guessVoiceGender(voice);
if(preferredGender&&g===preferredGender) score +=20;
if(name.indexOf('google')!==-1) score +=10;
if(name.indexOf('microsoft')!==-1) score +=8;
if(accent==='en-GB'){
if(name.indexOf('uk')!==-1||name.indexOf('british')!==-1) score +=12;
}else{
if(name.indexOf('us')!==-1||name.indexOf('american')!==-1) score +=12;
}
if(preferredGender==='female'){
if(/(zira|samantha|victoria|karen|ava|aria|jenny|serena|hazel)/.test(name)) score +=8;
}else{
if(/(daniel|alex|david|mark|george|fred|ryan|tom)/.test(name)) score +=8;
}
return score;
}
function pickSpeechVoice(voices, accent, preferredGender){
if(!voices||!voices.length) return null;
var best=null;
var bestScore=-9999;
for (var i=0; i < voices.length; i++){
var score=scoreVoice(voices[i], accent, preferredGender);
if(score > bestScore){
best=voices[i];
bestScore=score;
}}
return best;
}
function unlockSpeechSynthesis(){
try {
if(!('speechSynthesis' in window)||!window.SpeechSynthesisUtterance||window.__wlCtcSpeechUnlocked) return;
window.__wlCtcSpeechUnlocked=true;
var u=new window.SpeechSynthesisUtterance(' ');
u.volume=0;
u.rate=1;
u.pitch=1;
window.speechSynthesis.speak(u);
window.speechSynthesis.cancel();
} catch (e){}}
function waitForVoices(){
return new Promise(function (resolve){
if(!('speechSynthesis' in window)){
resolve([]);
return;
}
var synth=window.speechSynthesis;
var done=false;
var finish=function (){
if(done) return;
done=true;
var voices=[];
try { voices=synth.getVoices ? synth.getVoices():[]; } catch (e){ voices=[]; }
resolve(voices||[]);
};
var voices=[];
try { voices=synth.getVoices ? synth.getVoices():[]; } catch (e){ voices=[]; }
if(voices&&voices.length){
finish();
return;
}
var timer=setTimeout(finish, 800);
var handler=function (){
clearTimeout(timer);
finish();
};
try {
synth.addEventListener('voiceschanged', handler, { once: true });
} catch (e){
var prev=synth.onvoiceschanged;
synth.onvoiceschanged=function (){
try { if(typeof prev==='function') prev.apply(this, arguments); } catch (_e){}
handler();
};}});
}
function renderPronunciationControls(instance, text){
var accents=instance&&instance.attrs&&Array.isArray(instance.attrs.pronAccents)&&instance.attrs.pronAccents.length ? instance.attrs.pronAccents:['en-GB'];
var current=instance.getSelectedPronunciationAccent();
var html="<div class='wl-ctc-pron-wrap'>";
if(accents.length > 1){
html +="<label class='wl-ctc-pron-selectwrap' aria-label='Pronunciation accent'>";
html +="<select class='wl-ctc-pron-select'>";
accents.forEach(function (accent){
html +="<option value='" + escapeHtml(accent) + "'" + (accent===current ? " selected":"") + ">" + escapeHtml(pronunciationLabel(accent)) + "</option>";
});
html +="</select><span class='wl-ctc-pron-chevron' aria-hidden='true'>⌄</span></label>";
}else{
html +="<div class='wl-ctc-pron-pill'>" + escapeHtml(pronunciationLabel(current)) + "</div>";
}
html +="<button type='button' class='wl-ctc-speak' aria-label='Play pronunciation' title='Play pronunciation' data-pron-text='" + escapeHtml(String(text||'')) + "'>";
html +="<svg width='18' height='18' viewBox='0 0 24 24' aria-hidden='true'><path d='M11 5L6 9H2v6h4l5 4V5zm7.07 1.93a1 1 0 0 0-1.41 1.41A7 7 0 0 1 18 12a7 7 0 0 1-1.34 4.66 1 1 0 1 0 1.62 1.16A9 9 0 0 0 20 12a9 9 0 0 0-1.93-6.07zM15.54 4.46a1 1 0 0 0-1.08 1.68A11 11 0 0 1 16 12a11 11 0 0 1-1.54 5.86 1 1 0 1 0 1.68 1.08A13 13 0 0 0 18 12a13 13 0 0 0-2.46-7.54z'/></svg>";
html +="</button></div>";
return html;
}
WLClickToTranslate.prototype.init=function (){
var self=this;
self.renderFloatingToggle();
if(self.attrs.renderButton) self.renderButton();
document.addEventListener('touchstart', unlockSpeechSynthesis, { passive: true, once: true });
document.addEventListener('mousedown', unlockSpeechSynthesis, { passive: true, once: true });
document.addEventListener('keydown', function (e){
if(e.key==='Escape') self.closePopup();
});
if(self.attrs.autoload){
self.setEnabled(self.enabled);
}
self.loadLanguages();
};
WLClickToTranslate.prototype.renderFloatingToggle=function (){
var self=this;
if(document.getElementById('wl-ctc-float-toggle')) return;
var btn=createEl('div', { id: 'wl-ctc-float-toggle', class: 'wl-ctc-float-toggle' }, '');
function paint(){
btn.classList.toggle('is-on', self.enabled);
btn.classList.toggle('is-off', !self.enabled);
btn.textContent=self.enabled ? 'Click to Translate is On':'Click to Translate is Off';
}
btn.addEventListener('click', function (){
self.setEnabled(!self.enabled);
paint();
});
document.body.appendChild(btn);
paint();
};
WLClickToTranslate.prototype.loadLanguages=function (){
var self=this;
if(self.languagesPromise) return self.languagesPromise;
self.languagesPromise=new Promise(function (resolve){
if(!self.attrs.ajaxUrl){
self.languages=LANGS_FALLBACK.slice();
return resolve(self.languages);
}
var params=new URLSearchParams();
params.set('action', 'wl_ctc_languages');
params.set('nonce', self.attrs.nonce||'');
fetch(self.attrs.ajaxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: params.toString(),
credentials: 'same-origin',
})
.then(function (r){ return r.json(); })
.then(function (json){
var langs=(json&&json.success&&json.data&&json.data.languages) ? json.data.languages:null;
if(Array.isArray(langs)&&langs.length >=50){
self.languages=langs
.filter(function (x){ return Array.isArray(x)&&x.length >=2; })
.map(function (x){ return [String(x[0]), String(x[1])]; });
}else{
self.languages=LANGS_FALLBACK.slice();
}
resolve(self.languages);
})
.catch(function (){
self.languages=LANGS_FALLBACK.slice();
resolve(self.languages);
});
});
return self.languagesPromise;
};
WLClickToTranslate.prototype.renderButton=function (){
var self=this;
var containers=document.getElementsByClassName('wlapi_toggle_button_container');
if(!containers||containers.length===0) return;
Array.prototype.forEach.call(containers, function (c){
try {
c.innerHTML='';
c.style.display='none';
} catch (e){}});
};
WLClickToTranslate.prototype.setEnabled=function (val){
var self=this;
self.enabled = !!val;
window.localStorage.setItem('wl_ctc_enabled', self.enabled ? '1':'0');
self.closePopup();
self.detachListeners();
if(self.enabled) self.attachListeners();
};
WLClickToTranslate.prototype.attachListeners=function (){
var self=this;
self._onMouseUp=function (e){
if(!self.enabled) return;
if(isEditableTarget(e.target)) return;
if(self.popup&&self.popup.contains(e.target)) return;
var text=getSelectedText();
if(text&&text.length >=1){
self._ignoreClickUntil=Date.now() + 450;
clearHighlights();
try {
var sel=window.getSelection&&window.getSelection();
if(sel&&sel.rangeCount){
var r=sel.getRangeAt(0).cloneRange();
highlightRange(r);
}} catch (ex){}
self.lookup(text, { x: e.clientX, y: e.clientY });
}};
self._onClick=function (e){
if(!self.enabled) return;
if(self._ignoreClickUntil&&Date.now() < self._ignoreClickUntil) return;
if(isEditableTarget(e.target)) return;
try {
if(e.target &&
e.target.closest &&
e.target.closest('a,button,input,select,textarea,label,[role="button"],[onclick],.wp-block-button__link,.button'
)
){
return;
}} catch (err){}
if(self.popup&&self.popup.contains(e.target)){
if(e.target &&
(e.target.closest &&
(e.target.closest('.wl-ctc-popup__header') ||
e.target.closest('.wl-ctc-popup__tools') ||
e.target.closest('.wl-ctc-popup__close') ||
e.target.closest('button') ||
e.target.closest('select') ||
e.target.closest('a')))
){
return;
}
if(getSelectedText()) return;
var wrIn=getWordAndRangeAtPoint(e.clientX, e.clientY);
if(!wrIn||!wrIn.word) return;
self.lookup(wrIn.word, { x: e.clientX, y: e.clientY, inPopup: true });
return;
}
if(e.target&&(e.target.id==='wl-ctc-float-toggle'||(e.target.closest&&e.target.closest('#wl-ctc-float-toggle')))) return;
if(getSelectedText()) return;
var wr=getWordAndRangeAtPoint(e.clientX, e.clientY);
if(!wr||!wr.word) return;
clearHighlights();
highlightRange(wr.range);
self.lookup(wr.word, { x: e.clientX, y: e.clientY });
};
document.addEventListener('mouseup', self._onMouseUp, true);
document.addEventListener('click', self._onClick, true);
};
WLClickToTranslate.prototype.detachListeners=function (){
var self=this;
if(self._onMouseUp) document.removeEventListener('mouseup', self._onMouseUp, true);
if(self._onClick) document.removeEventListener('click', self._onClick, true);
self._onMouseUp=null;
self._onClick=null;
};
WLClickToTranslate.prototype.ensurePopup=function (){
var self=this;
if(self.popup) return self.popup;
var shell=document.getElementById('wl-ctc-shell');
if(!shell){
shell=createEl('div', { id: 'wl-ctc-shell', class: 'wl-ctc-shell' }, '');
document.body.appendChild(shell);
}
var pop=createEl('div', { class: 'wl-ctc-popup', role: 'dialog', 'aria-live': 'polite' });
var bannerSlot=createEl('div', { class: 'emhds-banner-slot', 'aria-hidden': 'true' }, '');
pop.appendChild(bannerSlot);
var header=createEl('div', { class: 'wl-ctc-popup__header' }, '');
var select=createEl('select', { class: 'wl-ctc-select', 'aria-label': 'Target language' }, '');
function populateSelect(langs){
select.innerHTML='';
var currentTL=normLangCode(self.attrs.targetLang||'en');
function makeOpt(code, name){
var opt=document.createElement('option');
opt.value=code;
opt.textContent=name;
if(String(code).toLowerCase()===String(currentTL).toLowerCase()) opt.selected=true;
return opt;
}
langs.forEach(function (row){
var code=row[0];
var name=row[1];
select.appendChild(makeOpt(code, name));
});
}
self.loadLanguages().then(function (langs){
populateSelect(langs||LANGS_FALLBACK);
});
select.addEventListener('change', function (){
var tl=normLangCode(select.value);
self.attrs.targetLang=tl;
try { window.localStorage.setItem('wl_ctc_tl', tl); } catch (e){}
if(self._lastText){
self.lookup(self._lastText, self._lastPos||{ x: 20, y: 20 });
}});
var headerLeft=createEl('div', { class: 'wl-ctc-header__left' }, '');
var headerRight=createEl('div', { class: 'wl-ctc-header__right' }, '');
var glossBtn=createEl(
'a',
{
href: '#',
class: 'wl-ctc-glossary-btn cstm_save',
'data-word': '',
'aria-disabled': 'true',
title: 'Add selected word to glossary'
},
'ADD TO GLOSSARY'
);
self._glossBtn=glossBtn;
var glossWarn=createEl('div', { class: 'wl-ctc-glossary-warning', 'aria-live': 'polite' }, '');
glossWarn.style.display='none';
self._glossWarn=glossWarn;
var _warnTimer=null;
function showGlossWarn(msg){
try {
if(!self._glossWarn) return;
self._glossWarn.textContent=msg||'';
self._glossWarn.style.display=msg ? 'block':'none';
if(_warnTimer) window.clearTimeout(_warnTimer);
if(msg){
_warnTimer=window.setTimeout(function (){
try { self._glossWarn.style.display='none'; } catch (e){}}, 3500);
}} catch (e){}}
var glossOverlay=createEl('div', { class: 'wl-ctc-glossary-overlay', 'aria-hidden': 'true' }, '');
glossOverlay.innerHTML="<div class='wl-ctc-glossary-spinner' role='status' aria-label='Loading'></div>";
self._glossOverlay=glossOverlay;
if(typeof self._pendingGlossWord==='string'){
glossBtn.setAttribute('data-word', self._pendingGlossWord||'');
try { if(window.jQuery) window.jQuery(glossBtn).data('word', self._pendingGlossWord||''); } catch (e){}}
glossBtn.classList.remove('is-disabled');
glossBtn.setAttribute('aria-disabled', 'false');
glossBtn.addEventListener('click',
function (ev){
ev.preventDefault();
try {
var _selTxt='';
try { _selTxt=(self&&self.currentWord) ? String(self.currentWord):''; } catch(e){}
if(!_selTxt){
try { _selTxt=(self&&self._lastWordOnly) ? String(self._lastWordOnly):''; } catch(e){}}
if(!_selTxt){
try { _selTxt=(glossBtn.getAttribute('data-word')||''); } catch(e){}}
var _cleanSel=String(_selTxt||'').trim().replace(/\s+/g, ' ');
var _wc=countWords(_cleanSel);
if(_wc!==1){
try { ev.stopImmediatePropagation(); ev.stopPropagation(); } catch(e){}
showGlossWarn('Please select a single word to add to your glossary.');
return false;
}
var _token=(_cleanSel.match(/[A-Za-z0-9][A-Za-z0-9'’\-]*/g)||[])[0]||'';
if(!_token){
try { ev.stopImmediatePropagation(); ev.stopPropagation(); } catch(e){}
showGlossWarn('Please select a single word to add to your glossary.');
return false;
}
glossBtn.setAttribute('data-word', _token);
try { if(window.jQuery) window.jQuery(glossBtn).data('word', _token); } catch (e){}} catch(e){}
try {
if(self._glossOverlay){
self._glossOverlay.classList.add('is-visible');
self._glossOverlay.setAttribute('aria-hidden', 'false');
}} catch(e){}
try {
window.setTimeout(function (){
var candidates=document.querySelectorAll('[id*="loader" i],[class*="loader" i],[id*="loading" i],[class*="loading" i],[id*="spinner" i],[class*="spinner" i],.spinner'
);
Array.prototype.forEach.call(candidates, function (el){
if(!el) return;
var cs=window.getComputedStyle(el);
if(!cs||cs.display==='none'||cs.visibility==='hidden'||cs.opacity==='0') return;
if(parseInt(cs.zIndex||'0', 10) < 9999) el.style.zIndex='2147483647';
});
}, 30);
} catch (e){}
try {
if(typeof window.cstm_save==='function'){
window.cstm_save(glossBtn.getAttribute('data-word')||'');
}} catch (e){}
try {
window.setTimeout(function(){
if(self._glossOverlay){
self._glossOverlay.classList.remove('is-visible');
self._glossOverlay.setAttribute('aria-hidden', 'true');
}}, 1200);
} catch(e){}},
true
);
headerLeft.appendChild(glossBtn);
headerRight.appendChild(select);
header.appendChild(headerLeft);
header.appendChild(headerRight);
var closeBtn=createEl('button', { type: 'button', class: 'wl-ctc-popup__close', 'aria-label': 'Close' }, '×');
closeBtn.addEventListener('click', function (e){
e.preventDefault();
e.stopPropagation();
self.closePopup();
});
headerRight.appendChild(closeBtn);
var tools=createEl('div', { class: 'wl-ctc-tools' }, '');
var btnSyn=createEl('button', { type: 'button', class: 'wl-ctc-toolbtn wl-ctc-toolbtn--syn', 'aria-pressed': 'false' }, 'Word Formation & Synonyms!');
var btnImg=createEl('button', { type: 'button', class: 'wl-ctc-toolbtn wl-ctc-toolbtn--img', 'aria-pressed': 'false' }, 'Find an image related to the word!');
tools.appendChild(btnSyn);
tools.appendChild(btnImg);
var body=createEl('div', { class: 'wl-ctc-popup__body' }, '');
var scroll=createEl('div', { class: 'wl-ctc-scroll', 'aria-label': 'Definition details' }, '');
body.appendChild(scroll);
var meta=createEl('div', { class: 'wl-ctc-popup__meta' }, '');
pop.appendChild(header);
pop.appendChild(glossWarn);
pop.appendChild(tools);
pop.appendChild(body);
pop.appendChild(glossOverlay);
pop.appendChild(meta);
var imgModal=createEl('div', { class: 'wl-ctc-imgmodal', 'aria-hidden': 'true' }, "<div class='wl-ctc-imgmodal__top'><div class='wl-ctc-imgmodal__title'>Related images</div><button type='button' class='wl-ctc-mini wl-ctc-imgclose'>Close</button></div><div class='wl-ctc-imggrid'></div>");
pop.appendChild(imgModal);
var synModal=createEl('div', { class: 'wl-ctc-synmodal', 'aria-hidden': 'true' }, "<div class='wl-ctc-imgmodal__top'><div class='wl-ctc-imgmodal__title'>Word Formation &amp; Synonyms</div><button type='button' class='wl-ctc-mini wl-ctc-synclose'>Close</button></div><div class='wl-ctc-syncontent'>Loading…</div>");
pop.appendChild(synModal);
btnSyn.addEventListener('click', function (e){
e.preventDefault();
e.stopPropagation();
if(!self.currentWord) return;
synModal.classList.add('is-open');
synModal.setAttribute('aria-hidden', 'false');
btnSyn.classList.add('is-active');
btnSyn.setAttribute('aria-pressed', 'true');
self.openSynModal(self.currentWord);
});
btnImg.addEventListener('click', function (){
if(!self.currentWord) return;
self.openImageModal(self.currentWord);
});
var imgClose=$('.wl-ctc-imgclose', imgModal);
if(imgClose) imgClose.addEventListener('click', function (e){
e.preventDefault();
e.stopPropagation();
imgModal.classList.remove('is-open');
imgModal.setAttribute('aria-hidden', 'true');
});
var synClose=$('.wl-ctc-synclose', synModal);
if(synClose) synClose.addEventListener('click', function (e){
e.preventDefault();
e.stopPropagation();
synModal.classList.remove('is-open');
synModal.setAttribute('aria-hidden', 'true');
btnSyn.classList.remove('is-active');
btnSyn.setAttribute('aria-pressed', 'false');
});
synModal.addEventListener('click', function (e){
var chip=e.target&&e.target.closest ? e.target.closest('.wl-ctc-chip'):null;
if(!chip) return;
e.preventDefault();
e.stopPropagation();
var w=(chip.textContent||'').trim();
if(!w) return;
synModal.classList.remove('is-open');
synModal.setAttribute('aria-hidden', 'true');
btnSyn.classList.remove('is-active');
btnSyn.setAttribute('aria-pressed', 'false');
var pr=self.popup ? self.popup.getBoundingClientRect():null;
var px=pr ? (pr.left + pr.width / 2):(e.clientX||20);
var py=pr ? (pr.top + 80):(e.clientY||20);
self.lookup(w, { x: px, y: py, inPopup: true });
}, true);
var old=shell.querySelector('.wl-ctc-popup');
if(old&&old.parentNode) old.parentNode.removeChild(old);
shell.appendChild(pop);
self.popup=pop;
self.updateScrollLayout();
if(!self._bannerLayoutBound){
self._bannerLayoutBound=true;
window.addEventListener('emhds:banner-rendered', function (){
try {
window.setTimeout(function (){
try { self.updateScrollLayout(); } catch (e){}}, 0);
} catch (e2){}});
}
if(!self._resizeBound){
self._resizeBound=true;
window.addEventListener('resize', function (){
try { self.updateScrollLayout(); } catch (e){}});
}
return pop;
};
WLClickToTranslate.prototype.updateScrollLayout=function (){
var pop=this.popup;
if(!pop) return;
var header=pop.querySelector('.wl-ctc-popup__header');
var tools=pop.querySelector('.wl-ctc-tools');
var meta=pop.querySelector('.wl-ctc-popup__meta');
var banner=pop.querySelector('.emhds-banner-slot');
var body=pop.querySelector('.wl-ctc-popup__body');
if(!body) return;
var cs=window.getComputedStyle(pop);
var padTop=parseFloat(cs.paddingTop||'0')||0;
var padBottom=parseFloat(cs.paddingBottom||'0')||0;
var total=pop.getBoundingClientRect().height;
var used=padTop + padBottom;
if(header) used +=header.getBoundingClientRect().height;
if(tools) used +=tools.getBoundingClientRect().height;
if(meta) used +=meta.getBoundingClientRect().height;
if(banner) used +=banner.getBoundingClientRect().height;
var available=Math.max(120, Math.floor(total - used - 2));
body.style.height=available + 'px';
body.style.maxHeight=available + 'px';
body.style.overflowY='auto';
body.style.overflowX='hidden';
};
WLClickToTranslate.prototype.closePopup=function (){
var self=this;
try { if(typeof window.closefunc==='function'){ window.closefunc(); }} catch (e){}
if(self.popup&&self.popup.parentNode){
self.popup.parentNode.removeChild(self.popup);
}
self.popup=null;
var shell=document.getElementById('wl-ctc-shell');
if(shell){
shell.style.display='none';
var slot=shell.querySelector('.emhds-banner-slot');
if(slot){ slot.innerHTML=''; slot.setAttribute('aria-hidden','true'); }}
clearHighlights();
};
WLClickToTranslate.prototype.positionPopup=function (pos){
var self=this;
var pop=self.ensurePopup();
var shell=document.getElementById('wl-ctc-shell');
if(shell){
shell.style.position='fixed';
shell.style.left='50%';
shell.style.right='auto';
shell.style.top='auto';
shell.style.bottom='30px';
shell.style.transform='translateX(-50%)';
shell.style.display='block';
shell.style.zIndex='2147483647';
try { shell.dataset.emhdsOpenToken=String(Date.now()); } catch(e){}
try {
if(window.EMHDS_BANNER_RENDER){
window.EMHDS_BANNER_RENDER();
}} catch (_be){}
try {
if(window.EMHDS_DICT_STATS&&window.EMHDS_DICT_STATS.ajax_url){
var t=null;
try { t=shell.dataset ? shell.dataset.emhdsOpenToken:null; } catch(e){ t=null; }
if(!shell.dataset.emhdsDictLoggedToken||shell.dataset.emhdsDictLoggedToken!==t){
shell.dataset.emhdsDictLoggedToken=t||'';
var p=new URLSearchParams();
p.set('action','emhds_dict_open');
p.set('_ajax_nonce', window.EMHDS_DICT_STATS.nonce||'');
p.set('page_url', location.href);
fetch(window.EMHDS_DICT_STATS.ajax_url, {method:'POST', credentials:'same-origin', headers:{'Content-Type':'application/x-www-form-urlencoded; charset=UTF-8'}, body:p.toString()}).catch(function(){});
}}
} catch(_e){}}
pop.style.position='relative';
pop.style.left='auto';
pop.style.right='auto';
pop.style.top='auto';
pop.style.bottom='auto';
pop.style.transform='none';
pop.style.display='flex';
try {
var margin=12;
var rect=(shell||pop).getBoundingClientRect();
if(shell){
if(rect.left < margin) shell.style.left=(margin + rect.width / 2) + 'px';
if(rect.right > window.innerWidth - margin) shell.style.left=(window.innerWidth - margin - rect.width / 2) + 'px';
}} catch (e){}};
WLClickToTranslate.prototype.setPopupContent=function (htmlBody, htmlMeta){
var pop=this.ensurePopup();
var body=pop.querySelector('.wl-ctc-scroll');
var meta=pop.querySelector('.wl-ctc-popup__meta');
if(body) body.innerHTML=htmlBody||'';
if(meta) meta.innerHTML=htmlMeta||'';
if(body) body.scrollTop=0;
var sc=pop.querySelector(".wl-ctc-popup__body");
if(sc) sc.scrollTop=0;
try { this.updateScrollLayout(); } catch (e){}};
WLClickToTranslate.prototype.lookup=function (text, pos){
var self=this;
try { if(typeof window.video_actions==='function'){ window.video_actions(); }} catch (e){}
if(!self.attrs.ajaxUrl) return;
var rl=rateLimitCheck(self.attrs.rateLimitPerMinute||0);
if(!rl.ok){
self.positionPopup(pos);
self.setPopupContent("<div class='wl-ctc-error'>You're using the dictionary too quickly. Please wait a moment and try again.</div>",
''
);
return;
}
self._lastText=text;
self._lastPos=pos;
var cleanedText=String(text||'').trim().replace(/\s+/g, ' ');
var wordCount=countWords(cleanedText);
var maxWords=parseInt(self.attrs.maxWords||100, 10)||100;
if(wordCount > maxWords){
self.positionPopup(pos);
self.setPopupContent("<div class='wl-ctc-error'>You can’t translate more than " + maxWords + " words at once.</div>",
''
);
if(self._glossBtn){
self._glossBtn.setAttribute('data-word', '');
self._glossBtn.classList.remove('is-disabled');
self._glossBtn.setAttribute('aria-disabled', 'false');
}
return;
}
var isSingleWord=false;
var lastWord='';
if(wordCount===1){
var token=(cleanedText.match(/[A-Za-z0-9][A-Za-z0-9'’\-]*/g)||[])[0]||'';
if(token){
isSingleWord=true;
lastWord=token;
}}
self._lastWordOnly=isSingleWord ? lastWord:cleanedText;
self._lastIsSingleWord=isSingleWord;
if(self._glossBtn){
self._glossBtn.setAttribute('data-word', isSingleWord ? self._lastWordOnly:'');
try { if(window.jQuery) window.jQuery(self._glossBtn).data('word', isSingleWord ? self._lastWordOnly:''); } catch (e){}
self._glossBtn.classList.remove('is-disabled');
self._glossBtn.setAttribute('aria-disabled', 'false');
}else{
self._pendingGlossWord=isSingleWord ? self._lastWordOnly:'';
}
self.attrs.targetLang=normLangCode(self.attrs.targetLang||'en');
var requestId=++self.lastRequestId;
self.positionPopup(pos);
self.setPopupContent("<div class='wl-ctc-loading'>Translating…</div>",
''
);
if(!isSingleWord){
self.translatePlain(cleanedText, self.attrs.targetLang||'en')
.then(function (plainTranslation){
if(requestId!==self.lastRequestId) return;
if(plainTranslation){
self.renderPlainTranslation(cleanedText, plainTranslation, self.attrs.targetLang||'en');
}else{
self.setPopupContent("<div class='wl-ctc-error'>Translation failed.</div>", '');
}})
.catch(function (){
if(requestId!==self.lastRequestId) return;
self.setPopupContent("<div class='wl-ctc-error'>Translation failed.</div>", '');
});
return;
}
var params=new URLSearchParams();
params.set('action', 'wl_ctc_translate');
params.set('nonce', self.attrs.nonce||'');
params.set('text', text);
params.set('tl', self.attrs.targetLang||'en');
fetch(self.attrs.ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
},
body: params.toString(),
credentials: 'same-origin',
})
.then(function (r){
return r.json();
})
.then(function (json){
if(requestId!==self.lastRequestId) return;
if(!json||json.success!==true){
self.translatePlain(text, self.attrs.targetLang||'en')
.then(function(plainTranslation){
if(requestId!==self.lastRequestId) return;
if(plainTranslation){
self.renderPlainTranslation(text, plainTranslation, self.attrs.targetLang||'en');
}else{
var msg=(json&&json.data&&json.data.message) ? json.data.message:'Translation failed.';
self.setPopupContent("<div class='wl-ctc-error'>" + escapeHtml(msg) + "</div>", '');
}})
.catch(function(){
if(requestId!==self.lastRequestId) return;
var msg=(json&&json.data&&json.data.message) ? json.data.message:'Translation failed.';
self.setPopupContent("<div class='wl-ctc-error'>" + escapeHtml(msg) + "</div>", '');
});
return;
}
var data=json.data||{};
var translation=data.translation||'';
var target=data.target||(self.attrs.targetLang||'en');
var pron=data.pron||'';
var phonetics=Array.isArray(data.phonetics) ? data.phonetics:[];
var meanings=Array.isArray(data.meanings) ? data.meanings:[];
var dict=Array.isArray(data.dictionary) ? data.dictionary:[];
var examples=Array.isArray(data.examples) ? data.examples:[];
var html="<div class='wl-ctc-head'>";
html +="<div class='wl-ctc-headrow'>";
html +="<div class='wl-ctc-headword'>" + escapeHtml(text) + "</div>";
html +=renderPronunciationControls(self, text);
html +="</div>";
var phonLine=(phonetics&&phonetics.length) ? phonetics.join(' , '):'';
if(!phonLine&&pron) phonLine=pron;
if(phonLine){
html +="<div class='wl-ctc-phonetics'>" + escapeHtml(phonLine) + "</div>";
}
html +="</div>";
html +="<div class='wl-ctc-result'>" + escapeHtml(translation) + "</div>";
if(meanings.length){
html +="<div class='wl-ctc-divider'></div>";
html +="<div class='wl-ctc-dict'>";
meanings.forEach(function (m){
if(!m||!m.pos||!Array.isArray(m.defs)||!m.defs.length) return;
html +="<div class='wl-ctc-pos'>";
html +="<span class='wl-ctc-pos__badge " + posClass(m.pos) + "'>" + escapeHtml(m.pos) + "</span>";
html +="<ol class='wl-ctc-senses'>";
m.defs.forEach(function (d){
if(!d) return;
var defTL=d.definition_tl||d.definition_en||'';
var defEN=d.definition_en||'';
html +="<li class='wl-ctc-sense'>";
html +="<div class='wl-ctc-def'>" + escapeHtml(defTL) + "</div>";
if(defEN&&defEN!==defTL){
html +="<div class='wl-ctc-def wl-ctc-def--sub'>" + escapeHtml(defEN) + "</div>";
}
if(d.example_en||d.example_tl){
html +="<div class='wl-ctc-examples'>";
if(d.example_en){
html +="<div class='wl-ctc-ex'>" + escapeHtml(d.example_en) + "</div>";
}
if(d.example_tl&&d.example_tl!==d.example_en){
html +="<div class='wl-ctc-ex wl-ctc-ex--sub'>" + escapeHtml(d.example_tl) + "</div>";
}
html +="</div>";
}
html +="</li>";
});
html +="</ol>";
html +="</div>";
});
html +="</div>";
}else{
if(dict.length){
html +="<div class='wl-ctc-divider'></div>";
html +="<div class='wl-ctc-dict'>";
dict.forEach(function (d){
if(!d||!d.pos||!Array.isArray(d.terms)||!d.terms.length) return;
html +="<div class='wl-ctc-pos'>";
html +="<span class='wl-ctc-pos__badge " + posClass(d.pos) + "'>" + escapeHtml(d.pos) + "</span>";
html +="<ol class='wl-ctc-meanings'>";
d.terms.slice(0, 10).forEach(function (mm){
html +="<li>" + escapeHtml(mm) + "</li>";
});
html +="</ol>";
html +="</div>";
});
html +="</div>";
}
if(examples.length){
html +="<div class='wl-ctc-divider'></div>";
html +="<div class='wl-ctc-examples'>";
html +="<div class='wl-ctc-sectiontitle'>Examples</div>";
html +="<ul class='wl-ctc-examplelist'>";
examples.slice(0, 3).forEach(function (ex){
html +="<li>" + escapeHtml(ex) + "</li>";
});
html +="</ul></div>";
}}
self.setPopupContent(html, '');
self.currentWord=String(text);
self.currentPos=pos||{ x: 20, y: 20 };
try {
if(self._glossBtn){
self._glossBtn.setAttribute('data-word', self.currentWord||'');
if(window.jQuery) window.jQuery(self._glossBtn).data('word', self.currentWord||'');
}} catch (e){}
try { self.bindPronunciationControls(self.popup, text); } catch (e){}})
.catch(function (){
if(requestId!==self.lastRequestId) return;
self.setPopupContent("<div class='wl-ctc-error'>Translation failed.</div>", '');
});
};
WLClickToTranslate.prototype.openImageModal=function (word){
var self=this;
var pop=self.ensurePopup();
var modal=$('.wl-ctc-imgmodal', pop);
var grid=$('.wl-ctc-imggrid', pop);
if(!modal||!grid) return;
var q=encodeURIComponent(String(word||'').trim());
grid.innerHTML='';
for (var i=0; i < 3; i++){
var img=document.createElement('img');
img.alt='Image for ' + word;
img.loading='lazy';
img.src='https://loremflickr.com/640/420/' + q + '?lock=' + (Date.now() + i);
grid.appendChild(img);
}
modal.classList.add('is-open');
modal.setAttribute('aria-hidden', 'false');
};
WLClickToTranslate.prototype.loadSynonyms=function (word){
var self=this;
self._synData=null;
if(!self.attrs.ajaxUrl) return Promise.resolve(null);
var params=new URLSearchParams();
params.set('action', 'wl_ctc_syn');
params.set('nonce', self.attrs.nonce||'');
params.set('q', String(word||'').trim());
return fetch(self.attrs.ajaxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: params.toString(),
credentials: 'same-origin',
})
.then(function (r){ return r.json(); })
.then(function (json){
if(json&&json.success&&json.data){
self._synData=json.data;
}
return self._synData;
})
.catch(function (){
return null;
});
};
WLClickToTranslate.prototype.openSynModal=function (word){
var self=this;
var pop=self.ensurePopup();
var modal=$('.wl-ctc-synmodal', pop);
var box=$('.wl-ctc-syncontent', pop);
if(!modal||!box) return;
box.innerHTML="<div style='opacity:.75;font-size:13px;padding:12px;'>Loading…</div>";
self.loadSynonyms(word).then(function (d){
if(!d){
box.innerHTML="<div style='opacity:.75;font-size:13px;padding:12px;'>No synonyms found.</div>";
return;
}
function chips(arr){
arr=Array.isArray(arr) ? arr.slice(0, 24):[];
if(!arr.length) return '<div style="opacity:.6;font-size:13px;">—</div>';
return arr
.map(function (w){
return "<span class='wl-ctc-chip'>" + escapeHtml(w) + "</span>";
})
.join('');
}
var html="<div class='wl-ctc-synsection'><div class='wl-ctc-sectiontitle'>Synonyms</div><div class='wl-ctc-chiprow'>" + chips(d.synonyms) + "</div></div>";
html +="<div class='wl-ctc-synsection'><div class='wl-ctc-sectiontitle'>Related words</div><div class='wl-ctc-chiprow'>" + chips(d.related) + "</div></div>";
html +="<div class='wl-ctc-synsection'><div class='wl-ctc-sectiontitle'>Word forms</div><div class='wl-ctc-chiprow'>" + chips(d.forms) + "</div></div>";
box.innerHTML=html;
});
};
WLClickToTranslate.prototype.getSelectedPronunciationAccent=function (){
var accents=Array.isArray(this.attrs.pronAccents)&&this.attrs.pronAccents.length ? this.attrs.pronAccents:['en-GB'];
var accent=this.attrs.pronDefaultAccent==='en-US' ? 'en-US':'en-GB';
if(accents.indexOf(accent)===-1) accent=accents[0];
return accent;
};
WLClickToTranslate.prototype.setSelectedPronunciationAccent=function (accent){
var accents=Array.isArray(this.attrs.pronAccents)&&this.attrs.pronAccents.length ? this.attrs.pronAccents:['en-GB'];
if(accents.indexOf(accent)===-1) accent=accents[0];
this.attrs.pronDefaultAccent=accent;
try { window.localStorage.setItem('wl_ctc_pron_accent', accent); } catch (e){}};
WLClickToTranslate.prototype.bindPronunciationControls=function (scope, text){
var self=this;
var root=scope||self.popup;
if(!root) return;
var select=root.querySelector('.wl-ctc-pron-select');
var btn=root.querySelector('.wl-ctc-speak');
if(select){
select.value=self.getSelectedPronunciationAccent();
select.addEventListener('change', function (){
self.setSelectedPronunciationAccent(String(this.value||'en-GB'));
});
}
if(btn){
btn.addEventListener('click', function (){
var phrase=String(this.getAttribute('data-pron-text')||text||self.currentWord||'').trim();
self.speakText(phrase, self.getSelectedPronunciationAccent());
});
}};
WLClickToTranslate.prototype.speakText=function (text, accent){
var self=this;
var phrase=String(text||'').trim();
if(!phrase||!('speechSynthesis' in window)||!window.SpeechSynthesisUtterance) return;
accent=accent==='en-US' ? 'en-US':'en-GB';
unlockSpeechSynthesis();
waitForVoices().then(function (voices){
var synth=window.speechSynthesis;
try { synth.cancel(); } catch (e){}
var u=new window.SpeechSynthesisUtterance(phrase);
u.lang=accent;
u.rate=0.95;
u.pitch=1;
var voice=pickSpeechVoice(voices, accent, self.attrs.pronPreferredVoice||'female');
if(voice){
u.voice=voice;
if(voice.lang) u.lang=voice.lang;
}
try { synth.speak(u); } catch (e){}});
};
WLClickToTranslate.prototype.renderPlainTranslation=function (originalText, translatedText, tl){
var self=this;
var orig=String(originalText||'').trim();
var tr=String(translatedText||'').trim();
var title="<div class='wl-ctc-head'><div class='wl-ctc-headrow'><div class='wl-ctc-plain-title'>" + escapeHtml(orig) + "</div>" + renderPronunciationControls(self, orig) + "</div></div>";
var box="<div class='wl-ctc-translation-box'><div class='wl-ctc-translation-text'>" + escapeHtml(tr) + "</div></div>";
self.setPopupContent(title + box, "");
try { self.bindPronunciationControls(self.popup, orig); } catch (e){}};
WLClickToTranslate.prototype.translatePlain=function (text, tl){
var self=this;
if(!self.attrs.ajaxUrl) return Promise.resolve('');
var params=new URLSearchParams();
params.set('action', 'wl_ctc_translate_plain');
params.set('nonce', self.attrs.nonce||'');
params.set('text', String(text||'').trim());
params.set('tl', normLangCode(tl||'en'));
return fetch(self.attrs.ajaxUrl, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
body: params.toString(),
credentials: 'same-origin',
})
.then(function (r){ return r.json(); })
.then(function (json){
if(json&&json.success&&json.data&&json.data.translation) return String(json.data.translation);
return '';
})
.catch(function (){ return ''; });
};
function escapeHtml(str){
return String(str)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
document.addEventListener('DOMContentLoaded', function (){
var attrs=getScriptAttributes();
window.WPLookupAPI=window.WPLookupAPI||{};
window.WPLookupAPI._instance=new WLClickToTranslate(attrs);
});
})();