// ── Rehearser cast view controls ──────────────────────────────────────────── // The cast renderer remains in rehearser.js; these handlers only resolve state // when the cast panel is rendered, making them safe to load as a leaf module. function applyCastView() { const list = $('reh-cast-list'); if (!list) return; const view = rehState.castView === 'list' ? 'list' : 'card'; list.classList.toggle('reh-cast-view-card', view === 'card'); list.classList.toggle('reh-cast-view-list', view === 'list'); document.querySelectorAll('#reh-cast-view-toggle .reh-view-btn').forEach(b => b.classList.toggle('active', b.dataset.view === view)); } function _wireCastControls() { const toggle = $('reh-cast-view-toggle'); if (!toggle || toggle._wired) return; toggle._wired = true; toggle.querySelectorAll('.reh-view-btn').forEach(b => b.addEventListener('click', () => { rehState.castView = b.dataset.view; try { localStorage.setItem('reh-cast-view', b.dataset.view); } catch (_) {} applyCastView(); })); const search = $('reh-cast-search'), fg = $('reh-cast-filter-gender'), fl = $('reh-cast-filter-lang'); const sortSel = $('reh-cast-sort'), dirBtn = $('reh-cast-sort-dir'); const setDirIcon = () => { if (dirBtn) { dirBtn.dataset.dir = rehState.castSort.dir; dirBtn.querySelector('.mdi').className = 'mdi mdi-sort-' + (rehState.castSort.dir === 'desc' ? 'descending' : 'ascending'); } }; const persist = () => { try { localStorage.setItem('reh-cast-sort', JSON.stringify(rehState.castSort)); } catch (_) {} }; if (sortSel) sortSel.value = rehState.castSort.by; setDirIcon(); search?.addEventListener('input', () => { rehState.castFilter.search = search.value.trim(); renderCastList(); search.focus(); }); fg?.addEventListener('change', () => { rehState.castFilter.gender = fg.value; renderCastList(); }); fl?.addEventListener('change', () => { rehState.castFilter.lang = fl.value; renderCastList(); }); sortSel?.addEventListener('change', () => { rehState.castSort.by = sortSel.value; rehState.castSort.dir = sortSel.value === 'lines' ? 'desc' : 'asc'; setDirIcon(); persist(); renderCastList(); }); dirBtn?.addEventListener('click', () => { rehState.castSort.dir = rehState.castSort.dir === 'desc' ? 'asc' : 'desc'; setDirIcon(); persist(); renderCastList(); }); } function _castSortFilter(speakers, lineCount) { const f = rehState.castFilter || {}, s = rehState.castSort || { by: 'name', dir: 'asc' }; const out = speakers.filter(sp => { const c = rehState.cast[sp] || {}; if (f.search) { const q = f.search.toLowerCase(); if (!sp.toLowerCase().includes(q) && !(c.tags || '').toLowerCase().includes(q)) return false; } if (f.gender && (c.gender || '') !== f.gender) return false; if (f.lang && (c.lang || '') !== f.lang) return false; return true; }); const byName = (a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }); const cmp = { name: byName, gender: (a, b) => (rehState.cast[a].gender || '~').localeCompare(rehState.cast[b].gender || '~') || byName(a, b), lang: (a, b) => (rehState.cast[a].lang || '~').localeCompare(rehState.cast[b].lang || '~') || byName(a, b), lines: (a, b) => (lineCount(a) - lineCount(b)) || byName(a, b), tag: (a, b) => (rehState.cast[a].tags || '~').localeCompare(rehState.cast[b].tags || '~') || byName(a, b), }[s.by] || byName; out.sort(cmp); if (s.dir === 'desc') out.reverse(); return out; } window.TTVC = window.TTVC || {}; window.TTVC.rehearser = window.TTVC.rehearser || {}; window.TTVC.rehearser.cast = { applyCastView, filterAndSort: _castSortFilter };