From 584183951f7fc8bedef4e6b9fcb4ff43f72a3f6d Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Mon, 23 Mar 2026 21:23:07 +0100 Subject: [PATCH] Tutorial: Umfassende Verbesserungen an Schritten 5,8+,11,20-26,28,31,32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Schritt 5: Simuliertes Dropdown statt einfachem Wechsel - Ab Schritt 8: Verbessertes Modal-Scrolling fuer alle Formularfelder - Schritt 11: Cursor-Demo zwischen Alle/Eigene Filtern - Schritt 20: Status-Durchlauf statt Scroll-Sprung - Schritt 21: Quellenübersicht nach Timeline/Karte verschoben - Schritt 22: Timeline mit Cursor-Navigation durch Zeitpunkte - Schritt 23: Cursor zeigt Orte einlesen + Vollbild Buttons - Schritt 24: Z-Index Fix fuer Bubble ueber Vollbild-Karte - Schritt 25/26: Kombinierte Drag+Resize Demo - Schritt 28/31: Position hoeher (position:top statt right/left) - Schritt 32: Bubble tiefer zentriert (55% statt 50%) - 6 neue Simulationsfunktionen hinzugefuegt --- src/static/js/tutorial.js | 599 +++++++++++++++++++++++++++++--------- 1 file changed, 464 insertions(+), 135 deletions(-) diff --git a/src/static/js/tutorial.js b/src/static/js/tutorial.js index 6db3483..8dfc032 100644 --- a/src/static/js/tutorial.js +++ b/src/static/js/tutorial.js @@ -731,16 +731,24 @@ const Tutorial = { Tutorial._clearSubHighlights(); }, }, - // 5 - Sidebar Filter + // Sidebar Filter { id: 'sidebar-filters', target: '.sidebar-filter', title: 'Lagen filtern', text: 'Mit diesen Filtern steuern Sie, welche Lagen angezeigt werden:

' - + 'Alle - Zeigt sämtliche Lagen Ihrer Organisation
' + + 'Alle - Zeigt s\u00e4mtliche Lagen Ihrer Organisation
' + 'Eigene - Nur Lagen, die Sie selbst erstellt haben

' - + 'Bei vielen Lagen hilft dies, den Überblick zu behalten.', + + 'Bei vielen Lagen hilft dies, den \u00dcberblick zu behalten.', position: 'right', + disableNav: true, + onEnter: function() { + Tutorial._runDemo(Tutorial._simulateFilterSwitch); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + Tutorial._hideCursor(); + }, }, // 6 - Demo-Lage einführen { @@ -872,33 +880,97 @@ const Tutorial = { Tutorial._clearSubHighlights(); }, }, - // 14 - Faktencheck: Einzelner Eintrag + // Faktencheck: Status-Demo { id: 'faktencheck-detail', target: '.factcheck-item[data-fc-status="confirmed"]', title: 'Faktencheck-Eintrag', text: 'Jeder Faktencheck-Eintrag besteht aus:

' - + 'Status-Symbol - Farbcodiert für schnelle Einordnung (links)
' - + 'Behauptung - Die geprüfte Aussage
' - + 'Quellenanzahl - Wie viele Quellen diese Behauptung stützen

' - + 'Die Filterfunktion oben rechts ermöglicht es, nach Status zu filtern, ' - + 'z.B. nur unbestätigte Meldungen anzeigen.', + + 'Status-Symbol - Farbcodiert f\u00fcr schnelle Einordnung
' + + 'Behauptung - Die gepr\u00fcfte Aussage
' + + 'Quellenanzahl - Wie viele Quellen diese Behauptung st\u00fctzen

' + + 'Beobachten Sie, wie der Cursor die verschiedenen Status durchgeht:', position: 'left', + disableNav: true, onEnter: function() { - // Zum nicht-verifizierten Eintrag scrollen - var fcList = document.getElementById('factcheck-list'); - if (fcList) fcList.scrollTo({ top: fcList.scrollHeight, behavior: 'smooth' }); - Tutorial._stepTimeout(function() { - if (fcList) fcList.scrollTo({ top: 0, behavior: 'smooth' }); - }, 2000); - var item = document.querySelector('.factcheck-item[data-fc-status="confirmed"]'); - if (item) item.classList.add('tutorial-sub-highlight'); - Tutorial._cleanupFns.push(function() { - if (item) item.classList.remove('tutorial-sub-highlight'); - }); + Tutorial._runDemo(Tutorial._simulateFactcheckStati); }, onExit: function() { Tutorial._clearSubHighlights(); + Tutorial._hideCursor(); + }, + }, + // Ereignis-Timeline + { + id: 'timeline', + target: '[gs-id="timeline"]', + title: 'Ereignis-Timeline', + text: 'Die Timeline zeigt den chronologischen Verlauf aller Ereignisse. ' + + 'Klicken Sie auf einen Zeitpunkt, um die zugeh\u00f6rige Meldung anzuzeigen.

' + + 'Punkte - Meldungen und Lageberichte auf der Zeitachse
' + + 'Detail-Panel - Zeigt Quelle, Titel und Zeitstempel

' + + 'So k\u00f6nnen Sie auch \u00e4ltere Lagebilder abrufen und den Verlauf nachvollziehen.', + position: 'top', + disableNav: true, + onEnter: function() { + Tutorial._runDemo(Tutorial._simulateTimeline); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + Tutorial._hideCursor(); + }, + }, + // Karte: Kachel-Ansicht + { + id: 'karte', + target: '[gs-id="karte"]', + title: 'Geografische Verteilung', + text: 'Die Karte zeigt per Geoparsing automatisch erkannte Orte aus den Quellen.

' + + 'Orte einlesen - Startet das Geoparsing manuell neu
' + + 'Vollbild - Vergr\u00f6\u00dfert die Karte auf den gesamten Bildschirm

' + + 'Im n\u00e4chsten Schritt \u00f6ffnen wir die Vollbildansicht.', + position: 'top', + disableNav: true, + onEnter: function() { + if (Tutorial._demoMapTileMap) { + Tutorial._demoMapTileMap.invalidateSize(); + setTimeout(function() { + if (Tutorial._demoMapTileMap) Tutorial._demoMapTileMap.setView([53.545, 9.98], 12); + }, 200); + } + Tutorial._runDemo(Tutorial._simulateKarteButtons); + }, + onExit: function() { + Tutorial._clearSubHighlights(); + Tutorial._hideCursor(); + }, + }, + // Karte: Vollbild + { + id: 'karte-fullscreen', + target: '.map-fullscreen-header', + title: 'Karte im Vollbild', + text: 'Die Orte werden automatisch aus den Quellenartikeln erkannt und auf der Karte dargestellt:

' + + '● Hauptereignisort - Wo das Ereignis stattfindet
' + + '● Erw\u00e4hnt - In Meldungen genannte Orte
' + + '● Kontext - Orte im weiteren Zusammenhang

' + + 'Klicken Sie auf einen Marker f\u00fcr Details und verkn\u00fcpfte Artikel.', + position: 'left', + disableNav: true, + onEnter: function() { + var chatBtn = document.getElementById('chat-toggle-btn'); + if (chatBtn) chatBtn.style.display = 'none'; + var bubble = document.getElementById('tutorial-bubble'); + if (bubble) bubble.style.zIndex = '99999'; + Tutorial._openDemoMapFullscreen(); + }, + onExit: function() { + Tutorial._closeDemoMapFullscreen(); + var chatBtn = document.getElementById('chat-toggle-btn'); + if (chatBtn) chatBtn.style.display = ''; + var bubble = document.getElementById('tutorial-bubble'); + if (bubble) bubble.style.zIndex = ''; + Tutorial._clearSubHighlights(); }, }, // 15 - Quellen @@ -921,105 +993,24 @@ const Tutorial = { Tutorial._clearSubHighlights(); }, }, - // 16 - Timeline + // Dashboard anpassen (Drag + Resize) { - id: 'timeline', - target: '[gs-id="timeline"]', - title: 'Ereignis-Timeline', - text: 'Die Timeline zeigt den chronologischen Verlauf aller Ereignisse:

' - + 'Meldungen - Einzelne Artikel und Nachrichten aus den Quellen
' - + 'Lageberichte - Automatisch erstellte Zusammenfassungen (hervorgehoben)

' - + 'Nutzen Sie die Filter oben:
' - + 'Alle/Meldungen/Lageberichte - Typ-Filter
' - + '24h/7T/Alles - Zeitraum eingrenzen
' - + 'Suchfeld - Freitextsuche in allen Einträgen', - position: 'top', - onEnter: function() { - Tutorial._highlightSub('.ht-controls'); - }, - onExit: function() { - Tutorial._clearSubHighlights(); - }, - }, - // 17 - Karte: Kachel-Ansicht - { - id: 'karte', - target: '[gs-id="karte"]', - title: 'Geografische Verteilung', - text: 'Die Karte zeigt per Geoparsing automatisch erkannte Orte aus den Quellen.

' - + 'Orte einlesen - Startet das Geoparsing manuell neu
' - + 'Vollbild - Vergr\u00f6\u00dfert die Karte auf den gesamten Bildschirm

' - + 'Im n\u00e4chsten Schritt \u00f6ffnen wir die Vollbildansicht und schauen uns die Marker im Detail an.', - position: 'top', - onEnter: function() { - // Tile-Map Resize triggern - if (Tutorial._demoMapTileMap) { - Tutorial._demoMapTileMap.invalidateSize(); - setTimeout(function() { - if (Tutorial._demoMapTileMap) Tutorial._demoMapTileMap.setView([53.545, 9.98], 12); - }, 200); - } - Tutorial._highlightSub('#geoparse-btn'); - Tutorial._stepTimeout(function() { - Tutorial._clearSubHighlights(); - Tutorial._highlightSub('#map-expand-btn'); - }, 2500); - }, - onExit: function() { - Tutorial._clearSubHighlights(); - }, - }, - // 18 - Karte: Vollbild + Zoom + Marker-Demo - { - id: 'karte-fullscreen', - target: '.map-fullscreen-header', - title: 'Karte im Vollbild', - text: 'Die Karte zoomt jetzt auf den Ereignisort. Die Marker zeigen:

' - + '● Hauptereignisort - Burchardkai Terminal (6 Artikel)
' - + '● Erw\u00e4hnt - Hamburg Innenstadt (2 Artikel)
' - + '● Kontext - Elbe / Hafengebiet (1 Artikel)

' - + 'Die Legende unten rechts erkl\u00e4rt die Farbkategorien. ' - + 'Klicken Sie auf einen Marker f\u00fcr Details und verkn\u00fcpfte Artikel.', - position: 'left', - disableNav: true, - onEnter: function() { - var chatBtn = document.getElementById('chat-toggle-btn'); - if (chatBtn) chatBtn.style.display = 'none'; - Tutorial._openDemoMapFullscreen(); - }, - onExit: function() { - Tutorial._closeDemoMapFullscreen(); - var chatBtn = document.getElementById('chat-toggle-btn'); - if (chatBtn) chatBtn.style.display = ''; - Tutorial._clearSubHighlights(); - }, - }, - // 18 - Drag Demo - { - id: 'drag-demo', + id: 'layout-demo', target: '[gs-id="lagebild"] .card-header', - title: 'Kacheln verschieben', - text: 'Alle Kacheln im Dashboard lassen sich frei per Drag-and-Drop verschieben. ' - + 'Greifen Sie dazu die Kopfzeile einer Kachel und ziehen Sie sie an die gewünschte Position.

' - + 'Beobachten Sie die virtuelle Maus-Demo:', + title: 'Dashboard anpassen', + text: 'Alle Kacheln lassen sich frei verschieben und in der Gr\u00f6\u00dfe anpassen. ' + + 'Greifen Sie die Kopfzeile zum Verschieben oder den unteren Rand zum Vergr\u00f6\u00dfern.

' + + 'Beobachten Sie die Demo: Erst wird das Lagebild verschoben, dann vergr\u00f6\u00dfert.', position: 'right', disableNav: true, onEnter: function() { - Tutorial._runDemo(Tutorial._simulateDrag); + Tutorial._runDemo(Tutorial._simulateLayoutDemo); }, - }, - // 19 - Resize Demo - { - id: 'resize-demo', - target: '[gs-id="faktencheck"]', - title: 'Kacheln in der Größe anpassen', - text: 'Ziehen Sie am rechten unteren Rand einer Kachel, um ihre Größe zu verändern. ' - + 'So können Sie wichtigen Inhalten wie dem Faktencheck mehr Platz einräumen.

' - + 'Beobachten Sie die virtuelle Maus-Demo:', - position: 'left', - disableNav: true, - onEnter: function() { - Tutorial._runDemo(Tutorial._simulateResize); + onExit: function() { + if (typeof LayoutManager !== 'undefined' && LayoutManager._grid) { + LayoutManager._applyLayout(LayoutManager.DEFAULT_LAYOUT); + } + Tutorial._hideCursor(); }, }, // 20 - Theme @@ -1031,17 +1022,17 @@ const Tutorial = { + 'Ihre Auswahl wird automatisch gespeichert und beim nächsten Besuch beibehalten.', position: 'bottom', }, - // 21 - Quellen verwalten + // Quellen verwalten { id: 'sources-btn', target: '.sidebar-sources-link button:first-child', - title: 'Quellenverwaltung öffnen', + title: 'Quellenverwaltung \u00f6ffnen', text: 'In der Seitenleiste ganz unten finden Sie den Zugang zur Quellenverwaltung. ' - + 'Hier können Sie:

' - + 'Neue Quellen hinzufügen - URL eingeben oder automatisch erkennen lassen
' + + 'Hier k\u00f6nnen Sie:

' + + 'Neue Quellen hinzuf\u00fcgen - URL eingeben oder automatisch erkennen lassen
' + 'Bestehende Quellen bearbeiten - Kategorie, Sprache, Notizen anpassen
' - + 'Quellen deaktivieren - Temporär oder dauerhaft ausschließen', - position: 'right', + + 'Quellen deaktivieren - Tempor\u00e4r oder dauerhaft ausschlie\u00dfen', + position: 'top', onEnter: function() { Tutorial._stepTimeout(function() { var overlay = document.getElementById('modal-sources'); @@ -1122,30 +1113,36 @@ const Tutorial = { Tutorial._hideCursor(); }, }, - // 23 - Chat + // Chat-Assistent { id: 'chat', target: '#chat-toggle-btn', title: 'Chat-Assistent', - text: 'Der Chat-Assistent steht Ihnen jederzeit zur Verfügung. ' + text: 'Der Chat-Assistent steht Ihnen jederzeit zur Verf\u00fcgung. ' + 'Stellen Sie Fragen zur Bedienung des Monitors und erhalten Sie sofort eine Antwort.

' + 'Beispiele:
' + '"Wie erstelle ich eine neue Lage?"
' + '"Was bedeuten die Faktencheck-Status?"
' + '"Wie exportiere ich einen Lagebericht?"', - position: 'left', + position: 'top', }, - // 24 - Ende + // Ende { id: 'end', target: null, title: 'Rundgang abgeschlossen', text: 'Sie kennen jetzt alle wichtigen Funktionen des AegisSight Monitors.

' - + 'Die Demo-Daten werden nach dem Schließen entfernt. ' - + 'Erstellen Sie Ihre erste eigene Lage über den Button "+ Neue Lage" in der Seitenleiste.

' + + 'Die Demo-Daten werden nach dem Schlie\u00dfen entfernt. ' + + 'Erstellen Sie Ihre erste eigene Lage \u00fcber den Button "+ Neue Lage" in der Seitenleiste.

' + 'Bei weiteren Fragen steht Ihnen der Chat-Assistent ' - + 'oder unser Support unter support@aegis-sight.de zur Verfügung.', + + 'oder unser Support unter support@aegis-sight.de zur Verf\u00fcgung.', position: 'center', + onEnter: function() { + Tutorial._stepTimeout(function() { + var bubble = document.getElementById('tutorial-bubble'); + if (bubble) bubble.style.top = '55%'; + }, 50); + }, }, ]; }, @@ -1746,8 +1743,11 @@ const Tutorial = { var el = document.querySelector(selector); var modalBody = document.querySelector('#modal-new .modal-body'); if (!el || !modalBody) return; - var elTop = el.offsetTop - modalBody.offsetTop; - modalBody.scrollTo({ top: Math.max(0, elTop - 20), behavior: 'smooth' }); + var elRect = el.getBoundingClientRect(); + var bodyRect = modalBody.getBoundingClientRect(); + var offset = elRect.top - bodyRect.top + modalBody.scrollTop; + var targetScroll = offset - Math.min(40, bodyRect.height * 0.2); + modalBody.scrollTo({ top: Math.max(0, targetScroll), behavior: 'smooth' }); }, // ----------------------------------------------------------------------- @@ -1787,6 +1787,8 @@ const Tutorial = { // ----------------------------------------------------------------------- // Step 4: Art der Lage (Typ-Wechsel) // ----------------------------------------------------------------------- + // Step 4: Art der Lage (simuliertes Dropdown) + // ----------------------------------------------------------------------- async _simulateTypeSwitch() { this._demoRunning = true; var sel = document.getElementById('inc-type'); @@ -1795,15 +1797,67 @@ const Tutorial = { var pos = await this._cursorToElement('#inc-type'); await this._wait(300); - // Wechsel zu Recherche - sel.value = 'research'; - sel.dispatchEvent(new Event('change')); - await this._wait(2000); + // Simuliertes Dropdown + var rect = sel.getBoundingClientRect(); + var dropdown = document.createElement('div'); + dropdown.id = 'tutorial-fake-dropdown'; + dropdown.style.cssText = 'position:fixed;z-index:99999;background:var(--bg-card,#1a1f2e);' + + 'border:1px solid var(--border,#2a3040);border-radius:8px;box-shadow:0 8px 32px rgba(0,0,0,0.4);' + + 'overflow:hidden;left:' + rect.left + 'px;top:' + (rect.bottom + 4) + 'px;width:' + rect.width + 'px;' + + 'opacity:0;transform:translateY(-4px);transition:opacity 0.2s,transform 0.2s;'; - // Zur\u00fcck zu Live-Monitoring + var opt1 = document.createElement('div'); + opt1.textContent = 'Live-Monitoring'; + opt1.style.cssText = 'padding:10px 14px;font-size:14px;color:var(--text-primary,#e0e0e0);' + + 'background:var(--accent-alpha,rgba(212,175,55,0.15));transition:background 0.15s;'; + + var opt2 = document.createElement('div'); + opt2.textContent = 'Analyse / Recherche'; + opt2.style.cssText = 'padding:10px 14px;font-size:14px;color:var(--text-primary,#e0e0e0);' + + 'transition:background 0.15s;'; + + dropdown.appendChild(opt1); + dropdown.appendChild(opt2); + document.body.appendChild(dropdown); + + await this._wait(50); + dropdown.style.opacity = '1'; + dropdown.style.transform = 'translateY(0)'; + await this._wait(400); + + // Cursor zu Live-Monitoring + var opt1Rect = opt1.getBoundingClientRect(); + await this._animateCursor(pos.x, pos.y, + opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2, 400); + await this._wait(1200); + + // Cursor zu Analyse/Recherche + var opt2Rect = opt2.getBoundingClientRect(); + await this._animateCursor( + opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2, + opt2Rect.left + opt2Rect.width / 2, opt2Rect.top + opt2Rect.height / 2, 400); + opt1.style.background = ''; + opt2.style.background = 'var(--accent-alpha,rgba(212,175,55,0.15))'; + sel.value = 'research'; + try { sel.dispatchEvent(new Event('change')); } catch(e) {} + await this._wait(1500); + + // Zurueck zu Live-Monitoring + opt1Rect = opt1.getBoundingClientRect(); + opt2Rect = opt2.getBoundingClientRect(); + await this._animateCursor( + opt2Rect.left + opt2Rect.width / 2, opt2Rect.top + opt2Rect.height / 2, + opt1Rect.left + opt1Rect.width / 2, opt1Rect.top + opt1Rect.height / 2, 400); + opt2.style.background = ''; + opt1.style.background = 'var(--accent-alpha,rgba(212,175,55,0.15))'; sel.value = 'adhoc'; - sel.dispatchEvent(new Event('change')); - await this._wait(800); + try { sel.dispatchEvent(new Event('change')); } catch(e) {} + await this._wait(1000); + + dropdown.style.opacity = '0'; + dropdown.style.transform = 'translateY(-4px)'; + await this._wait(250); + if (dropdown.parentNode) dropdown.remove(); this._hideCursor(); this._demoRunning = false; @@ -2209,6 +2263,281 @@ const Tutorial = { this._enableNavAfterDemo(); }, + + // ----------------------------------------------------------------------- + // Sidebar-Filter Demo + // ----------------------------------------------------------------------- + async _simulateFilterSwitch() { + this._demoRunning = true; + await this._wait(400); + var btns = document.querySelectorAll('.sidebar-filter button, .sidebar-filter .filter-btn'); + var allBtn = null, ownBtn = null; + btns.forEach(function(b) { + var t = b.textContent.trim().toLowerCase(); + if (t === 'alle') allBtn = b; + if (t === 'eigene') ownBtn = b; + }); + if (!allBtn) allBtn = document.querySelector('.sidebar-filter [data-filter="all"]'); + if (!ownBtn) ownBtn = document.querySelector('.sidebar-filter [data-filter="own"]'); + if (!allBtn || !ownBtn) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + + var ar = allBtn.getBoundingClientRect(); + this._showCursor(ar.left - 30, ar.top - 20, 'default'); + await this._wait(300); + await this._animateCursor(ar.left - 30, ar.top - 20, ar.left + ar.width/2, ar.top + ar.height/2, 400); + allBtn.classList.add('tutorial-sub-highlight'); + await this._wait(1500); + allBtn.classList.remove('tutorial-sub-highlight'); + + var or2 = ownBtn.getBoundingClientRect(); + await this._animateCursor(ar.left+ar.width/2, ar.top+ar.height/2, or2.left+or2.width/2, or2.top+or2.height/2, 500); + ownBtn.classList.add('tutorial-sub-highlight'); + await this._wait(1500); + ownBtn.classList.remove('tutorial-sub-highlight'); + + ar = allBtn.getBoundingClientRect(); + or2 = ownBtn.getBoundingClientRect(); + await this._animateCursor(or2.left+or2.width/2, or2.top+or2.height/2, ar.left+ar.width/2, ar.top+ar.height/2, 500); + allBtn.classList.add('tutorial-sub-highlight'); + await this._wait(1000); + allBtn.classList.remove('tutorial-sub-highlight'); + + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Faktencheck-Stati Demo + // ----------------------------------------------------------------------- + async _simulateFactcheckStati() { + this._demoRunning = true; + await this._wait(400); + var fcList = document.getElementById('factcheck-list'); + if (!fcList) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + + var stati = ['confirmed','established','unconfirmed','disputed','contradicted']; + var prevX, prevY; + for (var i = 0; i < stati.length; i++) { + if (!this._isActive) break; + var item = fcList.querySelector('[data-fc-status="' + stati[i] + '"]'); + if (!item) continue; + item.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + await this._wait(300); + var r = item.getBoundingClientRect(); + var tx = r.left + 20, ty = r.top + r.height/2; + if (prevX !== undefined) { + await this._animateCursor(prevX, prevY, tx, ty, 400); + } else { + this._showCursor(tx - 40, ty - 30, 'default'); + await this._wait(200); + await this._animateCursor(tx - 40, ty - 30, tx, ty, 400); + } + item.classList.add('tutorial-sub-highlight'); + await this._wait(1500); + if (i < stati.length - 1) item.classList.remove('tutorial-sub-highlight'); + prevX = tx; prevY = ty; + } + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Timeline Demo + // ----------------------------------------------------------------------- + async _simulateTimeline() { + this._demoRunning = true; + await this._wait(400); + var points = document.querySelectorAll('.ht-point'); + if (!points.length) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + var detailPanel = document.querySelector('.ht-detail-panel'); + var entries = this._DEMO_TIMELINE; + var prevX, prevY; + for (var i = 0; i < Math.min(points.length, entries.length); i++) { + if (!this._isActive) break; + var point = points[i]; + var entry = entries[i]; + var r = point.getBoundingClientRect(); + var tx = r.left + r.width/2, ty = r.top + r.height/2; + if (prevX !== undefined) { + await this._animateCursor(prevX, prevY, tx, ty, 400); + } else { + this._showCursor(tx - 40, ty - 30, 'default'); + await this._wait(200); + await this._animateCursor(tx - 40, ty - 30, tx, ty, 400); + } + points.forEach(function(p) { p.classList.remove('active'); }); + point.classList.add('active'); + if (detailPanel) { + var isSn = entry.type === 'snapshot'; + detailPanel.innerHTML = '
16.03.2026, ' + + entry.time + '' + (isSn ? 'Lagebericht' : '1 Meldung') + + '
' + + entry.source + '
' + entry.title + '
'; + } + await this._wait(1500); + prevX = tx; prevY = ty; + } + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Karte-Buttons Demo + // ----------------------------------------------------------------------- + async _simulateKarteButtons() { + this._demoRunning = true; + await this._wait(400); + if (this._demoMapTileMap) { + this._demoMapTileMap.invalidateSize(); + this._demoMapTileMap.setView([53.545, 9.98], 12); + } + var geoBtn = document.getElementById('geoparse-btn'); + var expandBtn = document.getElementById('map-expand-btn'); + var prevX, prevY; + if (geoBtn) { + this._highlightSub('#geoparse-btn'); + var gr = geoBtn.getBoundingClientRect(); + this._showCursor(gr.left - 40, gr.top - 30, 'default'); + await this._wait(300); + await this._animateCursor(gr.left-40, gr.top-30, gr.left+gr.width/2, gr.top+gr.height/2, 500); + prevX = gr.left+gr.width/2; prevY = gr.top+gr.height/2; + await this._wait(2000); + this._clearSubHighlights(); + } + if (expandBtn) { + this._highlightSub('#map-expand-btn'); + var er = expandBtn.getBoundingClientRect(); + if (prevX !== undefined) { + await this._animateCursor(prevX, prevY, er.left+er.width/2, er.top+er.height/2, 500); + } else { + this._showCursor(er.left+er.width/2, er.top+er.height/2, 'default'); + } + await this._wait(2000); + this._clearSubHighlights(); + } + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- + // Layout Demo (Drag + Resize kombiniert) + // ----------------------------------------------------------------------- + async _simulateLayoutDemo() { + this._demoRunning = true; + var lbTile = document.querySelector('[gs-id="lagebild"]'); + var fcTile = document.querySelector('[gs-id="faktencheck"]'); + if (!lbTile || !fcTile) { this._demoRunning = false; this._enableNavAfterDemo(); return; } + var grid = typeof LayoutManager !== 'undefined' ? LayoutManager._grid : null; + var self = this; + + // Phase 1: Drag + var lbH = lbTile.querySelector('.card-header'); + if (lbH) { + var lbR = lbH.getBoundingClientRect(); + var fcR = fcTile.getBoundingClientRect(); + var sx = lbR.left+lbR.width/2, sy = lbR.top+lbR.height/2; + var ex = fcR.left+fcR.width/2, ey = fcR.top+lbR.height/2; + + this._showCursor(sx-40, sy-30, 'default'); + await this._wait(300); + await this._animateCursor(sx-40, sy-30, sx, sy, 400); + await this._wait(200); + this._els.cursor.classList.remove('tutorial-cursor-default'); + this._els.cursor.classList.add('tutorial-cursor-grabbing'); + await this._wait(200); + + lbTile.style.transition = 'none'; + lbTile.style.zIndex = '9002'; + var dx = ex-sx, dy = ey-sy; + var ms = null; + await new Promise(function(resolve) { + function f(ts) { + if (!self._isActive) { resolve(); return; } + if (!ms) ms = ts; + var p = Math.min((ts-ms)/1200,1); + var e2 = p<0.5?4*p*p*p:1-Math.pow(-2*p+2,3)/2; + lbTile.style.transform = 'translate('+(dx*e2)+'px,'+(dy*e2)+'px)'; + self._els.cursor.style.left = (sx+dx*e2)+'px'; + self._els.cursor.style.top = (sy+dy*e2)+'px'; + if (p<1) requestAnimationFrame(f); else resolve(); + } + requestAnimationFrame(f); + }); + await this._wait(400); + + lbTile.style.transform = ''; + lbTile.style.transition = ''; + lbTile.style.zIndex = ''; + if (grid) { + var ln = grid.engine.nodes.find(function(n){return n.el===lbTile;}); + var fn = grid.engine.nodes.find(function(n){return n.el===fcTile;}); + if (ln && fn) { + var tmpX=ln.x, tmpY=ln.y; + grid.update(lbTile,{x:fn.x,y:fn.y}); + grid.update(fcTile,{x:tmpX,y:tmpY}); + grid.compact(); + } + } + this._els.cursor.classList.remove('tutorial-cursor-grabbing'); + this._els.cursor.classList.add('tutorial-cursor-default'); + await this._wait(800); + } + + // Phase 2: Resize + lbTile = document.querySelector('[gs-id="lagebild"]'); + if (lbTile) { + var tr = lbTile.getBoundingClientRect(); + var hx = tr.right-6, hy = tr.bottom-6; + await this._animateCursor( + parseFloat(this._els.cursor.style.left)||hx-40, + parseFloat(this._els.cursor.style.top)||hy-30, hx, hy, 500); + await this._wait(200); + this._els.cursor.classList.remove('tutorial-cursor-default'); + this._els.cursor.classList.add('tutorial-cursor-resize'); + await this._wait(200); + + lbTile.style.transition = 'none'; + lbTile.style.zIndex = '9002'; + lbTile.style.transformOrigin = 'top left'; + var ow = tr.width, oh = tr.height; + var epx = ow*0.6, epy = oh*0.5; + var rs = null; + await new Promise(function(resolve) { + function f(ts) { + if (!self._isActive) { resolve(); return; } + if (!rs) rs = ts; + var p = Math.min((ts-rs)/1200,1); + var e3 = p<0.5?4*p*p*p:1-Math.pow(-2*p+2,3)/2; + lbTile.style.transform = 'scale('+(1+epx/ow*e3)+','+(1+epy/oh*e3)+')'; + self._els.cursor.style.left = (hx+epx*e3)+'px'; + self._els.cursor.style.top = (hy+epy*e3)+'px'; + if (p<1) requestAnimationFrame(f); else resolve(); + } + requestAnimationFrame(f); + }); + await this._wait(600); + + lbTile.style.transform = ''; + lbTile.style.transformOrigin = ''; + lbTile.style.transition = ''; + lbTile.style.zIndex = ''; + if (grid) { + var ln2 = grid.engine.nodes.find(function(n){return n.el===lbTile;}); + if (ln2) { grid.update(lbTile,{w:12,h:Math.max(ln2.h+3,6)}); grid.compact(); } + } + await this._wait(600); + } + + this._hideCursor(); + this._demoRunning = false; + this._enableNavAfterDemo(); + }, + + // ----------------------------------------------------------------------- // Navigation nach Demo-Ende freigeben // -----------------------------------------------------------------------