/** * 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...'); // Hauptcontainer für die Mindmap const cyContainer = document.getElementById('cy'); if (!cyContainer) { console.error('Mindmap-Container #cy nicht gefunden!'); return; } // Info-Panel für Knotendetails const nodeInfoPanel = document.getElementById('node-info-panel'); const nodeDescription = document.getElementById('node-description'); const connectedNodes = document.getElementById('connected-nodes'); // Toolbar-Buttons const fitButton = document.getElementById('fit-btn'); const resetButton = document.getElementById('reset-btn'); const toggleLabelsButton = document.getElementById('toggle-labels-btn'); // Mindmap-Instanz let mindmap = null; // Cytoscape.js für die Visualisierung initialisieren try { // Cytoscape.js-Bibliothek überprüfen if (typeof cytoscape === 'undefined') { loadExternalScript('https://cdnjs.cloudflare.com/ajax/libs/cytoscape/3.26.0/cytoscape.min.js') .then(() => { initCytoscape(); }) .catch(error => { console.error('Fehler beim Laden von Cytoscape.js:', error); showErrorMessage(cyContainer, 'Cytoscape.js konnte nicht geladen werden.'); }); } else { initCytoscape(); } } catch (error) { console.error('Fehler bei der Initialisierung der Mindmap:', error); showErrorMessage(cyContainer, 'Die Mindmap konnte nicht initialisiert werden: ' + error.message); } /** * Lädt ein externes Script asynchron */ function loadExternalScript(url) { return new Promise((resolve, reject) => { const script = document.createElement('script'); script.src = url; script.onload = resolve; script.onerror = reject; document.head.appendChild(script); }); } /** * Zeigt eine Fehlermeldung im Container an */ function showErrorMessage(container, message) { container.innerHTML = `

${message}

Bitte laden Sie die Seite neu oder kontaktieren Sie den Support.

`; } /** * Initialisiert Cytoscape mit den Mindmap-Daten */ function initCytoscape() { console.log('Cytoscape.js wird initialisiert...'); // Zeige Ladeanimation cyContainer.innerHTML = `
`; // Lade Daten vom Backend fetch('/api/mindmap') .then(response => { if (!response.ok) { throw new Error('Netzwerkfehler beim Laden der Mindmap-Daten'); } return response.json(); }) .then(data => { console.log('Mindmap-Daten erfolgreich geladen'); renderMindmap(data); }) .catch(error => { console.error('Fehler beim Laden der Mindmap-Daten:', error); // Verwende Standarddaten als Fallback console.log('Verwende Standarddaten als Fallback...'); const defaultData = generateDefaultData(); renderMindmap(defaultData); }); } /** * 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)]; } }