const $ = (s) => document.querySelector(s); const drop = $('#drop'), fileInput = $('#file'), preview = $('#preview'); let direction = 'en2mm', voice = 'female', engine = 'nara', naraVoice = 'shoon', pollTimer = null, lastJid = null; let mode = 'file', checkedUrl = '', checkedTitle = ''; let subsOn = false, subBg = 'box', blurOn = false, linkSid = ''; let fileSid = '', pvServer = false, pvStaging = false, pvTimer = null; function selectGrid(gridSel, attr, cb) { document.querySelectorAll(gridSel + ' .opt').forEach((b) => { b.addEventListener('click', () => { document.querySelectorAll(gridSel + ' .opt').forEach((x) => x.classList.remove('selected')); b.classList.add('selected'); cb(b.dataset[attr]); }); }); } selectGrid('#dirgrid', 'dir', (v) => (direction = v)); // Nara voice follows the gender pick (female -> Shoon, male -> Ko Banyar) // until the user taps a Nara voice directly. function syncNaraVoice() { naraVoice = voice === 'male' ? 'ko-banyar' : 'shoon'; document.querySelectorAll('#naravoicegrid .opt').forEach((x) => x.classList.toggle('selected', x.dataset.naravoice === naraVoice)); } selectGrid('#voicegrid', 'voice', (v) => { voice = v; if (engine === 'nara') syncNaraVoice(); }); selectGrid('#enginegrid', 'engine', (v) => { engine = v; $('#naravoicewrap').hidden = v !== 'nara'; if (v === 'nara') syncNaraVoice(); }); selectGrid('#naravoicegrid', 'naravoice', (v) => (naraVoice = v)); selectGrid('#subgrid', 'subs', (v) => { subsOn = v === 'on'; $('#subbgwrap').hidden = !subsOn; }); selectGrid('#subbggrid', 'subbg', (v) => (subBg = v)); selectGrid('#blurgrid', 'blur', (v) => { blurOn = v === 'on'; updateBlurUI(); }); // ---- blur box editor: one draggable/resizable box over a media frame ---- // The box is stored as fractions of the frame (x, y, w, h in 0..1), so the // same editor works over the local