/** * AegisSight Lagebild Page - Dark Theme Design Refresh * Count-Up, Timeline, Scroll-Reveal, Particles, Live-Feed, Pulse-Markers */ /** Feste Zeitzone fuer alle Anzeigen - NIEMALS aendern. */ var TIMEZONE = 'Europe/Berlin'; var Lagebild = { data: null, allSnapshots: {}, currentView: null, map: null, timelineGroups: null, /* ===== Inline SVG Icons ===== */ icons: { clock: '', fileText: '', globe: '', shieldCheck: '', externalLink: '' }, async init() { if (typeof initTranslations === 'function') { try { initTranslations(); } catch(e) {} } this.initScrollProgress(); this.initParticles(); try { var resp = await fetch('data/current.json?t=' + Date.now()); if (!resp.ok) throw new Error('HTTP ' + resp.status); this.data = await resp.json(); this.currentView = { summary: this.data.current_lagebild.summary_markdown, sources_json: this.data.current_lagebild.sources_json, updated_at: this.data.current_lagebild.updated_at || this.data.generated_at, articles: this.data.articles, fact_checks: this.data.fact_checks }; this.render(); this.initTabs(); this.initLangToggle(); this.initScrollReveal(); this.initFloatingCta(); this.initLiveFeed(); } catch (e) { console.error('Lagebild laden fehlgeschlagen:', e); this.showError(); } }, render: function() { this.renderHero(); this.renderTimeline(); this.renderTabBadges(); this.renderCurrentView(); }, /* ===== SCROLL PROGRESS BAR ===== */ initScrollProgress: function() { var bar = document.getElementById('scroll-progress'); if (!bar) return; window.addEventListener('scroll', function() { var scrollTop = window.scrollY; var docHeight = document.documentElement.scrollHeight - window.innerHeight; if (docHeight <= 0) return; bar.style.width = ((scrollTop / docHeight) * 100) + '%'; }); }, /* ===== HERO PARTICLES ===== */ initParticles: function() { var canvas = document.getElementById('hero-particles'); if (!canvas) return; var ctx = canvas.getContext('2d'); var particles = []; var count = 35; var connectDist = 120; function resize() { var hero = canvas.parentElement; canvas.width = hero.offsetWidth; canvas.height = hero.offsetHeight; } resize(); window.addEventListener('resize', resize); for (var i = 0; i < count; i++) { particles.push({ x: Math.random() * canvas.width, y: Math.random() * canvas.height, vx: (Math.random() - 0.5) * 0.4, vy: (Math.random() - 0.5) * 0.4, r: Math.random() * 1.5 + 0.5 }); } function draw() { ctx.clearRect(0, 0, canvas.width, canvas.height); // Draw connections for (var i = 0; i < particles.length; i++) { for (var j = i + 1; j < particles.length; j++) { var dx = particles[i].x - particles[j].x; var dy = particles[i].y - particles[j].y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < connectDist) { var alpha = (1 - dist / connectDist) * 0.15; ctx.beginPath(); ctx.strokeStyle = 'rgba(200, 168, 81, ' + alpha + ')'; ctx.lineWidth = 0.5; ctx.moveTo(particles[i].x, particles[i].y); ctx.lineTo(particles[j].x, particles[j].y); ctx.stroke(); } } } // Draw & move particles for (var k = 0; k < particles.length; k++) { var p = particles[k]; ctx.beginPath(); ctx.arc(p.x, p.y, p.r, 0, Math.PI * 2); ctx.fillStyle = 'rgba(200, 168, 81, 0.4)'; ctx.fill(); p.x += p.vx; p.y += p.vy; if (p.x < 0 || p.x > canvas.width) p.vx *= -1; if (p.y < 0 || p.y > canvas.height) p.vy *= -1; } requestAnimationFrame(draw); } draw(); }, /* ===== LIVE FEED TICKER ===== */ initLiveFeed: function() { var container = document.getElementById('live-feed'); if (!container) return; var d = this.data; var genDate = new Date(this.toUTC(d.generated_at)); var diffMin = Math.max(1, Math.round((Date.now() - genDate.getTime()) / 60000)); var diffText = diffMin < 60 ? ('vor ' + diffMin + ' Min') : ('vor ' + Math.round(diffMin / 60) + ' Std'); container.innerHTML = '
' + '' + 'Letzte Aktualisierung: ' + diffText + '' + '
'; }, /* ===== HERO ===== */ renderHero: function() { var d = this.data; document.getElementById('incident-title').innerHTML = this.esc(this.fixUmlauts(d.incident.title)) + ' \u2013 Stand: ' + this.fmtDateOnly(d.generated_at) + ', ' + this.fmtTimeOnly(d.generated_at) + ' Uhr'; // Stat Cards (3: Artikel, Quellen, Faktenchecks) var statsHtml = ''; statsHtml += this.statCard(this.icons.fileText, '0', 'Artikel'); statsHtml += this.statCard(this.icons.globe, '0', 'Quellen'); statsHtml += this.statCard(this.icons.shieldCheck, '0', 'Faktenchecks'); document.getElementById('hero-stats').innerHTML = statsHtml; // Start count-up animations var self = this; requestAnimationFrame(function() { var els = document.querySelectorAll('.count-up'); for (var i = 0; i < els.length; i++) { self.animateCount(els[i], parseInt(els[i].getAttribute('data-target')), 800); } }); }, statCard: function(icon, value, label) { return '
' + '
' + icon + '
' + '
' + '' + value + '' + '' + label + '' + '
'; }, /* ===== COUNT-UP ANIMATION ===== */ animateCount: function(element, target, duration) { var start = performance.now(); function update(now) { var elapsed = now - start; var progress = Math.min(elapsed / duration, 1); var eased = 1 - Math.pow(1 - progress, 3); // easeOutCubic var current = Math.round(target * eased); element.textContent = current.toLocaleString('de-DE'); if (progress < 1) { requestAnimationFrame(update); } } requestAnimationFrame(update); }, /* ===== TIMELINE STRIP ===== */ renderTimeline: function() { var snaps = this.data.available_snapshots || []; var current = { id: 'current', article_count: this.data.incident.article_count, fact_check_count: this.data.incident.factcheck_count, created_at: this.data.generated_at }; var all = [current].concat(snaps); // Group by date var groups = {}; for (var i = 0; i < all.length; i++) { var s = all[i]; var dateKey = this.toDateKey(s.created_at); if (!groups[dateKey]) groups[dateKey] = []; groups[dateKey].push(s); } // Sort each group descending (newest first) for (var dk in groups) { groups[dk].sort(function(a, b) { return new Date(Lagebild.toUTC(b.created_at)) - new Date(Lagebild.toUTC(a.created_at)); }); } this.timelineGroups = groups; var dates = Object.keys(groups).sort(); var strip = document.getElementById('timeline-strip'); var h = ''; for (var j = 0; j < dates.length; j++) { var date = dates[j]; var daySnaps = groups[date]; var latest = daySnaps[0]; var isActive = (j === dates.length - 1); var d = new Date(date + 'T12:00:00Z'); h += ''; } strip.innerHTML = h; // Scroll to active day var active = strip.querySelector('.timeline-day.active'); if (active) { setTimeout(function() { active.scrollIntoView({ behavior: 'smooth', inline: 'center', block: 'nearest' }); }, 150); } // Click handler for day buttons var self = this; strip.addEventListener('click', function(e) { var btn = e.target.closest('.timeline-day'); if (!btn) return; var allDays = strip.querySelectorAll('.timeline-day'); for (var k = 0; k < allDays.length; k++) allDays[k].classList.remove('active'); btn.classList.add('active'); var dateKey = btn.getAttribute('data-date'); var snapId = btn.getAttribute('data-snapshot-id'); self.showTimelineDropdown(dateKey, snapId); if (snapId === 'current') { self.currentView = { summary: self.data.current_lagebild.summary_markdown, sources_json: self.data.current_lagebild.sources_json, updated_at: self.data.current_lagebild.updated_at || self.data.generated_at, articles: self.data.articles, fact_checks: self.data.fact_checks }; self.renderCurrentView(); } else { self.loadSnapshot(parseInt(snapId)); } }); // Click handler for dropdown snapshot items (delegated, set up once) var dropdown = document.getElementById('timeline-dropdown'); dropdown.addEventListener('click', function(e) { var item = e.target.closest('.timeline-snap-item'); if (!item) return; var items = dropdown.querySelectorAll('.timeline-snap-item'); for (var k = 0; k < items.length; k++) items[k].classList.remove('active'); item.classList.add('active'); var snapId = item.getAttribute('data-snapshot-id'); if (snapId === 'current') { self.currentView = { summary: self.data.current_lagebild.summary_markdown, sources_json: self.data.current_lagebild.sources_json, updated_at: self.data.current_lagebild.updated_at || self.data.generated_at, articles: self.data.articles, fact_checks: self.data.fact_checks }; self.renderCurrentView(); } else { self.loadSnapshot(parseInt(snapId)); } }); // Show dropdown for newest day by default var newestDate = dates[dates.length - 1]; if (newestDate && groups[newestDate].length > 1) { this.showTimelineDropdown(newestDate, groups[newestDate][0].id); } }, showTimelineDropdown: function(dateKey, activeSnapId) { var dropdown = document.getElementById('timeline-dropdown'); var snaps = this.timelineGroups[dateKey]; if (!snaps || snaps.length <= 1) { dropdown.classList.remove('open'); dropdown.innerHTML = ''; return; } var d = new Date(dateKey + 'T12:00:00Z'); var dateLabel = d.toLocaleDateString('de-DE', { day: 'numeric', month: 'long', year: 'numeric', timeZone: 'UTC' }); var h = '
' + dateLabel + ' \u2013 ' + snaps.length + ' Updates
'; h += '
'; for (var i = 0; i < snaps.length; i++) { var snap = snaps[i]; var isActive = (String(snap.id) === String(activeSnapId)); h += ''; } h += '
'; dropdown.innerHTML = h; dropdown.classList.add('open'); }, toDateKey: function(iso) { if (!iso) return ''; var d = new Date(this.toUTC(iso)); return d.toLocaleDateString('en-CA', { timeZone: TIMEZONE }); }, /* ===== TAB BADGES ===== */ renderTabBadges: function() { var quellenBadge = document.getElementById('tab-badge-quellen'); var fcBadge = document.getElementById('tab-badge-faktenchecks'); if (quellenBadge) quellenBadge.textContent = this.data.incident.source_count; if (fcBadge) fcBadge.textContent = this.data.incident.factcheck_count; }, /* ===== SNAPSHOT LOADING ===== */ loadSnapshot: async function(id) { if (this.allSnapshots[id]) { this.currentView = this.allSnapshots[id]; this.renderCurrentView(); return; } try { var resp = await fetch('data/snapshot-' + id + '.json'); if (!resp.ok) throw new Error('HTTP ' + resp.status); var sd = await resp.json(); var sj = sd.sources_json; if (typeof sj === 'string') { try { sj = JSON.parse(sj); } catch(e) { sj = []; } } this.currentView = { summary: sd.summary, sources_json: sj || [], updated_at: sd.created_at, articles: this.data.articles, fact_checks: this.data.fact_checks }; this.allSnapshots[id] = this.currentView; this.renderCurrentView(); } catch (e) { console.error('Snapshot Fehler:', e); } }, renderCurrentView: function() { this.renderSummary(); this.renderInlineSources(); this.renderSourcesTab(); this.renderArticlesTab(); this.renderFactChecksTab(); if (document.getElementById('panel-karte').classList.contains('active')) { this.renderMap(); } }, /* ===== TAB: LAGEBILD ===== */ renderSummary: function() { var v = this.currentView; document.getElementById('lagebild-timestamp').textContent = this.fmtDT(v.updated_at); var md = this.fixUmlauts(v.summary || ''); var html = this.mdToHtml(md); // Build source lookup for citation links var srcMap = {}; var sources = v.sources_json || []; for (var i = 0; i < sources.length; i++) { srcMap[sources[i].nr] = sources[i]; } var self = this; html = html.replace(/\[(\d+)\]/g, function(match, nr) { var src = srcMap[nr]; if (src && src.url) { return '[' + nr + ']'; } return '[' + nr + ']'; }); document.getElementById('summary-content').innerHTML = html; }, renderInlineSources: function() { document.getElementById('inline-sources').innerHTML = ''; }, /* ===== TAB: QUELLEN (Tile Grid) ===== */ renderSourcesTab: function() { var articles = this.currentView.articles || []; var container = document.getElementById('sources-grid-container'); if (!container) return; // Aggregate by source var sourceMap = {}; for (var i = 0; i < articles.length; i++) { var a = articles[i]; var name = a.source || 'Unbekannt'; if (!sourceMap[name]) sourceMap[name] = { count: 0, articles: [], languages: {}, domain: null }; sourceMap[name].count++; sourceMap[name].articles.push(a); var lang = (a.language || '').toUpperCase(); if (lang) sourceMap[name].languages[lang] = (sourceMap[name].languages[lang] || 0) + 1; if (!sourceMap[name].domain && a.source_url) sourceMap[name].domain = this.extractDomain(a.source_url); } // Sort by count desc var sources = []; for (var name in sourceMap) { sources.push({ name: name, data: sourceMap[name] }); } sources.sort(function(a, b) { return b.data.count - a.data.count; }); // Language totals var langTotals = {}; for (var i = 0; i < articles.length; i++) { var lang = (articles[i].language || '').toUpperCase(); if (lang) langTotals[lang] = (langTotals[lang] || 0) + 1; } var h = ''; // Header h += '
'; h += '' + articles.length + ' Artikel aus ' + sources.length + ' Quellen'; h += '
'; var langKeys = Object.keys(langTotals).sort(function(a, b) { return langTotals[b] - langTotals[a]; }); for (var i = 0; i < langKeys.length; i++) { h += '' + langKeys[i] + ' ' + langTotals[langKeys[i]] + ''; } h += '
'; // Grid h += '
'; for (var i = 0; i < sources.length; i++) { var s = sources[i]; var langBadge = Object.keys(s.data.languages).join('/'); h += '
'; h += '
'; if (s.data.domain) { h += ''; } h += '' + this.esc(s.name) + ''; h += '
'; h += '
'; h += '' + langBadge + ''; h += '' + s.data.count + ''; h += '
'; h += '
'; } h += '
'; container.innerHTML = h; this._sourceTiles = sources; // Click handler var self = this; document.getElementById('sources-grid').addEventListener('click', function(e) { var tile = e.target.closest('.source-tile'); if (!tile) return; self.toggleSourceDetail(tile); }); }, toggleSourceDetail: function(tile) { var grid = document.getElementById('sources-grid'); var idx = parseInt(tile.getAttribute('data-source-index')); var existingPanel = grid.querySelector('.source-detail-panel'); var wasActive = tile.classList.contains('active'); // Remove active from all tiles var allTiles = grid.querySelectorAll('.source-tile'); for (var k = 0; k < allTiles.length; k++) allTiles[k].classList.remove('active'); // Remove existing panel if (existingPanel) existingPanel.remove(); // If same tile was active, just close if (wasActive) return; tile.classList.add('active'); // Find last tile in same visual row (by offsetTop) var clickedTop = tile.offsetTop; var lastInRow = tile; for (var k = 0; k < allTiles.length; k++) { if (allTiles[k].offsetTop === clickedTop) { lastInRow = allTiles[k]; } } // Build detail panel var src = this._sourceTiles[idx]; var arts = src.data.articles.slice().sort(function(a, b) { var da = new Date(Lagebild.toUTC(a.published_at || a.collected_at || '')); var db = new Date(Lagebild.toUTC(b.published_at || b.collected_at || '')); return db - da; }); var h = '
'; h += '
'; h += ''; if (src.data.domain) { h += ' '; } h += this.esc(src.name) + ''; h += '' + src.data.count + ' Artikel'; h += ''; h += '
'; h += '
'; for (var j = 0; j < arts.length; j++) { var a = arts[j]; var dt = a.published_at || a.collected_at || ''; var dObj = dt ? new Date(this.toUTC(dt)) : null; var hl = this.fixUmlauts(a.headline_de || a.headline || ''); h += '
'; if (a.source_url) { h += '' + this.esc(hl) + ' ' + this.icons.externalLink + ''; } else { h += '' + this.esc(hl) + ''; } if (dObj && !isNaN(dObj.getTime())) { h += ''; } h += '
'; } h += '
'; // Insert after last tile in row lastInRow.insertAdjacentHTML('afterend', h); // Close button handler var panel = grid.querySelector('.source-detail-panel'); var self = this; panel.querySelector('.source-detail-close').addEventListener('click', function(e) { e.stopPropagation(); panel.remove(); tile.classList.remove('active'); }); // Scroll into view setTimeout(function() { panel.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 50); }, renderArticlesTab: function() {}, /* ===== TAB: KARTE (Pulse Markers) ===== */ renderMap: function() { if (this.map) { this.map.remove(); this.map = null; } this.map = L.map('map-container').setView([33.0, 48.0], 5); // Light map tiles (CartoDB Voyager) L.tileLayer('https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png', { attribution: '© OpenStreetMap © CARTO', maxZoom: 19, subdomains: 'abcd' }).addTo(this.map); function pulseIcon(color) { return L.divIcon({ className: '', html: '
' + '
' + '
' + '
' + '
', iconSize: [20, 20], iconAnchor: [10, 10], popupAnchor: [0, -12] }); } // Kategorie-Farben var categoryColors = { target: '#ef4444', retaliation: '#f59e0b', response: '#f59e0b', actor: '#3b82f6', mentioned: '#7b7b7b' }; var categoryLabels = { target: 'Angegriffene Ziele', retaliation: 'Vergeltung / Eskalation', response: 'Reaktion / Gegenmassnahmen', actor: 'Strategische Akteure', mentioned: 'Erwaehnt' }; // Locations aus API-Daten laden var locs = (this.data && this.data.locations) ? this.data.locations : []; if (locs.length === 0) { var emptyDiv = document.createElement('div'); emptyDiv.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:1000;background:#151D2E;padding:20px 30px;border-radius:8px;border:1px solid #1E2D45;color:#8896AB;text-align:center;'; emptyDiv.innerHTML = 'Keine Standortdaten verfuegbar'; document.getElementById('map-container').appendChild(emptyDiv); } var usedCategories = {}; for (var i = 0; i < locs.length; i++) { var l = locs[i]; if (!l.lat || !l.lon) continue; var cat = l.category || 'mentioned'; var color = categoryColors[cat] || '#7b7b7b'; usedCategories[cat] = true; var popupText = '' + (l.name || '') + ''; if (l.country_code) popupText += ' (' + l.country_code + ')'; popupText += '
' + (l.article_count || 0) + ' Artikel'; L.marker([l.lat, l.lon], { icon: pulseIcon(color) }) .addTo(this.map) .bindPopup(popupText); } // Dark legend (dynamisch) var legend = L.control({ position: 'bottomright' }); legend.onAdd = function() { var div = L.DomUtil.create('div', 'map-legend'); 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
'; ['target', 'response', 'retaliation', 'actor', 'mentioned'].forEach(function(cat) { if (usedCategories[cat]) { html += ' ' + categoryLabels[cat] + '
'; } }); div.innerHTML = html; return div; }; legend.addTo(this.map); // Dark popup styling if (!document.getElementById('leaflet-dark-style')) { var style = document.createElement('style'); style.id = 'leaflet-dark-style'; style.textContent = '.lagebild-page .leaflet-popup-content-wrapper{background:#151D2E;color:#E8ECF4;border:1px solid #1E2D45;border-radius:4px;box-shadow:0 4px 16px rgba(0,0,0,0.4);}.lagebild-page .leaflet-popup-tip{background:#151D2E;}'; document.head.appendChild(style); } setTimeout(function() { if (Lagebild.map) Lagebild.map.invalidateSize(); }, 300); }, /* ===== TAB: FAKTENCHECKS ===== */ /* ===== Factcheck Icons (from real Monitor) ===== */ fcIcons: { confirmed: '✓', unconfirmed: '?', contradicted: '✗', developing: '↻', established: '✓', disputed: '⚠', 'false': '✗', unverified: '?' }, fcLabels: { confirmed: 'Bestätigt', unconfirmed: 'Unbestätigt', contradicted: 'Widerlegt', developing: 'Unklar', established: 'Gesichert', disputed: 'Umstritten', 'false': 'Falsch', unverified: 'Nicht verifiziert' }, renderFactChecksTab: function() { var checks = this.currentView.fact_checks || []; if (!checks.length) { document.getElementById('factchecks-content').innerHTML = '

Keine Faktenchecks verfügbar.

'; return; } // Count stats var stats = { confirmed: 0, unconfirmed: 0, contradicted: 0, developing: 0, established: 0, disputed: 0 }; for (var k = 0; k < checks.length; k++) { var st = checks[k].status || 'developing'; if (stats[st] !== undefined) stats[st]++; } var confirmedTotal = stats.confirmed + stats.established; var openTotal = stats.unconfirmed + stats.developing; var contradictedTotal = stats.contradicted + stats.disputed; // Stat cards (clickable filters) var h = '
'; h += ''; h += ''; h += ''; if (contradictedTotal > 0) h += ''; h += '
'; // Sort: status_history first, then by sources_count checks = checks.slice().sort(function(a, b) { var aH = (a.status_history || []).length; var bH = (b.status_history || []).length; if (bH !== aH) return bH - aH; return (b.sources_count || 0) - (a.sources_count || 0); }); // Compact accordion list h += '
'; for (var i = 0; i < checks.length; i++) { var fc = checks[i]; var status = fc.status || 'developing'; var filterGroup = 'all'; if (status === 'confirmed' || status === 'established') filterGroup = 'confirmed'; else if (status === 'unconfirmed' || status === 'developing') filterGroup = 'unconfirmed'; else if (status === 'contradicted' || status === 'disputed') filterGroup = 'contradicted'; var hasProg = fc.status_history && fc.status_history.length > 1; var icon = this.fcIcons[status] || '?'; var label = this.fcLabels[status] || status; h += '
'; h += '
'; h += '' + icon + ''; h += '' + this.esc(this.fixUmlauts(fc.claim || '')) + ''; h += '' + (fc.sources_count || 0) + ''; if (hasProg) h += ''; h += ''; h += '
'; // Expandable detail h += '
'; h += '
'; h += '
' + icon + ' ' + label + ' – ' + (fc.sources_count || 0) + ' unabhängige Quellen
'; if (fc.evidence) { var ev = this.fixUmlauts(fc.evidence); ev = this.esc(ev).replace(/(https?:\/\/[^\s,)]+)/g, '$1'); h += '
Evidenz: ' + ev + '
'; } if (hasProg) { h += '
'; h += 'Verlauf:'; for (var j = 0; j < fc.status_history.length; j++) { var step = fc.status_history[j]; if (j > 0) h += ''; h += ''; h += '' + (this.fcIcons[step.status] || '?') + ''; if (step.at) h += '' + this.fmtShort(step.at) + ''; h += ''; } h += '
'; } h += '
'; h += '
'; } h += '
'; document.getElementById('factchecks-content').innerHTML = h; // Filter click handler var statBtns = document.querySelectorAll('.fc-stat'); statBtns.forEach(function(btn) { btn.addEventListener('click', function() { for (var k = 0; k < statBtns.length; k++) statBtns[k].classList.remove('active'); btn.classList.add('active'); var filter = btn.getAttribute('data-filter'); var rows = document.querySelectorAll('.fc-row'); for (var k = 0; k < rows.length; k++) { if (filter === 'all' || rows[k].getAttribute('data-status-group') === filter) { rows[k].style.display = ''; } else { rows[k].style.display = 'none'; } } }); }); // Accordion click handler document.getElementById('fc-list').addEventListener('click', function(e) { var header = e.target.closest('.fc-row-header'); if (!header) return; var row = header.closest('.fc-row'); var wasOpen = row.classList.contains('open'); // Close all open rows var allRows = document.querySelectorAll('.fc-row.open'); for (var k = 0; k < allRows.length; k++) allRows[k].classList.remove('open'); // Toggle clicked row if (!wasOpen) { row.classList.add('open'); var detail = row.querySelector('.fc-row-detail'); if (detail) { setTimeout(function() { detail.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); }, 50); } } }); }, /* ===== TABS ===== */ initTabs: function() { var btns = document.querySelectorAll('.tab-btn'); var self = this; for (var i = 0; i < btns.length; i++) { btns[i].addEventListener('click', function() { var tab = this.getAttribute('data-tab'); for (var j = 0; j < btns.length; j++) btns[j].classList.remove('active'); this.classList.add('active'); var panels = document.querySelectorAll('.tab-panel'); for (var j = 0; j < panels.length; j++) panels[j].classList.remove('active'); var activePanel = document.getElementById('panel-' + tab); activePanel.classList.add('active'); // Trigger reveal for cards in newly active panel var revealCards = activePanel.querySelectorAll('.reveal:not(.revealed)'); for (var k = 0; k < revealCards.length; k++) { revealCards[k].classList.add('revealed'); } if (tab === 'karte') self.renderMap(); }); } }, initLangToggle: function() { var btn = document.querySelector('.lang-toggle'); if (!btn) return; btn.addEventListener('click', function(e) { e.preventDefault(); if (typeof switchLanguage === 'function') { var cur = (typeof getCurrentLanguage === 'function') ? getCurrentLanguage() : 'de'; switchLanguage(cur === 'de' ? 'en' : 'de'); } }); }, /* ===== FLOATING CTA ===== */ initFloatingCta: function() { var cta = document.createElement('div'); cta.className = 'floating-cta'; cta.innerHTML = 'AegisSight Monitor f\u00fcr Ihre Organisation' + 'Kontakt aufnehmen \u2192' + ''; document.body.appendChild(cta); // Show after scrolling past hero var shown = false; window.addEventListener('scroll', function() { if (shown) return; if (window.scrollY > 400) { cta.classList.add('visible'); shown = true; } }); // Close button cta.querySelector('.floating-cta-close').addEventListener('click', function(e) { e.preventDefault(); cta.classList.add('dismissed'); setTimeout(function() { cta.classList.remove('dismissed'); }, 60000); }); }, /* ===== SCROLL REVEAL ===== */ initScrollReveal: function() { var cards = document.querySelectorAll('.content-card, .lagebild-cta'); if (!('IntersectionObserver' in window)) { for (var i = 0; i < cards.length; i++) cards[i].classList.add('revealed'); return; } var observer = new IntersectionObserver(function(entries) { entries.forEach(function(entry) { if (entry.isIntersecting) { entry.target.classList.add('revealed'); observer.unobserve(entry.target); } }); }, { threshold: 0.1 }); for (var i = 0; i < cards.length; i++) { cards[i].classList.add('reveal'); // Immediately reveal cards in the active (visible) tab panel var panel = cards[i].closest('.tab-panel'); if (!panel || panel.classList.contains('active')) { cards[i].classList.add('revealed'); } else { observer.observe(cards[i]); } } }, /* ===== HILFSFUNKTIONEN ===== */ extractDomain: function(url) { if (!url) return null; try { return new URL(url).hostname; } catch(e) { return null; } }, fixUmlauts: function(text) { if (!text) return text; var skip = ['Israel','Israelis','Jazeera','Euronews','Reuters','Februar', 'Juffair','abgefeuert','Feindseligkeiten','Gegenschlag','neuesten', 'auszuweiten','befeuert','feuerte','Feuer','feuer','neue','neuen', 'neuer','neues','Neue','Aero','aero','Manoeuvre','Dauerfeuer']; var ph = []; var c = 0; for (var i = 0; i < skip.length; i++) { var re = new RegExp('\\b' + skip[i] + '\\b', 'g'); text = text.replace(re, function(m) { ph.push(m); return '##S' + (c++) + '##'; }); } text = text.replace(/ae/g, '\u00e4').replace(/Ae/g, '\u00c4'); text = text.replace(/oe/g, '\u00f6').replace(/Oe/g, '\u00d6'); text = text.replace(/ue/g, '\u00fc').replace(/Ue/g, '\u00dc'); text = text.replace(/##S(\d+)##/g, function(m, idx) { return ph[parseInt(idx)]; }); return text; }, stLabel: function(s) { return { confirmed: 'Best\u00e4tigt', unconfirmed: 'Unbest\u00e4tigt', established: 'Gesichert', unverified: 'Nicht verifiziert', contradicted: 'Widerlegt', disputed: 'Umstritten', developing: 'In Entwicklung', 'false': 'Falsch' }[s] || s; }, mdToHtml: function(md) { if (!md) return ''; var lines = md.split('\n'), html = '', inList = false; for (var i = 0; i < lines.length; i++) { var l = lines[i]; if (/^### (.+)$/.test(l)) { if (inList) { html += ''; inList = false; } html += '

' + l.replace(/^### /, '') + '

'; continue; } if (/^## (.+)$/.test(l)) { if (inList) { html += ''; inList = false; } html += '

' + l.replace(/^## /, '') + '

'; continue; } if (/^[-*] (.+)$/.test(l)) { if (!inList) { html += ''; inList = false; } if (l.trim() === '') continue; html += '

' + l + '

'; } if (inList) html += ''; html = html.replace(/\*\*(.+?)\*\*/g, '$1'); html = html.replace(/\*(.+?)\*/g, '$1'); return html; }, esc: function(s) { if (!s) return ''; var d = document.createElement('div'); d.textContent = s; return d.innerHTML; }, toUTC: function(s) { if (!s) return s; s = String(s).trim(); if (/[Zz]$/.test(s) || /[+-]\d{2}:?\d{2}$/.test(s)) return s; return s.replace(' ', 'T') + 'Z'; }, fmtDT: function(iso) { if (!iso) return ''; try { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; var opts = { timeZone: TIMEZONE, weekday: 'long', day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false }; var parts = new Intl.DateTimeFormat('de-DE', opts).formatToParts(d); var p = {}; parts.forEach(function(x) { p[x.type] = x.value; }); return p.weekday + ', ' + p.day + '. ' + p.month + ' ' + p.year + ' um ' + p.hour + ':' + p.minute + ' Uhr'; } catch(e) { return iso; } }, fmtDateOnly: function(iso) { if (!iso) return ''; try { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; return d.toLocaleDateString('de-DE', { day: 'numeric', month: 'short', year: 'numeric', timeZone: TIMEZONE }); } catch(e) { return iso; } }, fmtTimeOnly: function(iso) { if (!iso) return ''; try { var d = new Date(this.toUTC(iso)); if (isNaN(d.getTime())) return iso; return d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); } catch(e) { return iso; } }, fmtShort: function(iso) { if (!iso) return ''; try { return new Date(this.toUTC(iso)).toLocaleDateString('de-DE', { day: 'numeric', month: 'short', hour: '2-digit', minute: '2-digit', timeZone: TIMEZONE }); } catch(e) { return iso; } }, showError: function() { document.getElementById('summary-content').innerHTML = '

Das Lagebild konnte nicht geladen werden. Bitte versuchen Sie es sp\u00e4ter erneut.

'; } }; document.addEventListener('DOMContentLoaded', function() { Lagebild.init(); });