1import { el } from "../js/utils/dom.js"; 2import { initParallaxBackground } from "../js/parallax-background.js"; 3 4initParallaxBackground(); 5 6// Initialize the page 7document.addEventListener('DOMContentLoaded', () => { 8 initPublications(); 9}); 10 11// Publications section 12function initPublications() { 13 const container = document.getElementById("publicationsList"); 14 const countElement = document.getElementById("pubCount"); 15 16 if (!container) return; 17 18 fetch("../data/publications.json", { cache: "no-store" }) 19 .then((r) => r.json()) 20 .then((items) => { 21 container.innerHTML = ""; 22 23 if (!Array.isArray(items) || items.length === 0) { 24 container.innerHTML = '<div class="text-center text-slate-500 py-8">No publications found.</div>'; 25 if (countElement) countElement.textContent = "0 publications"; 26 return; 27 } 28 29 // Sort by year (newest first) 30 items.sort((a, b) => (b.year || 0) - (a.year || 0)); 31 32 // Group by year 33 const groupedByYear = items.reduce((acc, pub) => { 34 const year = pub.year || 'Unknown'; 35 if (!acc[year]) acc[year] = []; 36 acc[year].push(pub); 37 return acc; 38 }, {}); 39 40 // Render publications grouped by year 41 Object.entries(groupedByYear) 42 .sort(([a], [b]) =>
42 b - a) // Sort years descending 43 .forEach(([year, publications]) => { 44 // Year header 45 const yearHeader = el( 46 "div", 47 { class: "text-lg font-semibold text-slate-700 dark:text-slate-300 mb-4 pt-6 first:pt-0" }, 48 year 49 ); 50 container.appendChild(yearHeader); 51 52 // Publications for this year 53 publications.forEach((pub) => { 54 const links = []; 55 Object.entries(pub.links || {}).forEach(([k, v]) => { 56 if (v && v !== "#") { 57 links.push( 58 el( 59 "a", 60 { 61 href: v, 62 target: "_blank", 63 rel: "noreferrer", 64 class: "text-xs px-3 py-1.5 rounded-lg border border-slate-200 dark:border-slate-700 underline hover:bg-slate-50 dark:hover:bg-slate-800 transition-colors", 65 }, 66 k 67 ) 68 ); 69 } 70 }); 71 72 const tags = (pub.tags || []).map((t) => 73 el( 74 "span", 75 { 76 class: "text-xs px-2 py-1 rounded-full border border-slate-200 dark:border-slate-700 text-slate-500 bg-slate-50 dark:bg-slate-800", 77 }, 78 t 79 ) 80 ); 81 82 const pubCard = el( 83 "article", 84 { 85 class: "publication-block bg-white dark:bg-slate-900 rounded-xl border border-slate-200 dark:border-slate-700 p-5 shadow-soft", 86 }, 87 // Basic info (always visible) 88 el("div", { class: "font-semibold text-lg mb-2" }, pub.title ?? ""), 89 el( 90 "div", 91 { class: "text-sm text-slate-600 dark:text-slate-300 mb-2" }, 92 pub.authors ?? "" 93 ), 94 el("div", { class: "text-sm font-medium mb-3" }, pub.venue ?? ""), 95 links.length ? el("div", { class: "my-4 flex gap-2 flex-wrap" }, links) : null, 96 tags.length ? el("div", { class: "mb-3 flex gap-2 flex-wrap" }, tags) : null, 97 pub.abstract ? el("details", { class: "mt-4 border-t border-slate-200 dark:border-slate-700 pt-4" }, 98 el("summary", { class: "cursor-pointer text-sm font-medium text-blue-600 dark:text-blue-400" }, "Read abstract"), 99 el("p", { class: "text-sm text-slate-600 dark:text-slate-400 mt-3 leading-relaxed" }, pub.abstract) 100 ) : null 101 ); 102 103 container.appendChild(pubCard); 104 }); 105 }); 106 107 if (countElement) { 108 countElement.textContent = `${items.length} publication${items.length > 1 ? 's' : ''}`; 109 } 110 }) 111 .catch((err) => { 112 console.error("Error loading publications:", err); 113 container.innerHTML = '<div class="text-center text-red-600 dark:text-red-400 py-8">Couldn\'t load publications.</div>'; 114 if (countElement) countElement.textContent = "Error loading"; 115 }); 116}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.