// ===================================================== // Flashcards — flippable cards with category filter // ===================================================== let fcData = null; let fcState = { filter: 'all', index: 0, flipped: false, cards: [], known: new Set(), // localStorage-backed hard: new Set() }; async function fcLoad() { if (fcData) return fcData; try { const res = await fetch('data/flashcards.json'); fcData = await res.json(); } catch (e) { console.error('Klarte ikke laste flashcards:', e); fcData = []; } // Load saved state try { const saved = JSON.parse(localStorage.getItem('smf-fc-state') || '{}'); if (saved.known) fcState.known = new Set(saved.known); if (saved.hard) fcState.hard = new Set(saved.hard); } catch {} return fcData; } function fcSaveState() { localStorage.setItem('smf-fc-state', JSON.stringify({ known: [...fcState.known], hard: [...fcState.hard] })); } function fcFilterCards(filter) { if (filter === 'all') return fcData; if (filter === 'hard') return fcData.filter(c => fcState.hard.has(c.id)); if (filter === 'new') return fcData.filter(c => !fcState.known.has(c.id) && !fcState.hard.has(c.id)); return fcData.filter(c => c.category === filter); } function fcSetFilter(filter) { fcState.filter = filter; fcState.cards = fcFilterCards(filter); // shuffle once when filter changes fcShuffle(fcState.cards); fcState.index = 0; fcState.flipped = false; fcRender(); } function fcShuffle(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]]; } } function fcRender() { const total = fcState.cards.length; document.getElementById('fcCurrent').textContent = total > 0 ? (fcState.index + 1) : 0; document.getElementById('fcTotal').textContent = total; document.getElementById('fcKnown').textContent = fcState.known.size; const card = document.getElementById('fcCard'); if (total === 0) { document.getElementById('fcFront').textContent = 'Ingen kort i denne kategorien'; document.getElementById('fcBack').textContent = 'Prøv et annet filter'; document.getElementById('fcCategory').textContent = '—'; card.classList.remove('fc-card--flipped'); return; } const c = fcState.cards[fcState.index]; document.getElementById('fcFront').textContent = c.front; document.getElementById('fcBack').innerHTML = c.back; document.getElementById('fcCategory').textContent = (c.subcategory || categoryLabel(c.category)); card.classList.toggle('fc-card--flipped', fcState.flipped); } function categoryLabel(cat) { return { etikk: 'Etikk', baerekraft: 'Bærekraft', samfunn: 'Samfunnsansvar', verktoy: 'Verktøy', case: 'Case' }[cat] || cat; } function fcRenderFilters() { const container = document.getElementById('fcFilters'); if (!container) return; const filters = [ { id: 'all', label: 'Alle' }, { id: 'new', label: 'Nye' }, { id: 'hard', label: 'Glemt' }, { id: 'etikk', label: 'Etikk' }, { id: 'baerekraft', label: 'Bærekraft' }, { id: 'samfunn', label: 'Samfunnsansvar' }, { id: 'verktoy', label: 'Verktøy' }, { id: 'case', label: 'Case' } ]; container.innerHTML = filters.map(f => { const active = fcState.filter === f.id ? 'fc-filter--active' : ''; return ``; }).join(''); } function fcNext() { if (!fcState.cards.length) return; fcState.flipped = false; fcState.index = (fcState.index + 1) % fcState.cards.length; fcRender(); } function fcPrev() { if (!fcState.cards.length) return; fcState.flipped = false; fcState.index = (fcState.index - 1 + fcState.cards.length) % fcState.cards.length; fcRender(); } function fcMark(level) { if (!fcState.cards.length) return; const c = fcState.cards[fcState.index]; if (level === 'hard') { fcState.hard.add(c.id); fcState.known.delete(c.id); } else if (level === 'good') { fcState.hard.delete(c.id); } else if (level === 'easy') { fcState.known.add(c.id); fcState.hard.delete(c.id); } fcSaveState(); fcNext(); } async function fcInit() { await fcLoad(); fcState.cards = fcFilterCards(fcState.filter); fcShuffle(fcState.cards); fcState.index = 0; fcState.flipped = false; fcRenderFilters(); fcRender(); // Card click → flip const card = document.getElementById('fcCard'); card.addEventListener('click', () => { fcState.flipped = !fcState.flipped; fcRender(); }); document.getElementById('fcNext').addEventListener('click', (e) => { e.stopPropagation(); fcNext(); }); document.getElementById('fcPrev').addEventListener('click', (e) => { e.stopPropagation(); fcPrev(); }); document.getElementById('fcHard').addEventListener('click', (e) => { e.stopPropagation(); fcMark('hard'); }); document.getElementById('fcGood').addEventListener('click', (e) => { e.stopPropagation(); fcMark('good'); }); document.getElementById('fcEasy').addEventListener('click', (e) => { e.stopPropagation(); fcMark('easy'); }); document.getElementById('fcFilters').addEventListener('click', (e) => { const btn = e.target.closest('[data-filter]'); if (btn) { fcSetFilter(btn.dataset.filter); fcRenderFilters(); } }); // Keyboard const handler = (e) => { if (location.hash !== '#/flashcards') { document.removeEventListener('keydown', handler); return; } const tag = document.activeElement?.tagName; if (tag === 'INPUT' || tag === 'TEXTAREA') return; if (e.key === ' ' || e.key === 'Enter') { e.preventDefault(); fcState.flipped = !fcState.flipped; fcRender(); } else if (e.key === 'ArrowRight') { e.preventDefault(); fcNext(); } else if (e.key === 'ArrowLeft') { e.preventDefault(); fcPrev(); } else if (e.key === '1') { fcMark('hard'); } else if (e.key === '2') { fcMark('good'); } else if (e.key === '3') { fcMark('easy'); } }; document.addEventListener('keydown', handler); } SMF.fcInit = fcInit;