/* 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 }) => ( `
  • ` + `` + `${escHtml(title)}` + (date ? `${escHtml(date)}` : "") + `
  • ` )) .join(""); } async function loadFeed(listUrl, ulId, max, prefix) { try { const resp = await fetch(listUrl, { credentials: "same-origin" }); if (!resp.ok) return; const html = await resp.text(); const doc = new DOMParser().parseFromString(html, "text/html"); renderFeed(ulId, extractLinks(doc, max, prefix)); } catch (_) { /* Fallback links remain in the HTML. */ } } function initCommandFilter() { const input = document.getElementById("db-command-search"); const nav = document.getElementById("db-quicknav"); if (!input || !nav) return; const items = Array.from(nav.querySelectorAll(".db-qn-item")); const applyFilter = () => { const query = input.value.trim().toLowerCase(); items.forEach((item) => { const haystack = [ item.textContent, item.getAttribute("href"), item.dataset.keywords, ].join(" ").toLowerCase(); item.classList.toggle("is-hidden", Boolean(query && !haystack.includes(query))); }); }; input.addEventListener("input", applyFilter); document.addEventListener("keydown", (event) => { if (event.key === "/" && !/^(input|textarea|select)$/i.test(event.target.tagName)) { event.preventDefault(); input.focus(); } }); } function init() { updateGreeting(); updateClock(); updateStats(); initCommandFilter(); setInterval(updateClock, 1000); loadFeed("/blogs/blogs-list.html", "db-feed-blogs", 6, "/blogs"); loadFeed("/posts/posts-list.html", "db-feed-posts", 6, "/posts"); loadFeed("/recently-updated.html", "db-feed-recent", 6, ""); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", init); } else { init(); } })();