Mindmap wird nicht initialisiert ! :(
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -166,8 +166,8 @@ function updateInfoPanel(node) {
|
|||||||
|
|
||||||
html += `
|
html += `
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
infoPanel.innerHTML = html;
|
infoPanel.innerHTML = html;
|
||||||
infoPanel.style.display = 'block';
|
infoPanel.style.display = 'block';
|
||||||
@@ -206,8 +206,8 @@ function updateSidebar(node) {
|
|||||||
html += `
|
html += `
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
sidebar.innerHTML = html;
|
sidebar.innerHTML = html;
|
||||||
}
|
}
|
||||||
@@ -236,316 +236,316 @@ function resetSelection(cy) {
|
|||||||
if (sidebar) {
|
if (sidebar) {
|
||||||
sidebar.innerHTML = '';
|
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', () => {
|
* Generiert Standarddaten für die Mindmap als Fallback
|
||||||
mindmap.layout({
|
*/
|
||||||
|
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',
|
name: 'cose',
|
||||||
animate: true,
|
animate: true,
|
||||||
|
animationDuration: 800,
|
||||||
|
nodeDimensionsIncludeLabels: true,
|
||||||
|
refresh: 30,
|
||||||
randomize: true,
|
randomize: true,
|
||||||
fit: true
|
componentSpacing: 100,
|
||||||
}).run();
|
nodeRepulsion: 8000,
|
||||||
});
|
nodeOverlap: 20,
|
||||||
}
|
idealEdgeLength: 200,
|
||||||
|
edgeElasticity: 100,
|
||||||
if (toggleLabelsButton) {
|
nestingFactor: 1.2,
|
||||||
let labelsVisible = true;
|
gravity: 80,
|
||||||
toggleLabelsButton.addEventListener('click', () => {
|
fit: true,
|
||||||
labelsVisible = !labelsVisible;
|
padding: 30
|
||||||
|
|
||||||
if (labelsVisible) {
|
|
||||||
mindmap.style()
|
|
||||||
.selector('node')
|
|
||||||
.style('label', 'data(name)')
|
|
||||||
.update();
|
|
||||||
} else {
|
|
||||||
mindmap.style()
|
|
||||||
.selector('node')
|
|
||||||
.style('label', '')
|
|
||||||
.update();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dark Mode-Änderungen überwachen
|
/**
|
||||||
document.addEventListener('darkModeToggled', function(event) {
|
* Konvertiert die Backend-Daten ins Cytoscape-Format
|
||||||
updateDarkModeStyles(event.detail.isDark);
|
*/
|
||||||
});
|
function convertToCytoscapeFormat(data) {
|
||||||
|
const elements = {
|
||||||
|
nodes: [],
|
||||||
|
edges: []
|
||||||
|
};
|
||||||
|
|
||||||
// Initial fit und center
|
// Nodes hinzufügen
|
||||||
setTimeout(() => {
|
if (data.nodes && data.nodes.length > 0) {
|
||||||
mindmap.fit();
|
data.nodes.forEach(node => {
|
||||||
mindmap.center();
|
elements.nodes.push({
|
||||||
}, 100);
|
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)
|
||||||
* Konvertiert die Backend-Daten ins Cytoscape-Format
|
if (node.parent_id) {
|
||||||
*/
|
elements.edges.push({
|
||||||
function convertToCytoscapeFormat(data) {
|
data: {
|
||||||
const elements = {
|
id: `edge-${node.parent_id}-${node.id}`,
|
||||||
nodes: [],
|
source: String(node.parent_id),
|
||||||
edges: []
|
target: String(node.id)
|
||||||
};
|
}
|
||||||
|
});
|
||||||
// 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)
|
// Zusätzliche Kanten zwischen Knoten hinzufügen (falls in den Daten vorhanden)
|
||||||
if (node.parent_id) {
|
if (data.edges && data.edges.length > 0) {
|
||||||
elements.edges.push({
|
data.edges.forEach(edge => {
|
||||||
data: {
|
elements.edges.push({
|
||||||
id: `edge-${node.parent_id}-${node.id}`,
|
data: {
|
||||||
source: String(node.parent_id),
|
id: `edge-${edge.source}-${edge.target}`,
|
||||||
target: String(node.id)
|
source: String(edge.source),
|
||||||
}
|
target: String(edge.target)
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
// Zusätzliche Kanten zwischen Knoten hinzufügen (falls in den Daten vorhanden)
|
return elements;
|
||||||
if (data.edges && data.edges.length > 0) {
|
}
|
||||||
data.edges.forEach(edge => {
|
|
||||||
elements.edges.push({
|
/**
|
||||||
data: {
|
* Aktualisiert das Informations-Panel mit den Knotendaten
|
||||||
id: `edge-${edge.source}-${edge.target}`,
|
*/
|
||||||
source: String(edge.source),
|
function updateNodeInfoPanel(nodeData) {
|
||||||
target: String(edge.target)
|
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 = '<div class="text-sm italic">Keine verbundenen Knoten</div>';
|
||||||
|
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 = `
|
||||||
|
<span class="w-2 h-2 rounded-full" style="background-color: ${targetData.color}"></span>
|
||||||
|
${targetData.name}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Klick-Event zum Fokussieren des Knotens
|
||||||
|
nodeLink.addEventListener('click', () => {
|
||||||
|
mindmap.center(targetNode);
|
||||||
|
targetNode.select();
|
||||||
|
updateNodeInfoPanel(targetData);
|
||||||
|
updateConnectedNodes(targetNode);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connectedNodes.appendChild(nodeLink);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return elements;
|
/**
|
||||||
}
|
* Aktualisiert die Styles bei Dark Mode-Änderungen
|
||||||
|
*/
|
||||||
|
function updateDarkModeStyles(isDark) {
|
||||||
|
if (!mindmap) return;
|
||||||
|
|
||||||
/**
|
const textColor = isDark ? '#f1f5f9' : '#334155';
|
||||||
* Aktualisiert das Informations-Panel mit den Knotendaten
|
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)';
|
||||||
function updateNodeInfoPanel(nodeData) {
|
|
||||||
if (nodeInfoPanel && nodeDescription) {
|
|
||||||
// Panel anzeigen
|
|
||||||
nodeInfoPanel.style.display = 'block';
|
|
||||||
|
|
||||||
// Titel und Beschreibung aktualisieren
|
mindmap.style()
|
||||||
const titleElement = nodeInfoPanel.querySelector('.info-panel-title');
|
.selector('node')
|
||||||
if (titleElement) {
|
.style({
|
||||||
titleElement.textContent = nodeData.name;
|
'color': textColor,
|
||||||
}
|
'text-background-color': textBgColor
|
||||||
|
})
|
||||||
if (nodeDescription) {
|
.selector('edge')
|
||||||
nodeDescription.textContent = nodeData.description || 'Keine Beschreibung verfügbar';
|
.style({
|
||||||
}
|
'line-color': edgeColor,
|
||||||
|
'target-arrow-color': edgeColor
|
||||||
|
})
|
||||||
|
.update();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aktualisiert die Liste der verbundenen Knoten
|
* Generiert eine zufällige Farbe
|
||||||
*/
|
*/
|
||||||
function updateConnectedNodes(node) {
|
function getRandomColor() {
|
||||||
if (connectedNodes) {
|
const colors = [
|
||||||
// Leere den Container
|
'#4299E1', // Blau
|
||||||
connectedNodes.innerHTML = '';
|
'#9F7AEA', // Lila
|
||||||
|
'#48BB78', // Grün
|
||||||
// Hole verbundene Knoten
|
'#ED8936', // Orange
|
||||||
const connectedEdges = node.connectedEdges();
|
'#ED64A6', // Pink
|
||||||
|
'#F56565' // Rot
|
||||||
if (connectedEdges.length === 0) {
|
];
|
||||||
connectedNodes.innerHTML = '<div class="text-sm italic">Keine verbundenen Knoten</div>';
|
return colors[Math.floor(Math.random() * colors.length)];
|
||||||
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 = `
|
|
||||||
<span class="w-2 h-2 rounded-full" style="background-color: ${targetData.color}"></span>
|
|
||||||
${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
|
// Initialisiere die Mindmap-Seite
|
||||||
initMindmapPage();
|
initMindmapPage();
|
||||||
@@ -112,12 +112,6 @@
|
|||||||
<!-- ChatGPT Assistant -->
|
<!-- ChatGPT Assistant -->
|
||||||
<script src="{{ url_for('static', filename='js/modules/chatgpt-assistant.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/modules/chatgpt-assistant.js') }}"></script>
|
||||||
|
|
||||||
<!-- MindMap Visualization Module -->
|
|
||||||
<script src="{{ url_for('static', filename='js/modules/mindmap.js') }}"></script>
|
|
||||||
|
|
||||||
<!-- MindMap Page Module -->
|
|
||||||
<script src="{{ url_for('static', filename='js/modules/mindmap-page.js') }}"></script>
|
|
||||||
|
|
||||||
<!-- Neural Network Background Script -->
|
<!-- Neural Network Background Script -->
|
||||||
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
<script src="{{ url_for('static', filename='neural-network-background.js') }}"></script>
|
||||||
|
|
||||||
@@ -741,204 +735,205 @@
|
|||||||
<!-- Hilfsscripts -->
|
<!-- Hilfsscripts -->
|
||||||
{% block scripts %}{% endblock %}
|
{% block scripts %}{% endblock %}
|
||||||
|
|
||||||
<!-- KI-Chat Initialisierung -->
|
<!-- ChatGPT Initialisierung -->
|
||||||
<script>
|
<script>
|
||||||
// ChatGPT-Assistent Klasse
|
// Prüfe, ob ChatGPTAssistant bereits existiert
|
||||||
class ChatGPTAssistant {
|
if (typeof ChatGPTAssistant === 'undefined') {
|
||||||
constructor() {
|
class ChatGPTAssistant {
|
||||||
this.chatContainer = null;
|
constructor() {
|
||||||
this.messages = [];
|
this.chatContainer = null;
|
||||||
this.isOpen = false;
|
this.messages = [];
|
||||||
}
|
this.isOpen = false;
|
||||||
|
|
||||||
init() {
|
|
||||||
// Chat-Container erstellen, falls noch nicht vorhanden
|
|
||||||
if (!document.getElementById('chat-assistant-container')) {
|
|
||||||
this.createChatInterface();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event-Listener für Chat-Button
|
init() {
|
||||||
const chatButton = document.getElementById('chat-assistant-button');
|
// Chat-Container erstellen, falls noch nicht vorhanden
|
||||||
if (chatButton) {
|
if (!document.getElementById('chat-assistant-container')) {
|
||||||
chatButton.addEventListener('click', () => this.toggleChat());
|
this.createChatInterface();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event-Listener für Chat-Button
|
||||||
|
const chatButton = document.getElementById('chat-assistant-button');
|
||||||
|
if (chatButton) {
|
||||||
|
chatButton.addEventListener('click', () => this.toggleChat());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event-Listener für Senden-Button
|
||||||
|
const sendButton = document.getElementById('chat-send-button');
|
||||||
|
if (sendButton) {
|
||||||
|
sendButton.addEventListener('click', () => this.sendMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event-Listener für Eingabefeld (Enter-Taste)
|
||||||
|
const inputField = document.getElementById('chat-input');
|
||||||
|
if (inputField) {
|
||||||
|
inputField.addEventListener('keypress', (e) => {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
this.sendMessage();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('KI-Assistent erfolgreich initialisiert');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Event-Listener für Senden-Button
|
createChatInterface() {
|
||||||
const sendButton = document.getElementById('chat-send-button');
|
// Chat-Button erstellen
|
||||||
if (sendButton) {
|
const chatButton = document.createElement('button');
|
||||||
sendButton.addEventListener('click', () => this.sendMessage());
|
chatButton.id = 'chat-assistant-button';
|
||||||
}
|
chatButton.className = 'fixed bottom-6 right-6 bg-primary-600 text-white rounded-full p-4 shadow-lg z-50 hover:bg-primary-700 transition-all';
|
||||||
|
chatButton.innerHTML = '<i class="fas fa-robot text-xl"></i>';
|
||||||
|
document.body.appendChild(chatButton);
|
||||||
|
|
||||||
// Event-Listener für Eingabefeld (Enter-Taste)
|
// Chat-Container erstellen
|
||||||
const inputField = document.getElementById('chat-input');
|
const chatContainer = document.createElement('div');
|
||||||
if (inputField) {
|
chatContainer.id = 'chat-assistant-container';
|
||||||
inputField.addEventListener('keypress', (e) => {
|
chatContainer.className = 'fixed bottom-24 right-6 w-80 md:w-96 bg-white dark:bg-gray-800 rounded-xl shadow-xl z-50 flex flex-col transition-all duration-300 transform scale-0 origin-bottom-right';
|
||||||
if (e.key === 'Enter') {
|
chatContainer.style.height = '500px';
|
||||||
e.preventDefault();
|
chatContainer.style.maxHeight = '70vh';
|
||||||
this.sendMessage();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('KI-Assistent erfolgreich initialisiert');
|
// Chat-Header
|
||||||
}
|
chatContainer.innerHTML = `
|
||||||
|
<div class="p-4 border-b dark:border-gray-700 flex justify-between items-center">
|
||||||
createChatInterface() {
|
<h3 class="font-bold text-gray-800 dark:text-white">Systades Assistent</h3>
|
||||||
// Chat-Button erstellen
|
<button id="chat-close-button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
||||||
const chatButton = document.createElement('button');
|
<i class="fas fa-times"></i>
|
||||||
chatButton.id = 'chat-assistant-button';
|
|
||||||
chatButton.className = 'fixed bottom-6 right-6 bg-primary-600 text-white rounded-full p-4 shadow-lg z-50 hover:bg-primary-700 transition-all';
|
|
||||||
chatButton.innerHTML = '<i class="fas fa-robot text-xl"></i>';
|
|
||||||
document.body.appendChild(chatButton);
|
|
||||||
|
|
||||||
// Chat-Container erstellen
|
|
||||||
const chatContainer = document.createElement('div');
|
|
||||||
chatContainer.id = 'chat-assistant-container';
|
|
||||||
chatContainer.className = 'fixed bottom-24 right-6 w-80 md:w-96 bg-white dark:bg-gray-800 rounded-xl shadow-xl z-50 flex flex-col transition-all duration-300 transform scale-0 origin-bottom-right';
|
|
||||||
chatContainer.style.height = '500px';
|
|
||||||
chatContainer.style.maxHeight = '70vh';
|
|
||||||
|
|
||||||
// Chat-Header
|
|
||||||
chatContainer.innerHTML = `
|
|
||||||
<div class="p-4 border-b dark:border-gray-700 flex justify-between items-center">
|
|
||||||
<h3 class="font-bold text-gray-800 dark:text-white">Systades Assistent</h3>
|
|
||||||
<button id="chat-close-button" class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
|
|
||||||
<i class="fas fa-times"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4"></div>
|
|
||||||
<div class="p-4 border-t dark:border-gray-700">
|
|
||||||
<div class="flex space-x-2">
|
|
||||||
<input id="chat-input" type="text" placeholder="Frage stellen..." class="flex-1 px-4 py-2 rounded-lg border dark:border-gray-700 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500">
|
|
||||||
<button id="chat-send-button" class="bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-all">
|
|
||||||
<i class="fas fa-paper-plane"></i>
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div id="chat-messages" class="flex-1 overflow-y-auto p-4 space-y-4"></div>
|
||||||
`;
|
<div class="p-4 border-t dark:border-gray-700">
|
||||||
|
<div class="flex space-x-2">
|
||||||
|
<input id="chat-input" type="text" placeholder="Frage stellen..." class="flex-1 px-4 py-2 rounded-lg border dark:border-gray-700 dark:bg-gray-700 dark:text-white focus:outline-none focus:ring-2 focus:ring-primary-500">
|
||||||
|
<button id="chat-send-button" class="bg-primary-600 text-white px-4 py-2 rounded-lg hover:bg-primary-700 transition-all">
|
||||||
|
<i class="fas fa-paper-plane"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
document.body.appendChild(chatContainer);
|
document.body.appendChild(chatContainer);
|
||||||
this.chatContainer = chatContainer;
|
this.chatContainer = chatContainer;
|
||||||
|
|
||||||
// Event-Listener für Schließen-Button
|
// Event-Listener für Schließen-Button
|
||||||
const closeButton = document.getElementById('chat-close-button');
|
const closeButton = document.getElementById('chat-close-button');
|
||||||
if (closeButton) {
|
if (closeButton) {
|
||||||
closeButton.addEventListener('click', () => this.toggleChat());
|
closeButton.addEventListener('click', () => this.toggleChat());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toggleChat() {
|
|
||||||
this.isOpen = !this.isOpen;
|
|
||||||
if (this.isOpen) {
|
|
||||||
this.chatContainer.classList.remove('scale-0');
|
|
||||||
this.chatContainer.classList.add('scale-100');
|
|
||||||
} else {
|
|
||||||
this.chatContainer.classList.remove('scale-100');
|
|
||||||
this.chatContainer.classList.add('scale-0');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async sendMessage() {
|
|
||||||
const inputField = document.getElementById('chat-input');
|
|
||||||
const messageText = inputField.value.trim();
|
|
||||||
|
|
||||||
if (!messageText) return;
|
|
||||||
|
|
||||||
// Benutzer-Nachricht anzeigen
|
|
||||||
this.addMessage('user', messageText);
|
|
||||||
inputField.value = '';
|
|
||||||
|
|
||||||
// Lade-Indikator anzeigen
|
|
||||||
this.addMessage('assistant', '...', 'loading-message');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// API-Anfrage senden
|
|
||||||
const response = await fetch('/api/assistant', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
messages: this.messages.map(msg => ({
|
|
||||||
role: msg.role,
|
|
||||||
content: msg.content
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
// Lade-Nachricht entfernen
|
|
||||||
const loadingMessage = document.getElementById('loading-message');
|
|
||||||
if (loadingMessage) {
|
|
||||||
loadingMessage.remove();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (data.error) {
|
toggleChat() {
|
||||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten: ' + data.error);
|
this.isOpen = !this.isOpen;
|
||||||
|
if (this.isOpen) {
|
||||||
|
this.chatContainer.classList.remove('scale-0');
|
||||||
|
this.chatContainer.classList.add('scale-100');
|
||||||
} else {
|
} else {
|
||||||
this.addMessage('assistant', data.response);
|
this.chatContainer.classList.remove('scale-100');
|
||||||
|
this.chatContainer.classList.add('scale-0');
|
||||||
}
|
}
|
||||||
} catch (error) {
|
}
|
||||||
console.error('Fehler bei der API-Anfrage:', error);
|
|
||||||
|
|
||||||
// Lade-Nachricht entfernen
|
async sendMessage() {
|
||||||
const loadingMessage = document.getElementById('loading-message');
|
const inputField = document.getElementById('chat-input');
|
||||||
if (loadingMessage) {
|
const messageText = inputField.value.trim();
|
||||||
loadingMessage.remove();
|
|
||||||
|
if (!messageText) return;
|
||||||
|
|
||||||
|
// Benutzer-Nachricht anzeigen
|
||||||
|
this.addMessage('user', messageText);
|
||||||
|
inputField.value = '';
|
||||||
|
|
||||||
|
// Lade-Indikator anzeigen
|
||||||
|
this.addMessage('assistant', '...', 'loading-message');
|
||||||
|
|
||||||
|
try {
|
||||||
|
// API-Anfrage senden
|
||||||
|
const response = await fetch('/api/assistant', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
messages: this.messages.map(msg => ({
|
||||||
|
role: msg.role,
|
||||||
|
content: msg.content
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
// Lade-Nachricht entfernen
|
||||||
|
const loadingMessage = document.getElementById('loading-message');
|
||||||
|
if (loadingMessage) {
|
||||||
|
loadingMessage.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.error) {
|
||||||
|
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten: ' + data.error);
|
||||||
|
} else {
|
||||||
|
this.addMessage('assistant', data.response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler bei der API-Anfrage:', error);
|
||||||
|
|
||||||
|
// Lade-Nachricht entfernen
|
||||||
|
const loadingMessage = document.getElementById('loading-message');
|
||||||
|
if (loadingMessage) {
|
||||||
|
loadingMessage.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addMessage(role, content, id = null) {
|
||||||
|
const messagesContainer = document.getElementById('chat-messages');
|
||||||
|
|
||||||
|
// Nachricht zum Array hinzufügen (außer Lade-Nachrichten)
|
||||||
|
if (id !== 'loading-message') {
|
||||||
|
this.messages.push({ role, content });
|
||||||
}
|
}
|
||||||
|
|
||||||
this.addMessage('assistant', 'Entschuldigung, es ist ein Fehler aufgetreten. Bitte versuche es später erneut.');
|
// Nachricht zum DOM hinzufügen
|
||||||
|
const messageElement = document.createElement('div');
|
||||||
|
messageElement.className = `p-3 rounded-lg ${role === 'user' ? 'bg-primary-100 dark:bg-primary-900/30 ml-6' : 'bg-gray-100 dark:bg-gray-700 mr-6'}`;
|
||||||
|
if (id) {
|
||||||
|
messageElement.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageElement.innerHTML = `
|
||||||
|
<div class="flex items-start">
|
||||||
|
<div class="w-8 h-8 rounded-full flex items-center justify-center ${role === 'user' ? 'bg-primary-600' : 'bg-gray-600'} text-white mr-2">
|
||||||
|
<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-xs"></i>
|
||||||
|
</div>
|
||||||
|
<div class="flex-1 text-sm ${role === 'user' ? 'text-gray-800 dark:text-gray-200' : 'text-gray-700 dark:text-gray-300'}">
|
||||||
|
${content}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
messagesContainer.appendChild(messageElement);
|
||||||
|
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addMessage(role, content, id = null) {
|
// Initialisiere den ChatGPT-Assistenten direkt
|
||||||
const messagesContainer = document.getElementById('chat-messages');
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// Prüfen, ob der Assistent bereits durch MindMap initialisiert wurde
|
||||||
|
if (!window.MindMap || !window.MindMap.assistant) {
|
||||||
|
console.log('KI-Assistent wird direkt initialisiert...');
|
||||||
|
const assistant = new ChatGPTAssistant();
|
||||||
|
assistant.init();
|
||||||
|
|
||||||
// Nachricht zum Array hinzufügen (außer Lade-Nachrichten)
|
// Speichere in window.MindMap, falls es existiert, oder erstelle es
|
||||||
if (id !== 'loading-message') {
|
if (!window.MindMap) {
|
||||||
this.messages.push({ role, content });
|
window.MindMap = {};
|
||||||
|
}
|
||||||
|
window.MindMap.assistant = assistant;
|
||||||
}
|
}
|
||||||
|
});
|
||||||
// Nachricht zum DOM hinzufügen
|
|
||||||
const messageElement = document.createElement('div');
|
|
||||||
messageElement.className = `p-3 rounded-lg ${role === 'user' ? 'bg-primary-100 dark:bg-primary-900/30 ml-6' : 'bg-gray-100 dark:bg-gray-700 mr-6'}`;
|
|
||||||
if (id) {
|
|
||||||
messageElement.id = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
messageElement.innerHTML = `
|
|
||||||
<div class="flex items-start">
|
|
||||||
<div class="w-8 h-8 rounded-full flex items-center justify-center ${role === 'user' ? 'bg-primary-600' : 'bg-gray-600'} text-white mr-2">
|
|
||||||
<i class="fas ${role === 'user' ? 'fa-user' : 'fa-robot'} text-xs"></i>
|
|
||||||
</div>
|
|
||||||
<div class="flex-1 text-sm ${role === 'user' ? 'text-gray-800 dark:text-gray-200' : 'text-gray-700 dark:text-gray-300'}">
|
|
||||||
${content}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
messagesContainer.appendChild(messageElement);
|
|
||||||
messagesContainer.scrollTop = messagesContainer.scrollHeight;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisiere den ChatGPT-Assistenten direkt, um sicherzustellen,
|
|
||||||
// dass er auf jeder Seite verfügbar ist, selbst wenn MindMap nicht geladen ist
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
// Prüfen, ob der Assistent bereits durch MindMap initialisiert wurde
|
|
||||||
if (!window.MindMap || !window.MindMap.assistant) {
|
|
||||||
console.log('KI-Assistent wird direkt initialisiert...');
|
|
||||||
const assistant = new ChatGPTAssistant();
|
|
||||||
assistant.init();
|
|
||||||
|
|
||||||
// Speichere in window.MindMap, falls es existiert, oder erstelle es
|
|
||||||
if (!window.MindMap) {
|
|
||||||
window.MindMap = {};
|
|
||||||
}
|
|
||||||
window.MindMap.assistant = assistant;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Dark/Light-Mode vereinheitlicht -->
|
<!-- Dark/Light-Mode vereinheitlicht -->
|
||||||
|
|||||||
@@ -228,7 +228,5 @@
|
|||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape-cose-bilkent/4.1.0/cytoscape-cose-bilkent.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/cytoscape-cose-bilkent/4.1.0/cytoscape-cose-bilkent.min.js"></script>
|
||||||
|
|
||||||
<!-- Unsere JavaScript-Dateien -->
|
<!-- Unsere JavaScript-Dateien -->
|
||||||
<script src="{{ url_for('static', filename='js/mindmap-interaction.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='js/mindmap-init.js') }}"></script>
|
|
||||||
<script src="{{ url_for('static', filename='js/update_mindmap.js') }}"></script>
|
<script src="{{ url_for('static', filename='js/update_mindmap.js') }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
Reference in New Issue
Block a user