/* =================================================== JAESWIFT BLOG — Individual Post Loader Markdown rendering, operator stats, navigation =================================================== */ (function () { 'use strict'; const API = window.location.hostname === 'localhost' ? 'http://localhost:5000' : '/api'; // ─── Simple Markdown Parser ─── function parseMarkdown(md) { let html = md; // Code blocks (```lang ... ```) html = html.replace(/```(\w+)?\n([\s\S]*?)```/g, (_, lang, code) => { const cls = lang ? ` class="language-${lang}"` : ''; return `
${escapeHtml(code.trim())}
`; }); // Inline code html = html.replace(/`([^`]+)`/g, '$1'); // Headers html = html.replace(/^#### (.+)$/gm, '

$1

'); html = html.replace(/^### (.+)$/gm, '

$1

'); html = html.replace(/^## (.+)$/gm, '

$1

'); html = html.replace(/^# (.+)$/gm, '

$1

'); // Bold & Italic html = html.replace(/\*\*\*(.+?)\*\*\*/g, '$1'); html = html.replace(/\*\*(.+?)\*\*/g, '$1'); html = html.replace(/\*(.+?)\*/g, '$1'); // Links html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); // Unordered lists html = html.replace(/^- (.+)$/gm, '
  • $1
  • '); html = html.replace(/(
  • .*<\/li>\n?)+/g, (match) => ``); // Horizontal rule html = html.replace(/^---$/gm, '
    '); // Paragraphs (double newline) html = html.replace(/\n\n(?!<)/g, '

    '); html = '

    ' + html + '

    '; // Clean up empty paragraphs and fix nesting html = html.replace(/

    <(h[1-4]|pre|ul|hr|blockquote)/g, '<$1'); html = html.replace(/<\/(h[1-4]|pre|ul|hr|blockquote)><\/p>/g, ''); html = html.replace(/

    <\/p>/g, ''); html = html.replace(/

    \s*<\/p>/g, ''); return html; } function escapeHtml(str) { return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); } // ─── Stat Pips ─── function buildPips(val, max) { max = max || 5; let html = '

    '; for (let i = 0; i < max; i++) { let cls = 'pip'; if (i < val) { cls += ' filled'; if (val <= 2) cls += ' danger'; else if (val <= 3) cls += ' warn'; } html += '
    '; } html += '
    '; return html; } // ─── 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', () => { const nb = document.getElementById('navbar'); if (nb) nb.classList.toggle('scrolled', window.scrollY > 50); }, { passive: true }); } // ─── Render Operator Stats ─── function renderStats(post) { const el = document.getElementById('postStats'); if (!el) return; const coffee = '☕'.repeat(post.coffee || 0) + '' + '☕'.repeat(5 - (post.coffee || 0)) + ''; el.innerHTML = `
    MOOD ${buildPips(post.mood)}
    ENERGY ${buildPips(post.energy)}
    MOTIVATION ${buildPips(post.motivation)}
    FOCUS ${buildPips(post.focus)}
    DIFFICULTY ${buildPips(post.difficulty)}
    ${post.heart_rate || '---'} BPM
    CAFFEINE ${coffee}
    `; // Animate heartbeat animateHeartbeat(post.heart_rate || 72); } // ─── Heartbeat Animation ─── function animateHeartbeat(bpm) { const el = document.getElementById('bpmValue'); if (!el) return; const interval = 60000 / bpm; setInterval(() => { el.classList.add('pulse'); setTimeout(() => el.classList.remove('pulse'), 200); // Flicker the value slightly const jitter = Math.floor(Math.random() * 5) - 2; el.textContent = bpm + jitter; }, interval); } // ─── Render Metadata Panel ─── function renderMetaPanel(post) { const el = document.getElementById('postMetaPanel'); if (!el) return; el.innerHTML = `
    DATE ${post.date}
    TIME ${post.time_written || 'UNKNOWN'}
    WORDS ${post.word_count || '---'}
    THREAT ${post.threat_level || 'LOW'}
    READ TIME ~${Math.max(1, Math.ceil((post.word_count || 300) / 250))} MIN
    SLUG ${post.slug}
    `; } // ─── Render Navigation ─── function renderNav(allPosts, currentSlug) { const el = document.getElementById('postNav'); if (!el) return; // Sort by date descending const sorted = [...allPosts].sort((a, b) => new Date(b.date) - new Date(a.date)); const idx = sorted.findIndex(p => p.slug === currentSlug); let navHtml = ''; if (idx > 0) { const prev = sorted[idx - 1]; navHtml += ` ← NEWER ${prev.title} `; } if (idx < sorted.length - 1) { const next = sorted[idx + 1]; navHtml += ` OLDER → ${next.title} `; } navHtml += ` ⟐ ALL TRANSMISSIONS `; el.innerHTML = navHtml; } // ─── Load Post ─── async function loadPost() { const params = new URLSearchParams(window.location.search); const slug = params.get('slug'); if (!slug) { document.getElementById('postContent').innerHTML = '
    NO TRANSMISSION ID SPECIFIED
    '; return; } try { // Fetch the specific post const res = await fetch(API + '/posts/' + encodeURIComponent(slug)); if (!res.ok) throw new Error('Post not found'); const post = await res.json(); // Update page title document.title = 'JAESWIFT // ' + post.title.toUpperCase(); // Update header document.getElementById('postTitle').textContent = post.title; document.getElementById('postDate').textContent = post.date; document.getElementById('postTime').textContent = post.time_written || ''; const threatEl = document.getElementById('postThreat'); threatEl.textContent = post.threat_level || 'LOW'; threatEl.className = 'post-threat threat-' + (post.threat_level || 'LOW').toLowerCase(); // Tags const tagsEl = document.getElementById('postTags'); tagsEl.innerHTML = (post.tags || []).map(t => '' + t + '' ).join(''); // Render content const contentEl = document.getElementById('postContent'); contentEl.innerHTML = '
    ' + parseMarkdown(post.content || '') + '
    '; // Animate content in contentEl.style.opacity = '0'; contentEl.style.transform = 'translateY(20px)'; contentEl.style.transition = 'all 0.6s ease'; requestAnimationFrame(() => { contentEl.style.opacity = '1'; contentEl.style.transform = 'translateY(0)'; }); // Sidebar panels renderStats(post); renderMetaPanel(post); // Load all posts for navigation try { const allRes = await fetch(API + '/posts'); const allPosts = await allRes.json(); renderNav(allPosts, slug); } catch (e) { console.warn('Could not load post navigation'); } } catch (err) { // Fallback: try static JSON try { const res2 = await fetch('api/data/posts.json'); const posts = await res2.json(); const post = posts.find(p => p.slug === slug); if (!post) throw new Error('Not found'); document.title = 'JAESWIFT // ' + post.title.toUpperCase(); document.getElementById('postTitle').textContent = post.title; document.getElementById('postDate').textContent = post.date; document.getElementById('postTime').textContent = post.time_written || ''; document.getElementById('postContent').innerHTML = '
    ' + parseMarkdown(post.content || '') + '
    '; renderStats(post); renderMetaPanel(post); renderNav(posts, slug); } catch (e2) { document.getElementById('postContent').innerHTML = '
    TRANSMISSION NOT FOUND — SIGNAL LOST
    '; } } } // ─── Init ─── document.addEventListener('DOMContentLoaded', () => { initClock(); initNavbar(); loadPost(); }); })();