/* eslint-env browser */
// Polished single-question card quiz renderer (choice / true-false only)
const QuizEngine = (function() {
// Top-level storage key; inside we'll store per-quiz results by quizId
const ROOT_KEY = 'weblesson_quiz_v1';
function makeStorage() {
const raw = localStorage.getItem(ROOT_KEY) || '{}';
try {return JSON.parse(raw);} catch (e) {return {};}
}
function saveStorage(obj) {localStorage.setItem(ROOT_KEY, JSON.stringify(obj));}
// Render a quiz as a card UI. `quizMeta` may contain id/title; questions array contains {id, question, options[], answer, type}
function renderQuiz(container, questions, quizMeta = {}) {
if (!Array.isArray(questions) || questions.length === 0) {
container.innerHTML = '<div class="quiz-empty">ไม่มีแบบทดสอบ</div>';
return;
}
const quizId = quizMeta.id || quizMeta.src || ('quiz_' + (questions[0].id || Date.now()));
// ensure only supported types
const filtered = questions.map(q => ({
id: q.id || String(Math.random()).slice(2, 8),
question: q.question || '',
options: Array.isArray(q.options) ? q.options : [],
answer: typeof q.answer !== 'undefined' ? q.answer : 0,
type: q.type === 'true-false' ? 'true-false' : 'choice',
explanation: q.explanation || ''
}));
const total = filtered.length;
let idx = 0;
function renderCard() {
container.innerHTML = '';
const card = document.createElement('div');
card.className = 'quiz-card';
const header = document.createElement('div');
header.className = 'quiz-header';
const title = document.createElement('div');
title.className = 'quiz-title';
title.textContent = quizMeta.title || 'แบบทดสอบ';
const progress = document.createElement('div');
progress.className = 'quiz-progress';
progress.textContent = `คำถาม ${idx + 1} / ${total}`;
header.appendChild(title);
header.appendChild(progress);
const body = document.createElement('div');
body.className = 'quiz-body';
const qobj = filtered[idx];
const qEl = document.createElement('div');
qEl.className = 'quiz-question';
qEl.textContent = qobj.question;
const optionsWrap = document.createElement('div');
optionsWrap.className = 'quiz-options';
const opts = qobj.type === 'true-false' ? ['True', 'False'] : qobj.options;
opts.forEach((opt, i) => {
const row = document.createElement('label');
row.className = 'quiz-option';
row.setAttribute('data-choice', String(i));
const radio = document.createElement('input');
radio.type = 'radio';
radio.name = 'quiz_opt_' + qobj.id;
radio.value = String(i);
// visually hidden, we use the whole label as button
const span = document.createElement('div');
span.className = 'label-text';
span.textContent = opt;
row.appendChild(radio);
row.appendChild(span);
row.addEventListener('click', () => handleSelect(qobj, i, row));
optionsWrap.appendChild(row);
});
body.appendChild(qEl);
body.appendChild(optionsWrap);
const actions = document.createElement('div');
actions.className = 'quiz-actions';
const skip = document.createElement('button');
skip.className = 'quiz-btn ghost';
skip.textContent = idx < total - 1 ? 'ข้าม' : 'ดูผล';
skip.addEventListener('click', () => {if (idx < total - 1) {idx++; renderCard();} else {showSummary();} });
const next = document.createElement('button');
next.className = 'quiz-btn';
next.textContent = idx < total - 1 ? 'ถัดไป' : 'ส่งคำตอบ';
next.addEventListener('click', () => {if (idx < total - 1) {idx++; renderCard();} else {showSummary();} });
actions.appendChild(skip);
actions.appendChild(next);
card.appendChild(header);
card.appendChild(body);
card.appendChild(actions);
container.appendChild(card);
// restore previous answer if present
const stored = makeStorage();
const quizData = stored[quizId] || {};
const saved = quizData[qobj.id];
if (typeof saved !== 'undefined') {
const chosen = String(saved.choice);
const label = card.querySelector('.quiz-option[data-choice="' + chosen + '"]');
if (label) label.classList.add(saved.correct ? 'correct' : 'wrong');
}
}
function handleSelect(qobj, choiceIndex, rowEl) {
// disable options for this question
const card = rowEl.closest('.quiz-card');
card.querySelectorAll('.quiz-option').forEach(r => r.style.pointerEvents = 'none');
const correct = Number(qobj.answer) === Number(choiceIndex);
rowEl.classList.add(correct ? 'correct' : 'wrong');
// save per-quiz/per-question
const store = makeStorage();
store[quizId] = store[quizId] || {};
store[quizId][qobj.id] = {choice: choiceIndex, correct: !!correct, ts: Date.now()};
saveStorage(store);
// show explanation if any
if (qobj.explanation) {
const fb = document.createElement('div');
fb.className = 'quiz-feedback';
fb.textContent = qobj.explanation;
const body = card.querySelector('.quiz-body');
body.appendChild(fb);
}
}
function showSummary() {
const store = makeStorage();
const data = (store[quizId]) || {};
let correctCount = 0;
filtered.forEach(q => {if (data[q.id] && data[q.id].correct) correctCount++;});
container.innerHTML = '';
const card = document.createElement('div');
card.className = 'quiz-card';
const header = document.createElement('div');
header.className = 'quiz-header';
const title = document.createElement('div');
title.className = 'quiz-title';
title.textContent = quizMeta.title || 'ผลการทดสอบ';
const progress = document.createElement('div');
progress.className = 'quiz-progress';
progress.textContent = `ได้ ${correctCount} / ${total}`;
header.appendChild(title);
header.appendChild(progress);
const score = document.createElement('div');
score.className = 'quiz-score';
score.innerHTML = `<div class="big">${Math.round((correctCount / total) * 100)}%</div><div class="quiz-small-muted">คำตอบถูก ${correctCount} จาก ${total}</div>`;
const actions = document.createElement('div');
actions.className = 'quiz-actions';
const retry = document.createElement('button');
retry.className = 'quiz-btn';
retry.textContent = 'ทำใหม่';
retry.addEventListener('click', () => {
// clear quiz results
const s = makeStorage();
if (s[quizId]) delete s[quizId];
saveStorage(s);
idx = 0; renderCard();
});
const close = document.createElement('button');
close.className = 'quiz-btn ghost';
close.textContent = 'ปิด';
close.addEventListener('click', () => {container.innerHTML = '';});
actions.appendChild(close);
actions.appendChild(retry);
card.appendChild(header);
card.appendChild(score);
card.appendChild(actions);
container.appendChild(card);
}
renderCard();
}
// convenience: load/save helpers exposed for tests
return {renderQuiz, _makeStorage: makeStorage};
})();
// Expose globally
window.QuizEngine = QuizEngine;