/* AegisSight Monitor - Product Page v2 */ (function () { 'use strict'; /* ==================== NAVBAR ==================== */ var navbar = document.getElementById('navbar'); window.addEventListener('scroll', function () { navbar.classList.toggle('scrolled', window.scrollY > 10); }); /* ==================== MOBILE MENU ==================== */ var toggle = document.querySelector('.mobile-menu-toggle'); var menu = document.getElementById('mobile-menu'); var overlay = document.getElementById('mobile-overlay'); function closeMenu() { toggle.classList.remove('active'); menu.classList.remove('open'); overlay.classList.remove('open'); toggle.setAttribute('aria-expanded', 'false'); } toggle.addEventListener('click', function () { var isOpen = menu.classList.contains('open'); if (isOpen) { closeMenu(); } else { toggle.classList.add('active'); menu.classList.add('open'); overlay.classList.add('open'); toggle.setAttribute('aria-expanded', 'true'); } }); overlay.addEventListener('click', closeMenu); menu.querySelectorAll('a').forEach(function (l) { l.addEventListener('click', closeMenu); }); /* ==================== SMOOTH SCROLL ==================== */ document.querySelectorAll('a[href^="#"]').forEach(function (link) { link.addEventListener('click', function (e) { var t = document.querySelector(this.getAttribute('href')); if (t) { e.preventDefault(); t.scrollIntoView({ behavior: 'smooth' }); } }); }); /* ==================== HERO VIDEOS ==================== */ var videos = document.querySelectorAll('.hero-video'); var currentVideo = 0; var rotationTimer; function switchVideo() { videos[currentVideo].classList.remove('active'); currentVideo = (currentVideo + 1) % videos.length; var next = videos[currentVideo]; next.currentTime = 0; next.play().catch(function () {}); next.classList.add('active'); } function startRotation() { rotationTimer = setInterval(switchVideo, 12000); } document.addEventListener('visibilitychange', function () { if (document.hidden) { clearInterval(rotationTimer); videos.forEach(function (v) { v.pause(); }); } else { videos.forEach(function (v) { if (v.classList.contains('active')) v.play().catch(function () {}); }); startRotation(); } }); if (videos.length > 1) startRotation(); /* ==================== 3D CAROUSEL ==================== */ var cards = document.querySelectorAll('.carousel-card'); var dots = document.querySelectorAll('.carousel-dot'); var activeIndex = 0; function positionCards(idx) { activeIndex = idx; cards.forEach(function (card, i) { card.classList.remove('active', 'left', 'right', 'hidden'); if (i === idx) card.classList.add('active'); else if (i === (idx - 1 + cards.length) % cards.length) card.classList.add('left'); else if (i === (idx + 1) % cards.length) card.classList.add('right'); else card.classList.add('hidden'); }); dots.forEach(function (dot, i) { dot.classList.toggle('active', i === idx); }); } cards.forEach(function (card, i) { card.addEventListener('click', function () { if (!card.classList.contains('active')) positionCards(i); }); }); dots.forEach(function (dot, i) { dot.addEventListener('click', function () { positionCards(i); }); }); positionCards(0); // Arrow navigation var prevBtn = document.querySelector('.carousel-prev'); var nextBtn = document.querySelector('.carousel-next'); if (prevBtn) prevBtn.addEventListener('click', function () { positionCards((activeIndex - 1 + cards.length) % cards.length); }); if (nextBtn) nextBtn.addEventListener('click', function () { positionCards((activeIndex + 1) % cards.length); }); /* ==================== SIMPLE MARKDOWN ==================== */ function mdToHtml(md) { if (!md) return ''; return md .replace(/^## .+$/gm, '') .replace(/^### .+$/gm, '') .replace(/\*\*(.+?)\*\*/g, '$1') .replace(/\[(\d+[a-z]?)\]/g, '') .replace(/\n{3,}/g, '\n\n') .trim() .split('\n\n') .filter(function (p) { return p.trim().length > 0; }) .map(function (p) { return '

' + p.trim().replace(/\n/g, ' ') + '

'; }) .join(''); } /* ==================== LIVE DATA ==================== */ function timeAgo(dateStr) { var diffMin = Math.floor((Date.now() - new Date(dateStr).getTime()) / 60000); if (diffMin < 1) return 'Gerade eben aktualisiert'; if (diffMin < 60) return 'Aktualisiert vor ' + diffMin + ' Min.'; var diffH = Math.floor(diffMin / 60); if (diffH < 24) return 'Aktualisiert vor ' + diffH + ' Std.'; var diffD = Math.floor(diffH / 24); return 'Aktualisiert vor ' + diffD + (diffD === 1 ? ' Tag' : ' Tagen'); } function loadLiveData() { fetch('/lagen/iran-konflikt/data/summary.json?t=' + Date.now()) .then(function (r) { if (!r.ok) throw new Error(r.status); return r.json(); }) .then(function (data) { var inc = data.incident || {}; // summary.json has flat structure var ea = document.getElementById('stat-articles'); var es = document.getElementById('stat-sources'); var ef = document.getElementById('stat-factchecks'); function countUp(el, target) { if (!el || !target) return; var start = 0; var duration = 1200; var startTime = null; function step(ts) { if (!startTime) startTime = ts; var progress = Math.min((ts - startTime) / duration, 1); var ease = 1 - Math.pow(1 - progress, 3); el.textContent = Math.floor(ease * target).toLocaleString('de-DE'); if (progress < 1) requestAnimationFrame(step); } requestAnimationFrame(step); } countUp(ea, inc.article_count); countUp(es, inc.source_count); countUp(ef, inc.factcheck_count); if (eu && data.updated_at) eu.textContent = timeAgo(data.updated_at); // Excerpt: pre-extracted in summary.json var excerptEl = document.getElementById('excerpt-text'); if (excerptEl && data.zusammenfassung) { excerptEl.innerHTML = mdToHtml(data.zusammenfassung); } // Map if (data.locations && data.locations.length > 0) { initMap(data.locations, data.category_labels || {}); } }) .catch(function () { if (eu) eu.textContent = 'Daten derzeit nicht verfügbar'; }); } /* ==================== LEAFLET MAP (exact lagebild style) ==================== */ function initMap(locations, apiLabels) { var mapEl = document.getElementById('map-container'); if (!mapEl || typeof L === 'undefined') return; var map = L.map(mapEl, { center: [33.0, 48.0], zoom: 5, zoomControl: true, scrollWheelZoom: true, minZoom: 2, maxBounds: [[-85, -180], [85, 180]], maxBoundsViscosity: 1.0 }); L.tileLayer('https://tile.openstreetmap.de/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19, noWrap: true }).addTo(map); // Exact same pulse icon as lagebild function pulseIcon(color) { return L.divIcon({ className: '', html: '
' + '
' + '
' + '
' + '
', iconSize: [20, 20], iconAnchor: [10, 10], popupAnchor: [0, -12] }); } var categoryColors = { primary: '#ef4444', secondary: '#f59e0b', tertiary: '#3b82f6', mentioned: '#7b7b7b' }; var defaultLabels = { primary: 'Hauptgeschehen', secondary: 'Reaktionen', tertiary: 'Beteiligte', mentioned: 'Erwähnt' }; var categoryLabels = {}; ['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function (k) { categoryLabels[k] = (apiLabels && apiLabels[k]) || defaultLabels[k]; }); var usedCategories = {}; var bounds = []; locations.forEach(function (loc) { if (!loc.lat || !loc.lon) return; var cat = loc.category || 'mentioned'; var color = categoryColors[cat] || '#7b7b7b'; usedCategories[cat] = true; var popup = '' + (loc.name || '') + ''; if (loc.country_code) popup += ' (' + loc.country_code + ')'; popup += '
' + (loc.article_count || 0) + ' Artikel'; L.marker([loc.lat, loc.lon], { icon: pulseIcon(color) }) .addTo(map) .bindPopup(popup); bounds.push([loc.lat, loc.lon]); }); // Dark legend (exact lagebild style) var legend = L.control({ position: 'bottomright' }); legend.onAdd = function () { var div = L.DomUtil.create('div'); div.style.cssText = 'background:#151D2E;padding:10px 14px;border-radius:4px;border:1px solid #1E2D45;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:0.8rem;line-height:1.8;color:#E8ECF4;'; var html = 'Legende
'; ['primary', 'secondary', 'tertiary', 'mentioned'].forEach(function (cat) { if (usedCategories[cat]) { html += ' ' + categoryLabels[cat] + '
'; } }); div.innerHTML = html; return div; }; legend.addTo(map); if (bounds.length > 0) { map.fitBounds(bounds, { padding: [30, 30], maxZoom: 7 }); } setTimeout(function () { map.invalidateSize(); }, 500); } /* ==================== INIT ==================== */ loadLiveData(); })();