${escapeHtml(label)}
${escapeHtml(opt.option_text)}
`;
// Restore prior selection
if (answers[q.id] && answers[q.id].selected.includes(opt.id)) {
div.classList.add('selected');
}
div.addEventListener('click', () => selectOption(div, opt.id, q));
list.appendChild(div);
});
const expBox = document.getElementById('explanation');
if (answers[q.id]) {
showExplanation(q);
markOptions(q);
} else {
expBox.style.display = 'none';
}
// Dot nav
const dots = document.getElementById('dotNav');
dots.innerHTML = '';
const maxDots = Math.min(total, 20);
for (let i = 0; i < maxDots; i++) {
const d = document.createElement('div');
d.style.cssText = `width:8px;height:8px;border-radius:50%;transition:all 0.25s;cursor:pointer;background:${i===currentQ?'var(--gold)':answers[QUIZ_DATA.questions[i].id]?'var(--teal)':'var(--border-subtle)'}`;
d.title = `Question ${i+1}`;
const idx = i;
d.onclick = () => { currentQ = idx; renderQuestion(); };
dots.appendChild(d);
}
if (total > 20) {
const more = document.createElement('span');
more.style.cssText = 'font-size:11px;color:var(--text-muted);padding:0 4px';
more.textContent = `+${total - 20} more`;
dots.appendChild(more);
}
document.getElementById('prevBtn').style.visibility = currentQ > 0 ? 'visible' : 'hidden';
const nextBtn = document.getElementById('nextBtn');
nextBtn.textContent = currentQ === total - 1 ? 'Submit Quiz ✓' : 'Next →';
}
function selectOption(div, optId, q) {
if (answers[q.id]) return; // already answered
if (q.question_type === 'multiple') {
// Toggle for multiple choice
if (!answers[q.id]) answers[q.id] = { selected: [] };
const idx = answers[q.id].selected.indexOf(optId);
if (idx === -1) {
answers[q.id].selected.push(optId);
div.classList.add('selected');
} else {
answers[q.id].selected.splice(idx, 1);
div.classList.remove('selected');
}
// For multiple: don't lock until Next is clicked
return;
}
// Single / true_false: lock immediately
document.querySelectorAll('.quiz-option').forEach(o => o.classList.remove('selected'));
div.classList.add('selected');
answers[q.id] = { selected: [optId] };
showExplanation(q);
markOptions(q);
}
function markOptions(q) {
const ans = answers[q.id];
if (!ans) return;
const correctIds = q.options.filter(o => parseInt(o.is_correct)).map(o => o.id);
document.querySelectorAll('.quiz-option').forEach(opt => {
const id = parseInt(opt.dataset.id);
const isSelected = ans.selected.includes(id);
const isCorrect = correctIds.includes(id);
opt.classList.remove('selected');
if (isSelected && isCorrect) opt.classList.add('correct');
else if (isSelected) opt.classList.add('wrong');
else if (isCorrect) opt.classList.add('correct');
});
}
function showExplanation(q) {
if (q.explanation && QUIZ_DATA.quiz.show_answers === '1') {
document.getElementById('explanationText').textContent = q.explanation;
document.getElementById('explanation').style.display = 'block';
}
}
function prevQuestion() {
if (currentQ > 0) { currentQ--; renderQuestion(); }
}
async function nextQuestion() {
const q = QUIZ_DATA.questions[currentQ];
const isMultiple = q.question_type === 'multiple';
// For multiple-choice, lock & mark on Next
if (isMultiple && !answers[q.id]) {
const selected = [...document.querySelectorAll('.quiz-option.selected')].map(d => parseInt(d.dataset.id));
if (!selected.length) { showToast('Select at least one answer', 'info'); return; }
answers[q.id] = { selected };
markOptions(q);
showExplanation(q);
return; // let them see feedback before moving
}
if (!answers[q.id] && !isMultiple) { showToast('Please select an answer', 'info'); return; }
if (currentQ < QUIZ_DATA.questions.length - 1) {
currentQ++;
renderQuestion();
} else {
await submitQuiz();
}
}
async function submitQuiz() {
if (timerInterval) clearInterval(timerInterval);
const timeTaken = Math.round((Date.now() - startTime) / 1000);
try {
const res = await fetch(SITE_URL + '/api/quiz.php?action=submit', {
method: 'POST', headers: {'Content-Type':'application/json'},
body: JSON.stringify({ attempt_id: attemptId, answers, time_taken: timeTaken })
});
const data = await res.json();
if (data.success) showResults(data, timeTaken);
else showToast(data.error || 'Submit failed', 'error');
} catch(e) {
showToast('Error submitting. Try again.', 'error');
}
}
function showResults(data, timeTaken) {
document.getElementById('quizInterface').style.display = 'none';
document.getElementById('resultsPanel').style.display = 'block';
const score = Math.round(data.score);
const passed = score >= parseInt(QUIZ_DATA.quiz.passing_score);
// innerHTML, not textContent - these are SVG fragments, and textContent
// would render the markup as visible source.
document.getElementById('resultEmoji').innerHTML =
score >= 80 ? "