// VBB Netz Status - Changelog-Loader + Markdown-Parser // ========================================== // UPDATES VIEW - CHANGELOG LOADER // ========================================== // ========================================== // CHANGELOG LOADER - VERBESSERT // Fetcht CHANGELOG.md und konvertiert Markdown zu HTML // ========================================== let changelogState = 'idle'; // 'idle' | 'loading' | 'loaded' async function loadChangelog() { // Guard: verhindert parallele/mehrfache Ladungen if (changelogState !== 'idle') return; changelogState = 'loading'; const container = document.getElementById('changelogContent'); try { // Lade CHANGELOG.md const response = await fetch('CHANGELOG.md'); if (!response.ok) throw new Error('CHANGELOG.md nicht gefunden'); const markdown = await response.text(); // Konvertiere Markdown zu HTML const html = markdownToHtml(markdown); // Wrapper mit changelog-container Klasse container.innerHTML = `
${html}
`; changelogState = 'loaded'; } catch (error) { console.error('Changelog Fehler:', error); changelogState = 'idle'; // erneuter Versuch möglich container.innerHTML = `

⚠️ Changelog konnte nicht geladen werden

${escapeHtml(error.message)}

`; } } // ========================================== // MARKDOWN TO HTML CONVERTER // ========================================== function markdownToHtml(markdown) { let html = ''; const lines = markdown.split('\n'); let inList = false; let inOrderedList = false; let inCodeBlock = false; let codeBlockContent = ''; for (let i = 0; i < lines.length; i++) { let line = lines[i]; // Code-Block Start/Ende (```) if (line.trim().startsWith('```')) { if (!inCodeBlock) { inCodeBlock = true; codeBlockContent = ''; continue; } else { inCodeBlock = false; html += `
${escapeHtml(codeBlockContent)}
`; codeBlockContent = ''; continue; } } // Innerhalb Code-Block if (inCodeBlock) { codeBlockContent += line + '\n'; continue; } // Leere Zeile - Schließe Listen if (line.trim() === '') { if (inList) { html += ''; inList = false; } if (inOrderedList) { html += ''; inOrderedList = false; } continue; } // Überschriften (# ## ### ####) if (line.match(/^(#{1,6})\s+(.+)$/)) { const match = line.match(/^(#{1,6})\s+(.+)$/); const level = match[1].length; const text = parseInline(match[2]); if (inList) { html += ''; inList = false; } if (inOrderedList) { html += ''; inOrderedList = false; } html += `${text}`; continue; } // Horizontale Linie (--- oder ***) if (line.match(/^(---+|\*\*\*+)$/)) { if (inList) { html += ''; inList = false; } if (inOrderedList) { html += ''; inOrderedList = false; } html += '
'; continue; } // Ungeordnete Liste (- oder *) if (line.match(/^[-*]\s+(.+)$/)) { const match = line.match(/^[-*]\s+(.+)$/); const text = parseInline(match[1]); if (inOrderedList) { html += ''; inOrderedList = false; } if (!inList) { html += ''; inList = false; } if (!inOrderedList) { html += '
    '; inOrderedList = true; } html += `
  1. ${text}
  2. `; continue; } // Blockquote (>) if (line.match(/^>\s+(.+)$/)) { const match = line.match(/^>\s+(.+)$/); const text = parseInline(match[1]); if (inList) { html += ''; inList = false; } if (inOrderedList) { html += '
'; inOrderedList = false; } html += `
${text}
`; continue; } // Normaler Paragraph if (line.trim() !== '') { if (inList) { html += ''; inList = false; } if (inOrderedList) { html += ''; inOrderedList = false; } const text = parseInline(line); html += `

${text}

`; } } // Schließe offene Listen am Ende if (inList) html += ''; if (inOrderedList) html += ''; return html; } // ========================================== // INLINE MARKDOWN PARSER // ========================================== function parseInline(text) { // Links [text](url) text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); // Inline Code `code` text = text.replace(/`([^`]+)`/g, '$1'); // Bold **text** oder __text__ text = text.replace(/\*\*([^*]+)\*\*/g, '$1'); text = text.replace(/__([^_]+)__/g, '$1'); // Italic *text* oder _text_ (aber nicht ** oder __) text = text.replace(/(?$1'); text = text.replace(/(?$1'); // Strikethrough ~~text~~ text = text.replace(/~~([^~]+)~~/g, '$1'); return text; } // HINWEIS: Die frühere zweite escapeHtml-Definition wurde entfernt. // Sie überschrieb die zentrale Version und escapte keine Anführungszeichen // (gefährlich in HTML-Attributen). Ebenso entfernt: der MutationObserver // für den Changelog - switchView() und der Dev-Tab-Handler laden ihn bereits, // der Observer führte zu mehrfachen parallelen Fetches.