/** * Matrix Cycling Blog - Markdown Parser Engine * Lightweight, zero-dependency Markdown parser with custom extensions for Matrix/FreeBSD alerts and code blocks. */ window.MatrixMarkdown = (function () { function escapeHtml(text) { return text .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } function render(md) { if (!md) return ""; let html = md; // 1. Code blocks with headers ```lang:title ... ``` html = html.replace(/```([a-zA-Z0-9_-]*)(?::([^\n]+))?\n([\s\S]*?)```/g, function (match, lang, title, code) { const headerTitle = title ? title.trim() : (lang ? lang.toUpperCase() : "CODE"); const escapedCode = escapeHtml(code.trim()); return `
${escapeHtml(headerTitle)} ${escapeHtml((lang || "SHELL").toUpperCase())}
${escapedCode}
`; }); // 2. Custom Alerts: > [!TIP], > [!WARN], > [!DANGER], > [!NOTE] html = html.replace(/^>\s*\[!(TIP|WARN|WARNING|DANGER|NOTE|INFO)\](?:\s*([^\n]+))?\n((?:>.*\n?)*)/gim, function (match, type, title, body) { const alertType = type.toLowerCase() === "warning" ? "warn" : (type.toLowerCase() === "info" ? "tip" : type.toLowerCase()); const alertTitle = title ? title.trim() : ( alertType === "tip" ? "πŸ’‘ Π‘ΠΎΠ²Π΅Ρ‚ ΠΈΠ· ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠΈ:" : alertType === "warn" ? "⚠️ Π’Π½ΠΈΠΌΠ°Π½ΠΈΠ΅ / Π“Ρ€Π°Π±Π»ΠΈ:" : alertType === "danger" ? "🚨 Ошибка / ΠšΡ€ΠΈΡ‚ΠΈΡ‡Π½ΠΎ:" : "πŸ“Œ ΠŸΡ€ΠΈΠΌΠ΅Ρ‡Π°Π½ΠΈΠ΅:" ); const cleanBody = body.replace(/^>\s?/gm, "").trim(); const renderedBody = renderInline(cleanBody); return `
${escapeHtml(alertTitle)}
${renderedBody}
`; }); // 3. Blockquotes: > quote text html = html.replace(/^>\s+(.+)$/gm, function (match, text) { return `
${renderInline(text)}
`; }); // 4. Headers: #, ##, ###, #### html = html.replace(/^#### (.*?)$/gm, '

$1

'); html = html.replace(/^### (.*?)$/gm, '

$1

'); html = html.replace(/^## (.*?)$/gm, '

$1

'); html = html.replace(/^# (.*?)$/gm, '

$1

'); // 5. Images: ![alt](url) html = html.replace(/!\[(.*?)\]\((.*?)\)/g, function (match, alt, url) { return `
${escapeHtml(alt)}
`; }); // 6. Links: [text](url) html = html.replace(/\[(.*?)\]\((.*?)\)/g, '$1'); // 7. Unordered lists: - item or * item html = html.replace(/^(?:-|\*)\s+(.+)$/gm, '
  • $1
  • '); html = html.replace(/((?:
  • .*?<\/li>\s*)+)/gs, ''); // 8. Ordered lists: 1. item html = html.replace(/^\d+\.\s+(.+)$/gm, '
  • $1
  • '); html = html.replace(/((?:
  • .*?<\/li>\s*)+)/gs, '
      $1
    '); // 9. Horizontal rules: --- or *** html = html.replace(/^---|\*\*\*$/gm, '
    '); // 10. Split by double newlines for paragraphs (if not already inside custom elements) const parts = html.split(/\n\n+/); const processedParts = parts.map(part => { part = part.trim(); if (!part) return ""; if (part.startsWith("${renderInline(part.replace(/\n/g, "
    "))}

    `; }); return processedParts.filter(Boolean).join("\n\n"); } function renderInline(text) { if (!text) return ""; let inline = text; // Bold + Italic: ***text*** or ___text___ inline = inline.replace(/\*\*\*(.*?)\*\*\*/g, '$1'); // Bold: **text** inline = inline.replace(/\*\*(.*?)\*\*/g, '$1'); // Italic: *text* or _text_ inline = inline.replace(/\*([^\*]+)\*/g, '$1'); inline = inline.replace(/_([^_]+)_/g, '$1'); // Strikethrough: ~~text~~ inline = inline.replace(/~~(.*?)~~/g, '$1'); // Inline code: `code` inline = inline.replace(/`([^`]+)`/g, '$1'); return inline; } return { render: render, renderInline: renderInline }; })();