(function () { const tokenPattern = /%%MDTOKEN(\d+)%%/g; function escapeHtml(value) { return String(value || "").replace(/[&<>"']/g, (char) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'", })[char]); } function sanitizeUrl(value) { const url = String(value || "").trim().replace(/[\u0000-\u001f\u007f\s]+/g, ""); if (/^(https?:\/\/|\/|\.\/|\.\.\/|uploads\/|mailto:|#)/i.test(url)) { return escapeHtml(url); } return "#"; } function storeToken(tokens, html) { const token = `%%MDTOKEN${tokens.length}%%`; tokens.push(html); return token; } function renderInline(markdown) { const tokens = []; let text = String(markdown || ""); text = text.replace(/`([^`]+)`/g, (_, code) => storeToken(tokens, `${escapeHtml(code)}`), ); text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, (_, alt, url) => storeToken( tokens, `${escapeHtml(alt)}`, ), ); text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, url) => storeToken( tokens, `${escapeHtml(label)}`, ), ); text = escapeHtml(text) .replace(/\*\*([^*]+)\*\*/g, "$1") .replace(/__([^_]+)__/g, "$1") .replace(/\*([^*]+)\*/g, "$1") .replace(/_([^_]+)_/g, "$1"); return text.replace(tokenPattern, (_, index) => tokens[Number(index)] || ""); } function renderList(lines) { const items = lines .map((line) => line.replace(/^\s*[-*]\s+/, "")) .map((line) => `
  • ${renderInline(line)}
  • `) .join(""); return ``; } function renderMarkdown(markdown) { const lines = String(markdown || "").replace(/\r\n/g, "\n").split("\n"); const blocks = []; let paragraph = []; let list = []; let codeBlock = null; function flushParagraph() { if (!paragraph.length) return; blocks.push(`

    ${renderInline(paragraph.join(" "))}

    `); paragraph = []; } function flushList() { if (!list.length) return; blocks.push(renderList(list)); list = []; } lines.forEach((line) => { if (line.trim().startsWith("```")) { if (codeBlock) { blocks.push(`
    ${escapeHtml(codeBlock.join("\n"))}
    `); codeBlock = null; } else { flushParagraph(); flushList(); codeBlock = []; } return; } if (codeBlock) { codeBlock.push(line); return; } if (!line.trim()) { flushParagraph(); flushList(); return; } const heading = line.match(/^(#{1,3})\s+(.+)$/); if (heading) { flushParagraph(); flushList(); const level = heading[1].length + 2; blocks.push(`${renderInline(heading[2])}`); return; } if (/^\s*[-*]\s+/.test(line)) { flushParagraph(); list.push(line); return; } flushList(); paragraph.push(line.trim()); }); flushParagraph(); flushList(); if (codeBlock) { blocks.push(`
    ${escapeHtml(codeBlock.join("\n"))}
    `); } return blocks.join(""); } window.renderMarkdown = renderMarkdown; })();