| layout | page |
|---|---|
| title | Timeline |
| permalink | /timeline/ |
| description | PhD journey & life milestones |
| header-img | img/bg-pingping2.jpg |
<div class="tl-controls">
<div class="tl-search"><input id="searchInput" type="search" placeholder="Search (e.g., ‘paper’, ‘Trondheim’, ‘CCUS’)"></div>
<div class="tl-chips" id="chipRow">
<!-- Chips will be injected. Default set lives in JS below. -->
</div>
</div>
Presented early CCS results and got feedback", image: "/img/post-bg-halting.jpg", alt: "Presenter at a conference podium" }, { date: "2023-07", title: "First Summer trips in Italy and Basque", location: "Italy", categories: ["Travel","Personal","Journey"], description: "", image: "/img/post-bg-web.jpg", alt: "Trip in Italy" }, { date: "2023-10", title: "Autumn School about Brightway & LCA ", location: "Energy & AI (under review)", categories: ["PhD", "Studying","Research","Journey"], description: "", image: "/img/post-bg-2015.jpg", alt: "Working photo" }, { date: "2024-01", title: "Travel in China with my boyfriend", location: "Beijing, CN", categories: ["Travel","Personal", "Journey"], description: "", image: "/img/post-bg-alitrip.jpg", alt: "Selfie in Beijing" }, { date: "2024-04", title: "Completed review paper about prospective assessments", location: "doi", categories: ["Publications", "PhD","Research","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-06", title: "First presentation at ESCAPE34", location: "Florence, IT", categories: ["Publications", "PhD","Research","Conference","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-08", title: "Bornout and 'Lost'", location: "Vienna, AT", categories: ["Personal","PhD",,"Bornout","Journey"], details: "
A hard stretch: poor focus, low mood. What helped:", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-10", title: "Autumn School about Energy systems modelling", location: "Gothenburg, SE", categories: ["Studying","PhD","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-02", title: "Completed my skiing lessons at Alps", location: "Bad Gastein, AT", categories: ["Sports","Personal","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-04", title: "First Conference and Trip in California", location: "Palm Springs, CA", categories: ["PhD","Conference","Personal","Journey"], description: "", image: "/img/timeline/USA2025.jpg", alt: "arXiv logo on a screen" }, { date: "2025-08", title: "Summer trip in Italy and Rebuilding", location: "Sardinia, IT", categories: ["Personal","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-10", title: "JuliaCon Paris and trip", location: "Paris, FR", categories: ["Conference","Travel","Personal","Journey"], description: "xxx", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" } ]; // Default categories available as filter chips. const defaultChips = ["All", "Journey", "PhD", "Research", "Publications", "Conference", "Studying", "Workshop", "Travel", "Sports", "Personal", "Burnout"]; // ===== 2) Utility functions ===== const qs = (sel, el = document) => el.querySelector(sel); const qsa = (sel, el = document) => Array.from(el.querySelectorAll(sel)); // Robust date parsing + YYYY-MM display function parseDateParts(iso) { // Supports YYYY, YYYY-M, YYYY-MM, YYYY-MM-D, YYYY-MM-DD const m = String(iso).trim().match(/^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?$/); if (!m) return { y: NaN, m: NaN, d: NaN }; return { y: +m[1], m: +(m[2] || 1), d: +(m[3] || 1) }; } function toDate(iso) { const { y, m, d } = parseDateParts(iso); return isNaN(y) ? new Date(iso) : new Date(y, (m || 1) - 1, d || 1); } const pad2 = (n) => String(n).padStart(2, "0"); function fmtYYYYMM(iso) { const { y, m } = parseDateParts(iso); if (isNaN(y)) return String(iso); return `${y}-${pad2(m || 1)}`; } const fmtDate = fmtYYYYMM; // show "YYYY-MM" const byDateDesc = (a, b) => toDate(b.date) - toDate(a.date); const getYear = (iso) => toDate(iso).getFullYear(); // Parse URL params for deep-linking const url = new URL(window.location); const initCat = url.searchParams.get('cat') || 'All'; const initQuery = url.searchParams.get('q') || ''; // ===== 3) Render chips ===== const chipRow = qs('#chipRow'); let activeCat = initCat; defaultChips.forEach(cat => { const b = document.createElement('button'); b.className = 'chip' + (cat === activeCat ? ' active' : ''); b.textContent = cat; b.setAttribute('data-cat', cat); b.addEventListener('click', () => { activeCat = cat; qsa('.chip', chipRow).forEach(c => c.classList.remove('active')); b.classList.add('active'); render(); const p = new URLSearchParams(url.search); p.set('cat', activeCat); history.replaceState({}, '', `${url.pathname}?${p.toString()}`); }); chipRow.appendChild(b); }); // ===== 4) Search box ===== const searchInput = qs('#searchInput'); searchInput.value = initQuery; searchInput.addEventListener('input', () => { const p = new URLSearchParams(url.search); p.set('q', searchInput.value); history.replaceState({}, '', `${url.pathname}?${p.toString()}`); render(); }); // ===== 5) Lightbox ===== const lb = qs('#lightbox'); lb.addEventListener('click', () => { lb.style.display = 'none'; lb.setAttribute('aria-hidden', 'true'); }); function openLightbox(src, alt) { const img = lb.querySelector('img'); img.src = src; img.alt = alt || ''; lb.style.display = 'flex'; lb.setAttribute('aria-hidden', 'false'); }
${ev.description}
` : 'No extra details.
'); pop.style.display = 'flex'; pop.setAttribute('aria-hidden', 'false'); } function closePopup() { const pop = qs('#popup'); pop.style.display = 'none'; pop.setAttribute('aria-hidden', 'true'); } // Close interactions qs('#popup').addEventListener('click', (e) => { // click on dimmed overlay closes; clicks inside card do not if (e.target.id === 'popup') closePopup(); }); qs('#popupClose').addEventListener('click', closePopup); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePopup(); }); function render() { const root = qs('#timeline'); root.innerHTML = ''; const term = searchInput.value.trim().toLowerCase(); const filtered = events .slice() .sort(byDateDesc) .filter(ev => { const matchCat = (activeCat === 'All') || (ev.categories || []).includes(activeCat); if (!matchCat) return false; if (!term) return true; const hay = [ev.title, ev.location, ev.description, ...(ev.categories||[])].join(' ').toLowerCase(); return hay.includes(term); }); if (!filtered.length) { const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'No items match your filters. Try another category or search term.'; root.appendChild(empty); return; } let currentYear = null; filtered.forEach(ev => { const y = getYear(ev.date); if (y !== currentYear) { currentYear = y; const yg = document.createElement('div'); yg.className = 'year-group'; yg.textContent = y; root.appendChild(yg); } const item = document.createElement('div'); item.className = 'item'; const colL = document.createElement('div'); const dot = document.createElement('div'); dot.className = 'dot'; const date = document.createElement('div'); date.className = 'date'; date.textContent = fmtDate(ev.date); colL.appendChild(dot); colL.appendChild(date); const colR = document.createElement('div'); const card = document.createElement('div'); card.className = 'card'; const h3 = document.createElement('h3'); const tbtn = document.createElement('button'); tbtn.className = 'link-title'; tbtn.type = 'button'; tbtn.textContent = ev.title; tbtn.addEventListener('click', () => openPopup(ev)); h3.appendChild(tbtn); card.appendChild(h3); const meta = document.createElement('div'); meta.className = 'meta'; if (ev.location) { const loc = document.createElement('div'); loc.textContent = '📍 ' + ev.location; meta.appendChild(loc); } if (ev.categories && ev.categories.length) { const cats = document.createElement('div'); cats.className = 'badges'; ev.categories.forEach(c => { const b = document.createElement('span'); b.className = 'badge'; b.textContent = c; cats.appendChild(b); }); meta.appendChild(cats); } card.appendChild(meta); if (ev.image) { const img = document.createElement('img'); img.className = 'thumb'; img.src = ev.image; img.alt = ev.alt || ''; img.addEventListener('click', () => openLightbox(ev.image, ev.alt)); card.appendChild(img); } if (ev.description) { const p = document.createElement('div'); p.className = 'desc'; p.textContent = ev.description; card.appendChild(p); } colR.appendChild(card); item.appendChild(colL); item.appendChild(colR); root.appendChild(item); }); } // Initial render render(); </script>