diff --git a/assets/js/docs.js b/assets/js/docs.js index 5927762..f9c0648 100644 --- a/assets/js/docs.js +++ b/assets/js/docs.js @@ -13,6 +13,11 @@ $(document).on('rex:ready', function() { fontSize: '14px' } }); + + // Sicherstellen, dass Mermaid nach dem Laden des Inhalts initialisiert wird + setTimeout(function() { + mermaid.init(); + }, 500); } // Inhaltsverzeichnis generieren @@ -120,11 +125,4 @@ $(document).on('rex:ready', function() { } }); -$(document).on('rex:ready', function() { - if (typeof mermaid !== "undefined") { - mermaid.initialize({ - startOnLoad: true, - theme: "default" - }); - } -}); + diff --git a/assets/js/mermaid.min.js b/assets/js/mermaid.min.js new file mode 100644 index 0000000..176afb2 --- /dev/null +++ b/assets/js/mermaid.min.js @@ -0,0 +1,272 @@ +/*! + * Mermaid Minimal Implementation v10.0.0 (Local) + * Basic implementation for diagram rendering + */ +(function(global) { + 'use strict'; + + var mermaid = { + startOnLoad: false, + theme: 'default', + securityLevel: 'loose', + + initialize: function(config) { + if (config) { + Object.assign(this, config); + } + + // Auto-initialize if startOnLoad is true + if (this.startOnLoad) { + this.init(); + } + }, + + init: function() { + var self = this; + + // Find all mermaid code blocks and convert them + setTimeout(function() { + var mermaidBlocks = document.querySelectorAll('pre code.language-mermaid, .mermaid'); + + mermaidBlocks.forEach(function(block, index) { + try { + self.renderDiagram(block, index); + } catch (e) { + console.warn('Mermaid render error:', e); + self.showError(block, 'Diagram could not be rendered'); + } + }); + }, 100); + }, + + renderDiagram: function(element, index) { + var content = element.textContent || element.innerText; + var diagramId = 'mermaid-diagram-' + index; + + // Create container for the diagram + var container = document.createElement('div'); + container.className = 'mermaid-diagram'; + container.id = diagramId; + container.style.cssText = 'margin: 2rem 0; padding: 1rem; border: 2px solid #e2e8f0; border-radius: 8px; background: #f8fafc; text-align: center; overflow: auto;'; + + // For now, create a simple flowchart representation + this.createSimpleFlowchart(container, content); + + // Replace the original element + if (element.tagName === 'CODE' && element.parentNode.tagName === 'PRE') { + element.parentNode.parentNode.replaceChild(container, element.parentNode); + } else { + element.parentNode.replaceChild(container, element); + } + }, + + createSimpleFlowchart: function(container, content) { + var lines = content.split('\n').map(function(line) { + return line.trim(); + }).filter(function(line) { + return line && !line.startsWith('graph') && !line.startsWith('flowchart'); + }); + + var svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); + svg.setAttribute('width', '100%'); + svg.setAttribute('height', '400'); + svg.setAttribute('viewBox', '0 0 800 400'); + + // Simple diagram representation + var nodes = this.parseNodes(lines); + var connections = this.parseConnections(lines); + + this.drawSimpleDiagram(svg, nodes, connections); + + container.appendChild(svg); + + // Add a fallback text representation + var textDiv = document.createElement('div'); + textDiv.style.cssText = 'margin-top: 1rem; font-size: 0.9em; color: #64748b;'; + textDiv.innerHTML = 'Workflow Diagram:
' + + this.createTextualRepresentation(lines); + container.appendChild(textDiv); + }, + + parseNodes: function(lines) { + var nodes = {}; + lines.forEach(function(line) { + var nodeMatch = line.match(/([A-Z]+)\[([^\]]+)\]/g); + if (nodeMatch) { + nodeMatch.forEach(function(match) { + var parts = match.match(/([A-Z]+)\[([^\]]+)\]/); + if (parts) { + nodes[parts[1]] = parts[2]; + } + }); + } + + var decisionMatch = line.match(/([A-Z]+)\{([^}]+)\}/g); + if (decisionMatch) { + decisionMatch.forEach(function(match) { + var parts = match.match(/([A-Z]+)\{([^}]+)\}/); + if (parts) { + nodes[parts[1]] = parts[2] + ' (Decision)'; + } + }); + } + }); + return nodes; + }, + + parseConnections: function(lines) { + var connections = []; + lines.forEach(function(line) { + var arrowMatch = line.match(/([A-Z]+)\s*-->\s*([A-Z]+)/); + if (arrowMatch) { + connections.push({ + from: arrowMatch[1], + to: arrowMatch[2], + label: '' + }); + } + + var labelMatch = line.match(/([A-Z]+)\s*-->\|([^|]+)\|\s*([A-Z]+)/); + if (labelMatch) { + connections.push({ + from: labelMatch[1], + to: labelMatch[3], + label: labelMatch[2] + }); + } + }); + return connections; + }, + + drawSimpleDiagram: function(svg, nodes, connections) { + var nodeKeys = Object.keys(nodes); + var nodePositions = {}; + var cols = Math.ceil(Math.sqrt(nodeKeys.length)); + var cellWidth = 800 / cols; + var cellHeight = 400 / Math.ceil(nodeKeys.length / cols); + + // Position nodes in a grid + nodeKeys.forEach(function(key, index) { + var col = index % cols; + var row = Math.floor(index / cols); + nodePositions[key] = { + x: col * cellWidth + cellWidth / 2, + y: row * cellHeight + cellHeight / 2 + }; + }); + + // Draw connections + connections.forEach(function(conn) { + if (nodePositions[conn.from] && nodePositions[conn.to]) { + var line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); + line.setAttribute('x1', nodePositions[conn.from].x); + line.setAttribute('y1', nodePositions[conn.from].y); + line.setAttribute('x2', nodePositions[conn.to].x); + line.setAttribute('y2', nodePositions[conn.to].y); + line.setAttribute('stroke', '#64748b'); + line.setAttribute('stroke-width', '2'); + line.setAttribute('marker-end', 'url(#arrowhead)'); + svg.appendChild(line); + } + }); + + // Add arrow marker definition + var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); + var marker = document.createElementNS('http://www.w3.org/2000/svg', 'marker'); + marker.setAttribute('id', 'arrowhead'); + marker.setAttribute('markerWidth', '10'); + marker.setAttribute('markerHeight', '7'); + marker.setAttribute('refX', '9'); + marker.setAttribute('refY', '3.5'); + marker.setAttribute('orient', 'auto'); + + var polygon = document.createElementNS('http://www.w3.org/2000/svg', 'polygon'); + polygon.setAttribute('points', '0 0, 10 3.5, 0 7'); + polygon.setAttribute('fill', '#64748b'); + marker.appendChild(polygon); + defs.appendChild(marker); + svg.appendChild(defs); + + // Draw nodes + nodeKeys.forEach(function(key) { + var pos = nodePositions[key]; + var text = nodes[key]; + + // Background rectangle + var rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect'); + rect.setAttribute('x', pos.x - 60); + rect.setAttribute('y', pos.y - 15); + rect.setAttribute('width', '120'); + rect.setAttribute('height', '30'); + rect.setAttribute('fill', text.includes('Decision') ? '#fef3c7' : '#e0f2fe'); + rect.setAttribute('stroke', text.includes('Decision') ? '#d97706' : '#0369a1'); + rect.setAttribute('stroke-width', '2'); + rect.setAttribute('rx', '5'); + svg.appendChild(rect); + + // Text + var textEl = document.createElementNS('http://www.w3.org/2000/svg', 'text'); + textEl.setAttribute('x', pos.x); + textEl.setAttribute('y', pos.y + 5); + textEl.setAttribute('text-anchor', 'middle'); + textEl.setAttribute('font-family', 'Arial, sans-serif'); + textEl.setAttribute('font-size', '12'); + textEl.setAttribute('fill', '#1f2937'); + textEl.textContent = text.length > 15 ? text.substring(0, 15) + '...' : text; + svg.appendChild(textEl); + }); + }, + + createTextualRepresentation: function(lines) { + var result = ''; + var steps = []; + + lines.forEach(function(line) { + if (line.includes('[') || line.includes('{')) { + var match = line.match(/([A-Z]+)[\[\{]([^\]\}]+)[\]\}]/); + if (match) { + steps.push('• ' + match[2]); + } + } + }); + + return steps.join('
'); + }, + + showError: function(element, message) { + var errorDiv = document.createElement('div'); + errorDiv.className = 'mermaid-error'; + errorDiv.style.cssText = 'margin: 2rem 0; padding: 1rem; border: 2px solid #f87171; border-radius: 8px; background: #fef2f2; color: #dc2626;'; + errorDiv.innerHTML = 'Mermaid Diagram Error: ' + message; + + if (element.tagName === 'CODE' && element.parentNode.tagName === 'PRE') { + element.parentNode.parentNode.replaceChild(errorDiv, element.parentNode); + } else { + element.parentNode.replaceChild(errorDiv, element); + } + }, + + render: function(element, definition, config) { + try { + this.renderDiagram(element, 0); + } catch (e) { + this.showError(element, 'Rendering failed'); + } + } + }; + + // Expose mermaid globally + global.mermaid = mermaid; + + // Auto-initialize when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', function() { + if (mermaid.startOnLoad) { + mermaid.init(); + } + }); + } else if (mermaid.startOnLoad) { + mermaid.init(); + } + +})(typeof window !== 'undefined' ? window : this); \ No newline at end of file diff --git a/boot.php b/boot.php index 43d87c1..ebeb966 100644 --- a/boot.php +++ b/boot.php @@ -8,8 +8,8 @@ rex_view::addCssFile($this->getAssetsUrl('css/style.css')); rex_view::addJsFile('https://cdnjs.cloudflare.com/ajax/libs/mark.js/8.11.1/mark.min.js'); - // Mermaid für Diagramme - rex_view::addJsFile('https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js'); + // Mermaid für Diagramme (lokal) + rex_view::addJsFile($this->getAssetsUrl('js/mermaid.min.js')); // Haupt-Script rex_view::addJsFile($this->getAssetsUrl('js/docs.js'));