// =====================================================
// Quiz — multiple choice per hovedtema med sub-temaer
// =====================================================
let quizData = null;
let quizState = {
category: 'all',
subtopic: 'all',
questions: [],
current: 0,
selected: null,
answered: false,
correct: 0,
wrong: 0,
perSubtopic: {}, // subtopic -> {correct, total}
finished: false
};
async function quizLoad() {
if (quizData) return quizData;
try {
const res = await fetch('data/quiz.json');
quizData = await res.json();
} catch (e) {
console.error('Klarte ikke laste quiz:', e);
quizData = [];
}
return quizData;
}
function quizCategoryLabel(cat) {
return {
etikk: 'Etikk',
baerekraft: 'Bærekraft',
samfunn: 'Samfunnsansvar',
verktoy: 'Verktøy & implementering',
all: 'Alt pensum'
}[cat] || cat;
}
function categoryQuestions(cat) {
if (cat === 'all') return quizData;
return quizData.filter(q => q.category === cat);
}
function subtopicsForCategory(cat) {
const qs = categoryQuestions(cat);
const counts = new Map();
qs.forEach(q => {
const sub = q.subtopic || 'Tverrgående';
counts.set(sub, (counts.get(sub) || 0) + 1);
});
return [...counts.entries()].sort((a, b) => b[1] - a[1]);
}
function quizFilter(cat, subtopic) {
const base = categoryQuestions(cat);
if (!subtopic || subtopic === 'all') return base;
return base.filter(q => (q.subtopic || 'Tverrgående') === subtopic);
}
function quizShuffle(arr) {
arr = [...arr];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
// ============= Start page (4 main parts) =============
function quizRenderStart() {
const stage = document.getElementById('quizStage');
const counts = {
all: quizData.length,
etikk: categoryQuestions('etikk').length,
baerekraft: categoryQuestions('baerekraft').length,
samfunn: categoryQuestions('samfunn').length,
verktoy: categoryQuestions('verktoy').length
};
const cardFor = (cat, label, eyebrow, romanNum, themeClass) => {
const subs = subtopicsForCategory(cat);
const chips = subs.slice(0, 4).map(([name]) => `${name}`).join('');
const more = subs.length > 4 ? `+${subs.length - 4} til` : '';
return `
`;
};
stage.innerHTML = `
${cardFor('etikk', 'Etikk', 'Oppgave I', 'I', 'theme-card--etikk')}
${cardFor('baerekraft', 'Bærekraft', 'Oppgave II', 'II', 'theme-card--baerekraft')}
${cardFor('samfunn', 'Samfunnsansvar', 'Oppgave III', 'III', 'theme-card--samfunn')}
${cardFor('verktoy', 'Verktøy & impl.', 'Oppgave IV · Case', 'IV', 'theme-card--verktoy')}
`;
stage.querySelectorAll('[data-cat]').forEach(btn => {
btn.addEventListener('click', () => {
const cat = btn.dataset.cat;
if (cat === 'all') {
quizBegin(cat, 'all');
} else {
quizRenderCategoryHub(cat);
}
});
});
}
// ============= Sub-topic hub for a category =============
function quizRenderCategoryHub(cat) {
const stage = document.getElementById('quizStage');
const subs = subtopicsForCategory(cat);
const total = categoryQuestions(cat).length;
const themeClass = `theme-card--${cat}`;
const themeColors = SMF.THEME_COLORS[cat];
stage.innerHTML = `
Velg deltema
${subs.map(([name, count]) => `
`).join('')}
`;
stage.querySelectorAll('[data-sub]').forEach(btn => {
btn.addEventListener('click', () => {
quizBegin(cat, btn.dataset.sub);
});
});
document.getElementById('quizBack').addEventListener('click', quizRenderStart);
}
// ============= Begin a quiz =============
function quizBegin(cat, sub) {
quizState.category = cat;
quizState.subtopic = sub;
quizState.questions = quizShuffle(quizFilter(cat, sub));
quizState.current = 0;
quizState.selected = null;
quizState.answered = false;
quizState.correct = 0;
quizState.wrong = 0;
quizState.perSubtopic = {};
quizState.finished = false;
quizRenderCurrent();
}
function quizRenderCurrent() {
const stage = document.getElementById('quizStage');
if (quizState.finished) return quizRenderResult();
const total = quizState.questions.length;
const q = quizState.questions[quizState.current];
if (!q) return;
const progress = (quizState.current / total) * 100;
const letters = ['A', 'B', 'C', 'D', 'E'];
const themeColors = quizState.category === 'all'
? SMF.THEME_COLORS[q.category]
: SMF.THEME_COLORS[quizState.category];
const subLabel = q.subtopic || 'Tverrgående';
stage.innerHTML = `
${quizState.current + 1} / ${total}
${quizCategoryLabel(q.category)} · ${subLabel}
Riktig: ${quizState.correct}
${q.question}
${q.options.map((opt, i) => `
`).join('')}
`;
stage.querySelectorAll('.quiz-option').forEach(opt => {
opt.addEventListener('click', () => {
if (quizState.answered) return;
quizSelect(parseInt(opt.dataset.i, 10));
});
});
document.getElementById('quizNext').addEventListener('click', quizAdvance);
document.getElementById('quizCancel').addEventListener('click', () => {
if (confirm('Avslutte testen?')) quizRenderStart();
});
}
function quizSelect(i) {
const q = quizState.questions[quizState.current];
quizState.selected = i;
quizState.answered = true;
const opts = document.querySelectorAll('.quiz-option');
opts.forEach((opt, idx) => {
opt.classList.add('quiz-option--disabled');
if (idx === q.correct) opt.classList.add('quiz-option--correct');
if (idx === i && i !== q.correct) opt.classList.add('quiz-option--wrong');
if (idx === i) opt.classList.add('quiz-option--selected');
});
const sub = q.subtopic || 'Tverrgående';
if (!quizState.perSubtopic[sub]) quizState.perSubtopic[sub] = { correct: 0, total: 0 };
quizState.perSubtopic[sub].total++;
if (i === q.correct) {
quizState.correct++;
quizState.perSubtopic[sub].correct++;
} else {
quizState.wrong++;
}
const exp = document.getElementById('quizExplanation');
exp.innerHTML = `
${i === q.correct ? 'Riktig — ' : 'Forklaring — '}
${q.explanation}
`;
document.getElementById('quizNext').disabled = false;
}
function quizAdvance() {
quizState.current++;
quizState.selected = null;
quizState.answered = false;
if (quizState.current >= quizState.questions.length) {
quizState.finished = true;
}
quizRenderCurrent();
}
function quizRenderResult() {
const stage = document.getElementById('quizStage');
const total = quizState.questions.length;
const pct = total ? Math.round((quizState.correct / total) * 100) : 0;
let verdict, vColor;
if (pct >= 90) { verdict = 'Fremragende.'; vColor = 'var(--theme-baerekraft)'; }
else if (pct >= 80) { verdict = 'Meget god.'; vColor = 'var(--theme-baerekraft)'; }
else if (pct >= 65) { verdict = 'Solid forståelse.'; vColor = 'var(--theme-samfunn)'; }
else if (pct >= 50) { verdict = 'Greit, men trener videre.'; vColor = 'var(--theme-samfunn)'; }
else { verdict = 'Repeter mer av dette temaet.'; vColor = 'var(--theme-etikk)'; }
// Per-subtopic breakdown
const subRows = Object.entries(quizState.perSubtopic)
.sort((a, b) => b[1].total - a[1].total)
.map(([name, { correct, total }]) => {
const p = total ? Math.round((correct / total) * 100) : 0;
const barColor = p >= 70 ? 'var(--theme-baerekraft)' : p >= 50 ? 'var(--theme-samfunn)' : 'var(--theme-etikk)';
return `
${name}
${correct}/${total}
`;
}).join('');
stage.innerHTML = `
${quizState.correct}/${total}
${pct} % riktig
${verdict}
${subRows ? `
Fordelt på deltema
${subRows}
` : ''}
`;
document.getElementById('quizRestart').addEventListener('click', () => quizBegin(quizState.category, quizState.subtopic));
document.getElementById('quizMore').addEventListener('click', () => quizRenderStart());
}
function quizKeyboard(e) {
if (location.hash !== '#/quiz') {
document.removeEventListener('keydown', quizKeyboard);
return;
}
const tag = document.activeElement?.tagName;
if (tag === 'INPUT' || tag === 'TEXTAREA') return;
// 1-4 to select option, Enter to advance, Esc to cancel
if (!quizState.finished && quizState.questions.length > 0) {
if (e.key === '1' || e.key === '2' || e.key === '3' || e.key === '4') {
const idx = parseInt(e.key, 10) - 1;
if (!quizState.answered) {
const q = quizState.questions[quizState.current];
if (q && idx < q.options.length) {
e.preventDefault();
quizSelect(idx);
}
}
} else if ((e.key === 'Enter' || e.key === ' ') && quizState.answered) {
e.preventDefault();
quizAdvance();
}
}
}
async function quizInit() {
await quizLoad();
quizRenderStart();
document.removeEventListener('keydown', quizKeyboard);
document.addEventListener('keydown', quizKeyboard);
}
SMF.quizInit = quizInit;