/* Home dashboard behaviour. All selectors are db-* scoped. */ (function () { "use strict"; function updateGreeting() { const el = document.getElementById("db-greeting"); if (!el) return; const hour = new Date().getHours(); if (hour < 5) el.textContent = "Late session"; else if (hour < 12) el.textContent = "Good morning"; else if (hour < 17) el.textContent = "Good afternoon"; else if (hour < 21) el.textContent = "Good evening"; else el.textContent = "Evening review"; } function updateClock() { const el = document.getElementById("db-clock"); if (!el) return; el.textContent = new Intl.DateTimeFormat(undefined, { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: false, }).format(new Date()); } function getIsoWeek(date) { const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate())); const dayNum = d.getUTCDay() || 7; d.setUTCDate(d.getUTCDate() + 4 - dayNum); const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1)); return Math.ceil(((d - yearStart) / 86400000 + 1) / 7); } function updateStats() { const now = new Date(); const year = now.getFullYear(); const start = new Date(year, 0, 1); const nextYear = new Date(year + 1, 0, 1); const dayOfYear = Math.floor((now - start) / 86400000) + 1; const daysInYear = Math.round((nextYear - start) / 86400000); const daysLeft = Math.max(0, daysInYear - dayOfYear); const month = new Intl.DateTimeFormat(undefined, { month: "short" }).format(now); const pct = ((dayOfYear / daysInYear) * 100).toFixed(1); setText("db-stat-day", dayOfYear); setText("db-stat-week", "W" + getIsoWeek(now)); setText("db-stat-month", month); setText("db-stat-left", daysLeft); setText("db-year-pct", pct + "%"); const fill = document.getElementById("db-year-fill"); if (fill) fill.style.width = pct + "%"; } function setText(id, value) { const el = document.getElementById(id); if (el) el.textContent = value; } function escHtml(str) { return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function normaliseHref(href, prefix) { if (!href || href.startsWith("http") || href.startsWith("#")) return null; const base = (prefix || "").replace(/\/$/, ""); if (!base || href.startsWith(base + "/")) return href; return href.startsWith("/") ? base + href : base + "/" + href; } function extractLinks(doc, max, prefix) { const items = []; const seen = new Set(); const links = doc.querySelectorAll("#content li a[href], .org-ul li a[href], ul li a[href]"); for (const a of links) { const href = normaliseHref(a.getAttribute("href"), prefix); const title = a.textContent.trim(); if (!href || !title || seen.has(href)) continue; const li = a.closest("li"); const dateMatch = li ? li.textContent.match(/\b\d{4}-\d{2}-\d{2}\b/) : null; seen.add(href); items.push({ href, title, date: dateMatch ? dateMatch[0] : null }); if (items.length >= max) break; } return items; } function renderFeed(ulId, items) { const ul = document.getElementById(ulId); if (!ul || !items.length) return; ul.innerHTML = items .map(({ href, title, date }) => ( `