Files
website/static/js/modules/mindmap.js

310 lines
20 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Mindmap.js - Modul für die Mindmap-Visualisierung mit Cytoscape.js
* Version: 1.0.0
* Datum: 01.05.2025
*/
// Stellen Sie sicher, dass das globale MindMap-Objekt existiert
if (!window.MindMap) {
window.MindMap = {};
}
/**
* Mindmap-Visualisierungsklasse
*/
class MindmapVisualization {
/**
* Konstruktor für die Mindmap-Visualisierung
* @param {string} containerId - ID des Container-Elements
* @param {Object} options - Konfigurationsoptionen
*/
constructor(containerId, options = {}) {
this.containerId = containerId;
this.container = document.getElementById(containerId);
this.cy = null;
this.data = null;
this.options = Object.assign({
defaultLayout: 'cose',
darkMode: document.documentElement.classList.contains('dark'),
apiEndpoint: '/api/mindmap/public',
onNodeClick: null
}, options);
// Event-Listener für Dark Mode-Änderungen
document.addEventListener('darkModeToggled', (event) => {
this.options.darkMode = event.detail.isDark;
if (this.cy) {
this.updateStyles();
}
});
}
/**
* Initialisiert die Mindmap
*/
async initialize() {
console.log("Initialisiere Mindmap...");
try {
// Versuche, Daten vom Server zu laden
this.data = await this.loadDataFromServer();
console.log("Daten vom Server geladen:", this.data);
} catch (error) {
console.warn("Fehler beim Laden der Daten vom Server:", error);
console.log("Verwende Standarddaten als Fallback");
this.data = this.getDefaultData();
}
// Cytoscape initialisieren
this.initializeCytoscape();
return this;
}
/**
* Lädt Daten vom Server
* @returns {Promise} - Promise mit den geladenen Daten
*/
async loadDataFromServer() {
try {
const response = await fetch(this.options.apiEndpoint);
if (!response.ok) {
throw new Error(`HTTP Fehler: ${response.status}`);
}
return await response.json();
} catch (error) {
console.error("Fehler beim Laden der Daten:", error);
throw error;
}
}
/**
* Liefert Standarddaten als Fallback
* @returns {Object} - Standarddaten für die Mindmap
*/
getDefaultData() {
return {
nodes: [
{ id: "root", name: "Wissen", description: "Zentrale Wissensbasis", color_code: "#4299E1" },
{ id: "philosophy", name: "Philosophie", description: "Philosophisches Denken", color_code: "#9F7AEA" },
{ id: "science", name: "Wissenschaft", description: "Wissenschaftliche Erkenntnisse", color_code: "#48BB78" },
{ id: "technology", name: "Technologie", description: "Technologische Entwicklungen", color_code: "#ED8936" },
{ id: "arts", name: "Künste", description: "Künstlerische Ausdrucksformen", color_code: "#ED64A6" }
],
edges: [
{ source: "root", target: "philosophy" },
{ source: "root", target: "science" },
{ source: "root", target: "technology" },
{ source: "root", target: "arts" }
]
};
}
/**
* Initialisiert Cytoscape.js
*/
initializeCytoscape() {
if (!this.container) {
console.error(`Container mit ID '${this.containerId}' nicht gefunden.`);
return;
}
// Konvertiert die API-Daten ins Cytoscape-Format
const elements = this.convertToCytoscapeFormat(this.data);
// Erstellt die Cytoscape-Instanz
this.cy = cytoscape({
container: this.container,
elements: elements,
style: this.getStyles(),
layout: {
name: this.options.defaultLayout,
animate: true,
animationDuration: 800,
nodeDimensionsIncludeLabels: true,
padding: 30,
spacingFactor: 1.2,
randomize: true,
componentSpacing: 100,
nodeRepulsion: 8000,
edgeElasticity: 100,
nestingFactor: 1.2,
gravity: 80
}
});
// Event-Listener für Knotenklicks
this.cy.on('tap', 'node', (event) => {
const node = event.target;
this.handleNodeClick(node);
});
console.log("Cytoscape initialisiert");
}
/**
* Konvertiert API-Daten ins Cytoscape-Format
* @param {Object} data - Daten von der API
* @returns {Array} - Elemente im Cytoscape-Format
*/
convertToCytoscapeFormat(data) {
const elements = [];
// Knoten hinzufügen
if (data.nodes && Array.isArray(data.nodes)) {
data.nodes.forEach(node => {
elements.push({
data: {
id: node.id,
name: node.name,
description: node.description,
color: node.color_code || '#8B5CF6',
category: node.category_id
}
});
});
}
// Kanten hinzufügen
if (data.edges && Array.isArray(data.edges)) {
data.edges.forEach(edge => {
elements.push({
data: {
id: `${edge.source}-${edge.target}`,
source: edge.source,
target: edge.target
}
});
});
}
return elements;
}
/**
* Liefert die Styles für Cytoscape
* @returns {Array} - Cytoscape-Stylesheets
*/
getStyles() {
const darkMode = this.options.darkMode;
return [
{
selector: 'node',
style: {
'background-color': 'data(color)',
'label': 'data(name)',
'width': 40,
'height': 40,
'font-size': 12,
'text-valign': 'bottom',
'text-halign': 'center',
'text-margin-y': 8,
'color': darkMode ? '#f1f5f9' : '#334155',
'text-background-color': darkMode ? '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': darkMode ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
'target-arrow-color': darkMode ? 'rgba(255, 255, 255, 0.15)' : 'rgba(30, 41, 59, 0.15)',
'curve-style': 'bezier',
'target-arrow-shape': 'triangle'
}
},
{
selector: 'node:selected',
style: {
'background-color': 'data(color)',
'border-width': 3,
'border-color': '#8b5cf6',
'width': 50,
'height': 50,
'font-size': 14,
'font-weight': 'bold',
'text-background-color': '#8b5cf6',
'text-background-opacity': 0.9
}
}
];
}
/**
* Aktualisiert die Styles basierend auf dem Dark Mode
*/
updateStyles() {
this.cy.style(this.getStyles());
}
/**
* Behandelt den Klick auf einen Knoten
* @param {Object} node - Cytoscape-Knoten
*/
handleNodeClick(node) {
console.log("Knoten angeklickt:", node.id(), node.data());
// Callback aufrufen, falls definiert
if (typeof this.options.onNodeClick === 'function') {
this.options.onNodeClick(node.data());
}
}
/**
* Passt die Ansicht an alle Elemente an
*/
fitToElements() {
if (this.cy) {
this.cy.fit();
this.cy.center();
}
}
/**
* Setzt das Layout zurück
* @param {string} layoutName - Name des Layouts (optional)
*/
resetLayout(layoutName) {
if (this.cy) {
this.cy.layout({
name: layoutName || this.options.defaultLayout,
animate: true,
randomize: true,
fit: true
}).run();
}
}
}
// Globale Export
window.MindMap.Visualization = MindmapVisualization;
// Automatische Initialisierung, wenn das DOM geladen ist
document.addEventListener('DOMContentLoaded', function() {
const cyContainer = document.getElementById('cy');
if (cyContainer) {
console.log("Mindmap-Container gefunden, initialisiere...");
const mindmap = new MindmapVisualization('cy', {
onNodeClick: function(nodeData) {
console.log("Knoten ausgewählt:", nodeData);
// Hier könnte man weitere Aktionen durchführen
}
});
mindmap.initialize().then(() => {
console.log("Mindmap erfolgreich initialisiert");
// Speichere die Instanz global für den Zugriff von außen
window.mindmap = mindmap;
}).catch(error => {
console.error("Fehler bei der Initialisierung der Mindmap:", error);
});
}
});