// ===================================================== // Render — markdown loading and lesson rendering // ===================================================== const noteCache = new Map(); async function loadNote(file) { if (noteCache.has(file)) return noteCache.get(file); const res = await fetch(`notes/${file}`); if (!res.ok) throw new Error(`Klarte ikke laste ${file}`); const text = await res.text(); noteCache.set(file, text); return text; } // Split markdown by week sections — looking for headers like "## Uke X — ..." or "# UKE X" function extractWeekSection(markdown, weekId) { const lines = markdown.split('\n'); // Match #, ##, ### at start; "Uke" or "UKE"; the week id as separate token const headerRe = new RegExp(`^#{1,3}\\s+UKE\\s+${weekId}(?!\\d)`, 'i'); let start = -1; let startLevel = 0; for (let i = 0; i < lines.length; i++) { const m = lines[i].match(headerRe); if (m) { start = i; // Count leading hashes const hashMatch = lines[i].match(/^(#+)/); startLevel = hashMatch ? hashMatch[1].length : 2; break; } } if (start === -1) return null; // Find next header at same or higher level that starts a NEW week const nextHeaderRe = new RegExp(`^#{1,${startLevel}}\\s+UKE\\s+\\d+`, 'i'); let end = lines.length; for (let i = start + 1; i < lines.length; i++) { if (nextHeaderRe.test(lines[i])) { end = i; break; } } let section = lines.slice(start, end).join('\n'); // Strip the first heading entirely (we show the title in the lesson-header) section = section.replace(/^#{1,3}\s+[^\n]+\n+/, ''); // Normalize remaining heading levels: bring everything to H2/H3/H4 starting fresh. // Original starting level (after stripped header) was startLevel + 1 typically. // We want the deepest visible header in the section to start at H2. const subLines = section.split('\n'); // Find the minimum heading level inside section let minLevel = Infinity; for (const l of subLines) { const m = l.match(/^(#{1,6})\s/); if (m) minLevel = Math.min(minLevel, m[1].length); } if (minLevel === Infinity) return section; const shift = 2 - minLevel; // bring minLevel up to 2 if (shift !== 0) { section = subLines.map(l => { const m = l.match(/^(#{1,6})\s/); if (!m) return l; const newLevel = Math.min(6, Math.max(1, m[1].length + shift)); return '#'.repeat(newLevel) + l.slice(m[1].length); }).join('\n'); } return section; } // Custom marked renderer for tighter HTML function renderMarkdown(md) { marked.setOptions({ gfm: true, breaks: false, headerIds: true, mangle: false }); return marked.parse(md); } // ============= Home view ============= function renderHome() { const tpl = document.getElementById('t-home').content.cloneNode(true); return tpl; } // ============= Lesson view ============= async function renderLesson(weekId) { const tpl = document.getElementById('t-lesson').content.cloneNode(true); const week = SMF.WEEKS.find(w => w.id === weekId); if (!week) { tpl.getElementById('lessonNum').textContent = 'Ikke funnet'; tpl.getElementById('lessonTitle').textContent = 'Ukjent uke'; return tpl; } const themeColors = SMF.THEME_COLORS[week.theme] || SMF.THEME_COLORS.all; // Header const num = tpl.getElementById('lessonNum'); num.textContent = `Uke ${String(weekId).padStart(2, '0')} · ${themeLabel(week.theme)}`; num.style.color = themeColors.color; const title = tpl.getElementById('lessonTitle'); title.innerHTML = formatTitleWithEm(week.title); // Meta const meta = tpl.getElementById('lessonMeta'); const tag = document.createElement('span'); tag.className = 'tag'; tag.textContent = themeLabel(week.theme); tag.style.setProperty('--theme-color', themeColors.color); tag.style.setProperty('--theme-color-bg', themeColors.bg); meta.appendChild(tag); // Body — load and render const body = tpl.getElementById('lessonBody'); try { const md = await loadNote(week.file); let weekMd = extractWeekSection(md, weekId); if (!weekMd) { // Fallback: entire file (Uke 17 case) weekMd = md.replace(/^#\s+[^\n]+\n+/, ''); } body.innerHTML = renderMarkdown(weekMd); } catch (e) { body.innerHTML = `
Klarte ikke laste ukens innhold: ${e.message}
`; } // Pager const idx = SMF.WEEKS.findIndex(w => w.id === weekId); const prev = idx > 0 ? SMF.WEEKS[idx - 1] : null; const next = idx < SMF.WEEKS.length - 1 ? SMF.WEEKS[idx + 1] : null; const pager = tpl.getElementById('lessonPager'); if (prev) { const a = document.createElement('a'); a.href = `#/uke/${prev.id}`; a.className = 'pager-link pager-link--prev'; a.innerHTML = `Klarte ikke laste tl;dr: ${e.message}
`; } return tpl; } SMF.renderHome = renderHome; SMF.renderLesson = renderLesson; SMF.renderTema = renderTema; SMF.renderTldr = renderTldr; SMF.loadNote = loadNote; SMF.renderMarkdown = renderMarkdown; SMF.extractWeekSection = extractWeekSection; SMF.themeLabel = themeLabel;