/* =================================================== JAESWIFT BLOG — Post loader + Cyberpunk stats =================================================== */ (function () { 'use strict'; const API = window.location.hostname === 'localhost' ? 'http://localhost:5000' : '/api'; // ─── Clock ─── function initClock() { const el = document.getElementById('navClock'); if (!el) return; const tick = () => { const d = new Date(); el.textContent = d.toLocaleTimeString('en-GB', { hour12: false }) + ' UTC' + (d.getTimezoneOffset() <= 0 ? '+' : '') + (-d.getTimezoneOffset() / 60); }; tick(); setInterval(tick, 1000); } // ─── Navbar ─── function initNavbar() { const toggle = document.getElementById('navToggle'); const menu = document.getElementById('navMenu'); if (toggle && menu) { toggle.addEventListener('click', () => menu.classList.toggle('active')); } window.addEventListener('scroll', () => { document.getElementById('navbar')?.classList.toggle('scrolled', window.scrollY > 50); }, { passive: true }); } // ─── Build Stat Pips ─── function buildPips(val, max = 5) { let html = '
'; for (let i = 0; i < max; i++) { const filled = i < val; let cls = 'stat-pip'; if (filled) { cls += ' filled'; if (val <= 2) cls += ' danger'; else if (val <= 3) cls += ' warn'; } html += `
`; } html += '
'; return html; } // ─── Coffee Icons ─── function buildCoffee(val) { val = Math.max(0, Math.min(5, val || 0)); return '' + '☕'.repeat(val) + '' + '☕'.repeat(5 - val) + ''; } // ─── Render Post Card ─── function renderPostCard(post) { return `
${post.threat_level} ${post.time_written || ''}

${post.title}

${post.excerpt}

${(post.tags || []).map(t => ``).join('')}
OPERATOR STATUS
MOOD ${buildPips(post.mood)}
ENERGY ${buildPips(post.energy)}
MOTIVE ${buildPips(post.motivation)}
FOCUS ${buildPips(post.focus)}
${post.heart_rate} BPM
${buildCoffee(post.coffee)}
`; } // ─── Load Posts ─── async function loadPosts() { const grid = document.getElementById('blogPosts'); if (!grid) return; try { const res = await fetch(API + '/posts'); if (!res.ok) throw new Error('API ' + res.status); const posts = await res.json(); if (!Array.isArray(posts) || posts.length === 0) { grid.innerHTML = '
NO TRANSMISSIONS FOUND
'; return; } // Sort by date descending posts.sort((a, b) => new Date(b.date) - new Date(a.date)); // Render each card with individual error protection let html = ''; posts.forEach((post, idx) => { try { html += renderPostCard(post); } catch (cardErr) { console.error('Card render error for post ' + idx + ':', cardErr); html += '

RENDER ERROR

' + cardErr.message + '

'; } }); grid.innerHTML = html; // Animate cards in const cards = grid.querySelectorAll('.post-card'); cards.forEach((card, i) => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'all 0.5s ease'; setTimeout(() => { card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 100 + i * 150); }); } catch (err) { console.error('Blog primary load error:', err); // Fallback: load from static JSON (absolute path) try { const res2 = await fetch('/api/data/posts.json'); if (!res2.ok) throw new Error('Fallback ' + res2.status); const posts = await res2.json(); posts.sort((a, b) => new Date(b.date) - new Date(a.date)); grid.innerHTML = posts.map(renderPostCard).join(''); const cards = grid.querySelectorAll('.post-card'); cards.forEach((card, i) => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; card.style.transition = 'all 0.5s ease'; setTimeout(() => { card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 100 + i * 150); }); } catch (e2) { console.error('Blog fallback error:', e2); grid.innerHTML = '
TRANSMISSION ERROR: ' + err.message + ' / FALLBACK: ' + e2.message + '
'; } } } function initFilters() { const btns = document.querySelectorAll('.filter-btn'); btns.forEach(btn => { btn.addEventListener('click', () => { btns.forEach(b => b.classList.remove('active')); btn.classList.add('active'); const filter = btn.dataset.filter; const cards = document.querySelectorAll('.post-card'); cards.forEach(card => { const tags = card.dataset.tags || ''; if (filter === 'all' || tags.includes(filter)) { card.style.display = ''; card.style.opacity = '1'; } else { card.style.opacity = '0'; setTimeout(() => { card.style.display = 'none'; }, 300); } }); }); }); } // ─── Init ─── document.addEventListener('DOMContentLoaded', () => { initClock(); initNavbar(); loadPosts(); initFilters(); }); })();