/** * Mindmap-Seite JavaScript * Spezifische Funktionen für die Mindmap-Seite */ // Füge das Modul zum globalen MindMap-Objekt hinzu if (!window.MindMap) { window.MindMap = {}; } // Registriere den Initialisierer im MindMap-Objekt window.MindMap.pageInitializers = window.MindMap.pageInitializers || {}; window.MindMap.pageInitializers.mindmap = initMindmapPage; // Event-Listener für DOMContentLoaded document.addEventListener('DOMContentLoaded', function() { // Prüfe, ob wir auf der Mindmap-Seite sind if (document.body && document.body.dataset && document.body.dataset.page === 'mindmap') { initMindmapPage(); } }); /** * Initialisiert die Mindmap-Seite */ function initMindmapPage() { console.log('Mindmap-Seite wird initialisiert...'); // Warte auf die Cytoscape-Instanz document.addEventListener('mindmap-loaded', function() { const cy = window.cy; if (!cy) return; // Event-Listener für Knoten-Klicks cy.on('tap', 'node', function(evt) { const node = evt.target; // Alle vorherigen Hervorhebungen zurücksetzen cy.nodes().forEach(n => { n.removeStyle(); n.connectedEdges().removeStyle(); }); // Speichere ausgewählten Knoten window.mindmapInstance.selectedNode = node; // Aktiviere leuchtenden Effekt statt Umkreisung node.style({ 'background-opacity': 1, 'background-color': node.data('color'), 'shadow-color': node.data('color'), 'shadow-opacity': 1, 'shadow-blur': 15, 'shadow-offset-x': 0, 'shadow-offset-y': 0 }); // Verbundene Kanten und Knoten hervorheben const connectedEdges = node.connectedEdges(); const connectedNodes = node.neighborhood('node'); connectedEdges.style({ 'line-color': '#a78bfa', 'target-arrow-color': '#a78bfa', 'source-arrow-color': '#a78bfa', 'line-opacity': 0.8, 'width': 2 }); connectedNodes.style({ 'shadow-opacity': 0.7, 'shadow-blur': 10, 'shadow-color': '#a78bfa' }); // Info-Panel aktualisieren updateInfoPanel(node); // Seitenleiste aktualisieren updateSidebar(node); }); // Klick auf Hintergrund - Auswahl zurücksetzen cy.on('tap', function(evt) { if (evt.target === cy) { resetSelection(cy); } }); // Zoom-Controls document.getElementById('zoomIn')?.addEventListener('click', () => { cy.zoom({ level: cy.zoom() * 1.2, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } }); }); document.getElementById('zoomOut')?.addEventListener('click', () => { cy.zoom({ level: cy.zoom() / 1.2, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } }); }); document.getElementById('resetView')?.addEventListener('click', () => { cy.fit(); resetSelection(cy); }); // Legend-Toggle document.getElementById('toggleLegend')?.addEventListener('click', () => { const legend = document.getElementById('categoryLegend'); if (legend) { isLegendVisible = !isLegendVisible; legend.style.display = isLegendVisible ? 'block' : 'none'; } }); // Keyboard-Controls document.addEventListener('keydown', (e) => { if (e.key === '+' || e.key === '=') { cy.zoom({ level: cy.zoom() * 1.2, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } }); } else if (e.key === '-' || e.key === '_') { cy.zoom({ level: cy.zoom() / 1.2, renderedPosition: { x: cy.width() / 2, y: cy.height() / 2 } }); } else if (e.key === 'Escape') { resetSelection(cy); } }); }); } /** * Aktualisiert das Info-Panel mit Knoteninformationen * @param {Object} node - Der ausgewählte Knoten */ function updateInfoPanel(node) { const infoPanel = document.getElementById('infoPanel'); if (!infoPanel) return; const data = node.data(); const connectedNodes = node.neighborhood('node'); let html = `

${data.label || data.name}

${data.category || 'Keine Kategorie'}

${data.description ? `

${data.description}

` : ''}

Verbindungen (${connectedNodes.length})

`; infoPanel.innerHTML = html; infoPanel.style.display = 'block'; } /** * Aktualisiert die Seitenleiste mit Knoteninformationen * @param {Object} node - Der ausgewählte Knoten */ function updateSidebar(node) { const sidebar = document.getElementById('sidebar'); if (!sidebar) return; const data = node.data(); const connectedNodes = node.neighborhood('node'); let html = `

${data.label || data.name}

${data.category || 'Keine Kategorie'}

${data.description ? `

${data.description}

` : ''}

Verbindungen (${connectedNodes.length})

`; sidebar.innerHTML = html; } /** * Setzt die Auswahl zurück * @param {Object} cy - Cytoscape-Instanz */ function resetSelection(cy) { window.mindmapInstance.selectedNode = null; // Alle Hervorhebungen zurücksetzen cy.nodes().forEach(node => { node.removeStyle(); node.connectedEdges().removeStyle(); }); // Info-Panel ausblenden const infoPanel = document.getElementById('infoPanel'); if (infoPanel) { infoPanel.style.display = 'none'; } // Seitenleiste leeren const sidebar = document.getElementById('sidebar'); if (sidebar) { sidebar.innerHTML = ''; } } /** * Generiert Standarddaten für die Mindmap als Fallback */ function generateDefaultData() { return { nodes: [ { id: 'root', name: 'Wissen', description: 'Zentrale Wissensbasis', category: 'Zentral', color_code: '#4299E1' }, { id: 'philosophy', name: 'Philosophie', description: 'Philosophisches Denken', category: 'Philosophie', color_code: '#9F7AEA', parent_id: 'root' }, { id: 'science', name: 'Wissenschaft', description: 'Wissenschaftliche Erkenntnisse', category: 'Wissenschaft', color_code: '#48BB78', parent_id: 'root' }, { id: 'technology', name: 'Technologie', description: 'Technologische Entwicklungen', category: 'Technologie', color_code: '#ED8936', parent_id: 'root' }, { id: 'arts', name: 'Künste', description: 'Künstlerische Ausdrucksformen', category: 'Künste', color_code: '#ED64A6', parent_id: 'root' }, { id: 'psychology', name: 'Psychologie', description: 'Menschliches Verhalten und Geist', category: 'Psychologie', color_code: '#4299E1', parent_id: 'root' } ] }; } /** * Rendert die Mindmap mit Cytoscape.js */ function renderMindmap(data) { console.log('Rendere Mindmap mit Daten:', data); // Konvertiere Backend-Daten in Cytoscape-Format const elements = convertToCytoscapeFormat(data); // Leere den Container cyContainer.innerHTML = ''; // Erstelle Cytoscape-Instanz mindmap = cytoscape({ container: cyContainer, elements: elements, style: [ { selector: 'node', style: { 'background-color': 'data(color)', 'label': 'data(name)', 'width': 30, 'height': 30, 'font-size': 12, 'text-valign': 'bottom', 'text-halign': 'center', 'text-margin-y': 8, 'color': document.documentElement.classList.contains('dark') ? '#f1f5f9' : '#334155', 'text-background-color': document.documentElement.classList.contains('dark') ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)', 'text-background-opacity': 0.8, 'text-background-padding': '2px', 'text-background-shape': 'roundrectangle', 'text-wrap': 'ellipsis', 'text-max-width': '100px' } }, { selector: 'edge', style: { 'width': 2, 'line-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)', 'target-arrow-color': document.documentElement.classList.contains('dark') ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)', 'curve-style': 'bezier' } }, { selector: 'node:selected', style: { 'background-color': 'data(color)', 'border-width': 3, 'border-color': '#8b5cf6', 'width': 40, 'height': 40, 'font-size': 14, 'font-weight': 'bold', 'text-background-color': '#8b5cf6', 'text-background-opacity': 0.9 } } ], layout: { name: 'cose', animate: true, animationDuration: 800, nodeDimensionsIncludeLabels: true, refresh: 30, randomize: true, componentSpacing: 100, nodeRepulsion: 8000, nodeOverlap: 20, idealEdgeLength: 200, edgeElasticity: 100, nestingFactor: 1.2, gravity: 80, fit: true, padding: 30 } }); // Event-Listener für Knoteninteraktionen mindmap.on('tap', 'node', function(evt) { const node = evt.target; const nodeData = node.data(); // Update Info-Panel updateNodeInfoPanel(nodeData); // Lade verbundene Knoten updateConnectedNodes(node); }); // Toolbar-Buttons aktivieren if (fitButton) { fitButton.addEventListener('click', () => { mindmap.fit(); mindmap.center(); }); } if (resetButton) { resetButton.addEventListener('click', () => { mindmap.layout({ name: 'cose', animate: true, randomize: true, fit: true }).run(); }); } if (toggleLabelsButton) { let labelsVisible = true; toggleLabelsButton.addEventListener('click', () => { labelsVisible = !labelsVisible; if (labelsVisible) { mindmap.style() .selector('node') .style('label', 'data(name)') .update(); } else { mindmap.style() .selector('node') .style('label', '') .update(); } }); } // Dark Mode-Änderungen überwachen document.addEventListener('darkModeToggled', function(event) { updateDarkModeStyles(event.detail.isDark); }); // Initial fit und center setTimeout(() => { mindmap.fit(); mindmap.center(); }, 100); } /** * Konvertiert die Backend-Daten ins Cytoscape-Format */ function convertToCytoscapeFormat(data) { const elements = { nodes: [], edges: [] }; // Nodes hinzufügen if (data.nodes && data.nodes.length > 0) { data.nodes.forEach(node => { elements.nodes.push({ data: { id: String(node.id), name: node.name, description: node.description || 'Keine Beschreibung verfügbar', category: node.category || 'Allgemein', color: node.color_code || getRandomColor() } }); // Kante zum Elternknoten hinzufügen (falls vorhanden) if (node.parent_id) { elements.edges.push({ data: { id: `edge-${node.parent_id}-${node.id}`, source: String(node.parent_id), target: String(node.id) } }); } }); // Zusätzliche Kanten zwischen Knoten hinzufügen (falls in den Daten vorhanden) if (data.edges && data.edges.length > 0) { data.edges.forEach(edge => { elements.edges.push({ data: { id: `edge-${edge.source}-${edge.target}`, source: String(edge.source), target: String(edge.target) } }); }); } } return elements; } /** * Aktualisiert das Informations-Panel mit den Knotendaten */ function updateNodeInfoPanel(nodeData) { if (nodeInfoPanel && nodeDescription) { // Panel anzeigen nodeInfoPanel.style.display = 'block'; // Titel und Beschreibung aktualisieren const titleElement = nodeInfoPanel.querySelector('.info-panel-title'); if (titleElement) { titleElement.textContent = nodeData.name; } if (nodeDescription) { nodeDescription.textContent = nodeData.description || 'Keine Beschreibung verfügbar'; } } } /** * Aktualisiert die Liste der verbundenen Knoten */ function updateConnectedNodes(node) { if (connectedNodes) { // Leere den Container connectedNodes.innerHTML = ''; // Hole verbundene Knoten const connectedEdges = node.connectedEdges(); if (connectedEdges.length === 0) { connectedNodes.innerHTML = '
Keine verbundenen Knoten
'; return; } // Füge alle verbundenen Knoten hinzu connectedEdges.forEach(edge => { const targetNode = edge.target().id() === node.id() ? edge.source() : edge.target(); const targetData = targetNode.data(); const nodeLink = document.createElement('div'); nodeLink.className = 'node-link'; nodeLink.innerHTML = ` ${targetData.name} `; // Klick-Event zum Fokussieren des Knotens nodeLink.addEventListener('click', () => { mindmap.center(targetNode); targetNode.select(); updateNodeInfoPanel(targetData); updateConnectedNodes(targetNode); }); connectedNodes.appendChild(nodeLink); }); } } /** * Aktualisiert die Styles bei Dark Mode-Änderungen */ function updateDarkModeStyles(isDark) { if (!mindmap) return; const textColor = isDark ? '#f1f5f9' : '#334155'; const textBgColor = isDark ? 'rgba(30, 41, 59, 0.8)' : 'rgba(241, 245, 249, 0.8)'; const edgeColor = isDark ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)'; mindmap.style() .selector('node') .style({ 'color': textColor, 'text-background-color': textBgColor }) .selector('edge') .style({ 'line-color': edgeColor, 'target-arrow-color': edgeColor }) .update(); } /** * Generiert eine zufällige Farbe */ function getRandomColor() { const colors = [ '#4299E1', // Blau '#9F7AEA', // Lila '#48BB78', // Grün '#ED8936', // Orange '#ED64A6', // Pink '#F56565' // Rot ]; return colors[Math.floor(Math.random() * colors.length)]; } // Initialisiere die Mindmap-Seite initMindmapPage();