'use strict'; /* ────────────────────────────────────────────────────────────────── Exam 9 — Math 2025 renderer Variants loaded into window.VARIANTS by /js/exam9/variants/vNN.js ────────────────────────────────────────────────────────────────── */ const STORAGE_KEY = 'exam9_progress_v1'; let currentVariant = null; let katexLoaded = false; /* ── KaTeX bootstrap ────────────────────────────────────────────── */ function onKatexLoad() { katexLoaded = true; if (currentVariant !== null) runKatex(document.getElementById('ex-main')); } function runKatex(el) { if (!katexLoaded || !el) return; try { renderMathInElement(el, { delimiters: [ { left: '$$', right: '$$', display: true }, { left: '$', right: '$', display: false }, ], throwOnError: false, }); } catch {} } /* ── Progress in localStorage ───────────────────────────────────── */ function loadProgress() { try { return JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}'); } catch { return {}; } } function saveProgress(p) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(p)); } catch {} } function markSolutionViewed(variantNum, taskIdx) { const p = loadProgress(); p[variantNum] = p[variantNum] || []; if (!p[variantNum].includes(taskIdx)) { p[variantNum].push(taskIdx); saveProgress(p); } } /* ── Variant picker ─────────────────────────────────────────────── */ function buildGrid() { const grid = document.getElementById('variant-grid'); const progress = loadProgress(); grid.innerHTML = ''; Object.keys(VARIANTS).sort((a, b) => Number(a) - Number(b)).forEach(n => { const v = VARIANTS[n]; const total = (v.tasks || []).length; const viewed = (progress[n] || []).length; let cls = ''; if (viewed === total && total > 0) cls = ' done'; else if (viewed > 0) cls = ' partial'; const isActive = Number(n) === currentVariant ? ' active' : ''; const btn = document.createElement('button'); btn.className = 'vg-btn' + cls + isActive; btn.textContent = n; btn.title = `${v.label}${viewed === total ? ' ✓' : viewed > 0 ? ` (${viewed}/${total})` : ''}`; btn.onclick = () => { selectVariant(Number(n)); closePicker(); }; grid.appendChild(btn); }); } function togglePicker() { const overlay = document.getElementById('picker-overlay'); const btn = document.getElementById('picker-btn'); if (overlay.classList.contains('visible')) closePicker(); else { buildGrid(); overlay.classList.add('visible'); btn.classList.add('open'); document.addEventListener('keydown', onEsc); } } function closePicker() { document.getElementById('picker-overlay').classList.remove('visible'); document.getElementById('picker-btn').classList.remove('open'); document.removeEventListener('keydown', onEsc); } function onOverlayClick(e) { if (e.target === document.getElementById('picker-overlay')) closePicker(); } function onEsc(e) { if (e.key === 'Escape') closePicker(); } /* ── Task rendering ─────────────────────────────────────────────── */ function buildOpts(opts) { const isLong = opts.some(([, t]) => t.length > 40 && !t.startsWith('$')); const cls = isLong ? 'opts-vertical' : 'opts'; return `