/**
* 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 `
`;
});
// 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: 
html = html.replace(/!\[(.*?)\]\((.*?)\)/g, function (match, alt, url) {
return ``;
});
// 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
};
})();