${post.title}
${post.excerpt || (post.content || '').substring(0, 120) + '...'}
/* =================================================== JAESWIFT.XYZ — Main JavaScript Animations, particles, typing, scroll effects =================================================== */ (function () { 'use strict'; // ─── CONFIG ─── const CONFIG = { typingStrings: [ 'Developer // Tinkerer // Builder', 'Self-hosted everything.', 'Linux enthusiast since day one.', 'Building the future, one commit at a time.', 'root@jaeswift:~# echo "Hello, World"', 'Cybersecurity & Infrastructure.', 'AI Agent Operator.', ], typingSpeed: 60, typingDeleteSpeed: 30, typingPause: 2500, particleCount: 80, particleMaxSpeed: 0.3, particleConnectionDist: 120, startTime: Date.now(), }; // ─── UTILITIES ─── const $ = (sel, ctx = document) => ctx.querySelector(sel); const $$ = (sel, ctx = document) => [...ctx.querySelectorAll(sel)]; // ─── NAVBAR ─── function initNavbar() { const navbar = $('#navbar'); const toggle = $('#navToggle'); const menu = $('#navMenu'); const navItems = $$('.nav-item'); // Scroll effect let lastScroll = 0; window.addEventListener('scroll', () => { const scrollY = window.scrollY; navbar.classList.toggle('scrolled', scrollY > 50); lastScroll = scrollY; }, { passive: true }); // Mobile toggle toggle.addEventListener('click', () => { menu.classList.toggle('active'); toggle.classList.toggle('active'); }); // Mobile dropdown toggle if (window.innerWidth <= 768) { navItems.forEach(item => { const link = item.querySelector('.nav-link'); link.addEventListener('click', (e) => { if (item.querySelector('.dropdown')) { e.preventDefault(); item.classList.toggle('active'); } }); }); } // Active link on scroll const sections = $$('section[id]'); window.addEventListener('scroll', () => { const scrollPos = window.scrollY + 100; sections.forEach(section => { const top = section.offsetTop; const height = section.offsetHeight; const id = section.getAttribute('id'); if (scrollPos >= top && scrollPos < top + height) { $$('.nav-link').forEach(l => l.classList.remove('active')); const activeLink = $(`.nav-link[href="#${id}"]`); if (activeLink) activeLink.classList.add('active'); } }); }, { passive: true }); // Smooth scroll for nav links $$('.nav-link, .dropdown a[href^="#"]').forEach(link => { link.addEventListener('click', (e) => { const href = link.getAttribute('href'); if (href && href.startsWith('#')) { e.preventDefault(); const target = $(href); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); menu.classList.remove('active'); toggle.classList.remove('active'); } } }); }); } // ─── LIVE CLOCK ─── function initClock() { const navTime = $('#navTime'); if (!navTime) return; function update() { const now = new Date(); const h = String(now.getHours()).padStart(2, '0'); const m = String(now.getMinutes()).padStart(2, '0'); const s = String(now.getSeconds()).padStart(2, '0'); navTime.textContent = `${h}:${m}:${s}`; } update(); setInterval(update, 1000); } // ─── UPTIME COUNTER ─── function initUptime() { const uptimeEl = $('#uptime'); if (!uptimeEl) return; function update() { const elapsed = Date.now() - CONFIG.startTime; const totalSec = Math.floor(elapsed / 1000); const days = Math.floor(totalSec / 86400); const hours = Math.floor((totalSec % 86400) / 3600); const mins = Math.floor((totalSec % 3600) / 60); uptimeEl.textContent = `${days}d ${String(hours).padStart(2, '0')}h ${String(mins).padStart(2, '0')}m`; } update(); setInterval(update, 60000); } // ─── TYPING EFFECT ─── function initTyping() { const el = $('#typingText'); if (!el) return; let stringIndex = 0; let charIndex = 0; let isDeleting = false; function type() { const current = CONFIG.typingStrings[stringIndex]; if (isDeleting) { el.textContent = current.substring(0, charIndex - 1); charIndex--; } else { el.textContent = current.substring(0, charIndex + 1); charIndex++; } let delay = isDeleting ? CONFIG.typingDeleteSpeed : CONFIG.typingSpeed; if (!isDeleting && charIndex === current.length) { delay = CONFIG.typingPause; isDeleting = true; } else if (isDeleting && charIndex === 0) { isDeleting = false; stringIndex = (stringIndex + 1) % CONFIG.typingStrings.length; delay = 400; } setTimeout(type, delay); } setTimeout(type, 1200); } // ─── SCROLL REVEAL ─── function initScrollReveal() { const elements = $$('[data-animate]'); if (!elements.length) return; const observer = new IntersectionObserver((entries) => { entries.forEach((entry, index) => { if (entry.isIntersecting) { setTimeout(() => { entry.target.classList.add('visible'); }, index * 100); observer.unobserve(entry.target); } }); }, { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }); elements.forEach(el => observer.observe(el)); } // ─── SKILL BARS ANIMATION ─── function initSkillBars() { const bars = $$('.bar-fill'); if (!bars.length) return; const observer = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const bar = entry.target; const width = bar.getAttribute('data-width'); bar.style.setProperty('--target-width', width + '%'); bar.classList.add('animated'); observer.unobserve(bar); } }); }, { threshold: 0.5 }); bars.forEach(bar => observer.observe(bar)); } // ─── PARTICLE SYSTEM ─── function initParticles() { const canvas = $('#particles'); if (!canvas) return; const ctx = canvas.getContext('2d'); let particles = []; let animationId; let mouseX = -1000; let mouseY = -1000; function resize() { canvas.width = window.innerWidth; canvas.height = window.innerHeight; } function createParticle() { return { x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * CONFIG.particleMaxSpeed, vy: (Math.random() - 0.5) * CONFIG.particleMaxSpeed, size: Math.random() * 1.5 + 0.5, opacity: Math.random() * 0.5 + 0.1, }; } function initParticleArray() { particles = []; for (let i = 0; i < CONFIG.particleCount; i++) { particles.push(createParticle()); } } function drawParticle(p) { ctx.beginPath(); ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2); ctx.fillStyle = `rgba(0, 255, 65, ${p.opacity})`; ctx.fill(); } function drawConnections() { for (let i = 0; i < particles.length; i++) { for (let j = i + 1; j < particles.length; j++) { const dx = particles[i].x - particles[j].x; const dy = particles[i].y - particles[j].y; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < CONFIG.particleConnectionDist) { const opacity = (1 - dist / CONFIG.particleConnectionDist) * 0.15; ctx.beginPath(); ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.strokeStyle = `rgba(0, 255, 65, ${opacity})`; ctx.lineWidth = 0.5; ctx.stroke(); } } } // Mouse connections particles.forEach(p => { const dx = p.x - mouseX; const dy = p.y - mouseY; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 200) { const opacity = (1 - dist / 200) * 0.3; ctx.beginPath(); ctx.moveTo(p.x, p.y); ctx.lineTo(mouseX, mouseY); ctx.strokeStyle = `rgba(0, 255, 65, ${opacity})`; ctx.lineWidth = 0.8; ctx.stroke(); } }); } function update() { particles.forEach(p => { p.x += p.vx; p.y += p.vy; // Wrap around if (p.x < 0) p.x = canvas.width; if (p.x > canvas.width) p.x = 0; if (p.y < 0) p.y = canvas.height; if (p.y > canvas.height) p.y = 0; // Mouse repulsion const dx = p.x - mouseX; const dy = p.y - mouseY; const dist = Math.sqrt(dx * dx + dy * dy); if (dist < 100) { const force = (100 - dist) / 100 * 0.02; p.vx += (dx / dist) * force; p.vy += (dy / dist) * force; } // Speed limit const speed = Math.sqrt(p.vx * p.vx + p.vy * p.vy); if (speed > CONFIG.particleMaxSpeed * 2) { p.vx *= 0.98; p.vy *= 0.98; } }); } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); update(); drawConnections(); particles.forEach(drawParticle); animationId = requestAnimationFrame(animate); } // Event listeners window.addEventListener('resize', () => { resize(); initParticleArray(); }); window.addEventListener('mousemove', (e) => { mouseX = e.clientX; mouseY = e.clientY; }, { passive: true }); window.addEventListener('mouseleave', () => { mouseX = -1000; mouseY = -1000; }); // Visibility API - pause when tab hidden document.addEventListener('visibilitychange', () => { if (document.hidden) { cancelAnimationFrame(animationId); } else { animate(); } }); resize(); initParticleArray(); animate(); } // ─── GLITCH EFFECT (random intensification) ─── function initGlitchEffect() { const glitch = $('.glitch'); if (!glitch) return; setInterval(() => { glitch.classList.add('glitch-active'); setTimeout(() => glitch.classList.remove('glitch-active'), 200); }, 5000 + Math.random() * 5000); } // ─── TERMINAL ANIMATION ─── function initTerminal() { const terminal = $('#terminal'); if (!terminal) return; const commands = [ { prompt: 'uptime', output: ' up 47 days, 3:22, 1 user, load: 0.12' }, { prompt: 'df -h / | tail -1', output: '/dev/sda1 50G 12G 36G 25% /' }, { prompt: 'curl -s ifconfig.me', output: '███.███.███.███' }, { prompt: 'docker ps --format "table {{.Names}}"', output: 'gitea\nnginx-proxy\nagent-zero\nmonitoring' }, { prompt: 'echo $SHELL', output: '/bin/zsh' }, { prompt: 'neofetch --off | head -3', output: 'OS: Debian GNU/Linux 12\nKernel: 6.1.0-18-amd64\nUptime: 47 days, 3 hours' }, ]; let cmdIndex = 0; function addTermLine(content, isOutput = false) { const line = document.createElement('div'); line.className = 'term-line' + (isOutput ? ' term-output' : ''); if (isOutput) { line.textContent = content; } else { line.innerHTML = `jae@swift:~$ ${content}`; } // Insert before the last line (cursor line) const cursorLine = terminal.lastElementChild; terminal.insertBefore(line, cursorLine); } function typeCommand() { if (cmdIndex >= commands.length) cmdIndex = 0; const cmd = commands[cmdIndex]; let charIdx = 0; // Remove old cursor line, add new command line being typed const cursorLine = terminal.lastElementChild; const typingLine = document.createElement('div'); typingLine.className = 'term-line'; typingLine.innerHTML = `jae@swift:~$ █`; terminal.insertBefore(typingLine, cursorLine); cursorLine.remove(); const cmdSpan = typingLine.querySelector('.term-cmd'); function typeChar() { if (charIdx < cmd.prompt.length) { cmdSpan.textContent += cmd.prompt[charIdx]; charIdx++; setTimeout(typeChar, 50 + Math.random() * 80); } else { // Remove cursor from typing line const cursor = typingLine.querySelector('.term-cursor'); if (cursor) cursor.remove(); // Show output setTimeout(() => { const outputLines = cmd.output.split('\n'); outputLines.forEach(line => { const outputEl = document.createElement('div'); outputEl.className = 'term-line term-output'; outputEl.textContent = line; terminal.appendChild(outputEl); }); // Add new cursor line const newCursor = document.createElement('div'); newCursor.className = 'term-line'; newCursor.innerHTML = `jae@swift:~$ █`; terminal.appendChild(newCursor); // Scroll terminal terminal.scrollTop = terminal.scrollHeight; // Keep only last ~15 lines while (terminal.children.length > 15) { terminal.removeChild(terminal.firstChild); } cmdIndex++; }, 300); } } typeChar(); } // Run terminal animation every 6-10 seconds setInterval(typeCommand, 8000 + Math.random() * 4000); } // ─── CONTACT FORM ─── function initContactForm() { const form = $('#contactForm'); if (!form) return; form.addEventListener('submit', async (e) => { e.preventDefault(); const btn = form.querySelector('.form-submit'); const submitText = btn.querySelector('.submit-text'); const originalText = submitText.textContent; const name = $('#contactName').value.trim(); const email = $('#contactEmail').value.trim(); const subject = $('#contactSubject').value.trim(); const message = $('#contactMessage').value.trim(); if (!name || !email || !message) return; submitText.textContent = 'TRANSMITTING...'; btn.disabled = true; btn.style.borderColor = 'var(--warning)'; btn.style.color = 'var(--warning)'; try { const payload = { name: name, email: email, message: subject ? `[${subject}] ${message}` : message }; const res = await fetch('/api/contact', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); const data = await res.json(); if (res.ok) { submitText.textContent = '✓ TRANSMITTED'; btn.style.borderColor = 'var(--accent)'; btn.style.color = 'var(--accent)'; form.reset(); } else { submitText.textContent = '✗ FAILED'; btn.style.borderColor = '#ff2d2d'; btn.style.color = '#ff2d2d'; } } catch (err) { submitText.textContent = '✗ ERROR'; btn.style.borderColor = '#ff2d2d'; btn.style.color = '#ff2d2d'; } setTimeout(() => { submitText.textContent = originalText; btn.disabled = false; btn.style.borderColor = ''; btn.style.color = ''; }, 2500); }); } // ─── FOOTER SIGNAL ANIMATION ─── function initFooterSignal() { const signal = $('#footerSignal'); if (!signal) return; const blocks = ['░', '█']; function update() { const strength = 6 + Math.floor(Math.random() * 4); // 6-9 out of 10 let bar = ''; for (let i = 0; i < 10; i++) { bar += i < strength ? blocks[1] : blocks[0]; } signal.textContent = `SIGNAL: ${bar} ${strength * 10}%`; } setInterval(update, 3000); } // ─── RANDOM HUD DATA FLICKERS ─── function initHUDFlickers() { const statValues = $$('.stat-value:not(.stat-online)'); setInterval(() => { statValues.forEach(el => { if (Math.random() > 0.7) { el.style.opacity = '0.3'; setTimeout(() => { el.style.opacity = '1'; }, 100 + Math.random() * 200); } }); }, 2000); } // ─── PANEL HOVER GLOW TRAIL ─── function initPanelGlow() { $$('.panel, .blog-card, .dev-card, .link-card').forEach(card => { card.addEventListener('mousemove', (e) => { const rect = card.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; card.style.setProperty('--mouse-x', `${x}px`); card.style.setProperty('--mouse-y', `${y}px`); card.style.background = `radial-gradient(circle 200px at ${x}px ${y}px, rgba(0, 255, 65, 0.04), var(--bg-panel))`; }); card.addEventListener('mouseleave', () => { card.style.background = ''; }); }); } // ─── INIT ─── // ─── NETWORK GRAPH ─── function initNetworkGraph() { const canvas = document.getElementById('networkGraph'); if (!canvas) return; const ctx = canvas.getContext('2d'); function resize() { const rect = canvas.parentElement.getBoundingClientRect(); canvas.width = rect.width; canvas.height = 100; } resize(); window.addEventListener('resize', resize); const dlData = []; const ulData = []; const maxPoints = 80; // Seed initial data for (let i = 0; i < maxPoints; i++) { dlData.push(30 + Math.random() * 40); ulData.push(20 + Math.random() * 30); } function drawLine(data, color, alpha) { const w = canvas.width; const h = canvas.height; const step = w / (maxPoints - 1); ctx.beginPath(); ctx.strokeStyle = color; ctx.lineWidth = 1.5; ctx.globalAlpha = alpha; for (let i = 0; i < data.length; i++) { const x = i * step; const y = h - (data[i] / 100) * h; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.stroke(); // Fill under the line ctx.lineTo(canvas.width, canvas.height); ctx.lineTo(0, canvas.height); ctx.closePath(); ctx.fillStyle = color; ctx.globalAlpha = alpha * 0.08; ctx.fill(); ctx.globalAlpha = 1; } function drawGrid() { const w = canvas.width; const h = canvas.height; ctx.strokeStyle = 'rgba(0,255,200,0.06)'; ctx.lineWidth = 0.5; // Horizontal for (let y = 0; y < h; y += 20) { ctx.beginPath(); ctx.moveTo(0, y); ctx.lineTo(w, y); ctx.stroke(); } // Vertical for (let x = 0; x < w; x += 30) { ctx.beginPath(); ctx.moveTo(x, 0); ctx.lineTo(x, h); ctx.stroke(); } } function updateStats() { const dl = dlData[dlData.length - 1]; const ul = ulData[ulData.length - 1]; const dlEl = document.getElementById('dlSpeed'); const ulEl = document.getElementById('ulSpeed'); const pkEl = document.getElementById('packetCount'); if (dlEl) dlEl.textContent = (dl * 0.032).toFixed(1) + ' Gbps'; if (ulEl) ulEl.textContent = (ul * 0.028).toFixed(1) + ' Gbps'; if (pkEl) pkEl.textContent = Math.floor(dl * 180).toLocaleString() + ' pkt/s'; } function animate() { ctx.clearRect(0, 0, canvas.width, canvas.height); drawGrid(); // Push new data const lastDl = dlData[dlData.length - 1]; const lastUl = ulData[ulData.length - 1]; dlData.push(Math.max(10, Math.min(95, lastDl + (Math.random() - 0.48) * 12))); ulData.push(Math.max(5, Math.min(85, lastUl + (Math.random() - 0.48) * 10))); if (dlData.length > maxPoints) dlData.shift(); if (ulData.length > maxPoints) ulData.shift(); drawLine(dlData, '#00ff41', 0.9); drawLine(ulData, '#00a8ff', 0.6); updateStats(); requestAnimationFrame(animate); } animate(); } // ─── METRIC BARS (fluctuating) ─── function initMetricBars() { const metrics = [ { bar: 'cpuBar', val: 'cpuVal', base: 23, range: 18 }, { bar: 'memBar', val: 'memVal', base: 67, range: 8 }, { bar: 'diskBar', val: 'diskVal', base: 45, range: 25 }, { bar: 'bwBar', val: 'bwVal', base: 78, range: 15 }, ]; function updateMetrics() { metrics.forEach(m => { const barEl = document.getElementById(m.bar); const valEl = document.getElementById(m.val); if (!barEl || !valEl) return; const val = Math.max(5, Math.min(98, m.base + (Math.random() - 0.5) * m.range)); barEl.style.width = val + '%'; valEl.textContent = Math.round(val) + '%'; // Colour shift for high values if (val > 85) { barEl.style.background = 'linear-gradient(90deg, #ff2d2d, rgba(255,71,87,0.4))'; barEl.style.boxShadow = '0 0 8px rgba(255,71,87,0.4)'; valEl.style.color = '#ff2d2d'; } else if (val > 70) { barEl.style.background = 'linear-gradient(90deg, #c9a227, rgba(255,165,2,0.4))'; barEl.style.boxShadow = '0 0 8px rgba(255,165,2,0.3)'; valEl.style.color = '#c9a227'; } else { barEl.style.background = ''; barEl.style.boxShadow = ''; valEl.style.color = ''; } }); } updateMetrics(); setInterval(updateMetrics, 2000 + Math.random() * 1000); } // ─── SCAN BAR ─── function initScanBar() { const fill = document.getElementById('scanFill'); const pct = document.getElementById('scanPct'); if (!fill || !pct) return; let progress = 0; function tick() { progress += 0.5 + Math.random() * 1.5; if (progress >= 100) { progress = 0; fill.style.transition = 'none'; fill.style.width = '0%'; pct.textContent = '0%'; setTimeout(() => { fill.style.transition = 'width 0.3s linear'; requestAnimationFrame(tick); }, 800); return; } fill.style.width = progress + '%'; pct.textContent = Math.round(progress) + '%'; setTimeout(tick, 80 + Math.random() * 120); } fill.style.transition = 'width 0.3s linear'; tick(); } // ─── POWER FLICKER ─── function initPowerFlicker() { const el = document.getElementById('powerPct'); if (!el) return; setInterval(() => { const vals = [97, 98, 99, 98, 100, 98, 97, 99]; const v = vals[Math.floor(Math.random() * vals.length)]; el.innerHTML = v + '%'; }, 4000); } // ─── SERVER HEALTH ─── function initServerHealth() { const el = document.getElementById('serverHealth'); if (!el) return; setInterval(() => { const v = 95 + Math.floor(Math.random() * 5); el.textContent = v + '%'; }, 5000); } // ─── API CONFIG ─── const API_BASE = window.location.hostname === 'localhost' ? 'http://localhost:5000' : '/api'; // ─── BLOG FEED (dynamic from API) ─── function initBlogFeed() { const grid = document.getElementById('blogGrid'); if (!grid) return; fetch(API_BASE + '/posts') .then(r => r.json()) .then(posts => { posts.sort((a, b) => new Date(b.date) - new Date(a.date)); const latest = posts.slice(0, 3); if (latest.length === 0) { grid.innerHTML = '
${post.excerpt || (post.content || '').substring(0, 120) + '...'}