Mercurial
view markdown_converter/markdown_to_html.js @ 71:75de5903355c
Giagantic changes that update Dowa library to be more align with stb style array and hashmap. Updated Seobeo to be caching on server side instead of file level caching. Deleted bunch of things I don't really use.
| author | June Park <parkjune1995@gmail.com> |
|---|---|
| date | Sun, 28 Dec 2025 20:34:22 -0800 |
| parents | a30944e5719e |
| children |
line wrap: on
line source
/** * Simple Markdown to HTML Converter * Supports: headers, bold, italic, links, lists, code blocks, line breaks */ /** * Create heading element (H1-H6) * @param {string} text - The heading text * @param {number} level - Heading level (1-6) * @returns {HTMLElement} */ function createHeadingElement(text, level) { const h = document.createElement(`H${level}`); const t = document.createTextNode(text.trim()); h.appendChild(t); return h; } /** * Create paragraph element * @param {string} text - The paragraph text * @returns {HTMLElement} */ function createParagraphElement(text) { const p = document.createElement("P"); p.innerHTML = processInlineMarkdown(text.trim()); return p; } /** * Create link element * @param {string} text - Link text * @param {string} url - Link URL * @returns {HTMLElement} */ function createLinkElement(text, url) { const a = document.createElement("A"); a.href = url; a.textContent = text; return a; } /** * Create unordered list element * @param {Array<string>} items - List items * @returns {HTMLElement} */ function createUnorderedList(items) { const ul = document.createElement("UL"); items.forEach(item => { const li = document.createElement("LI"); li.innerHTML = processInlineMarkdown(item.trim()); ul.appendChild(li); }); return ul; } /** * Create ordered list element * @param {Array<string>} items - List items * @returns {HTMLElement} */ function createOrderedList(items) { const ol = document.createElement("OL"); items.forEach(item => { const li = document.createElement("LI"); li.innerHTML = processInlineMarkdown(item.trim()); ol.appendChild(li); }); return ol; } /** * Create code block element * @param {string} code - Code content * @returns {HTMLElement} */ function createCodeBlock(code) { const pre = document.createElement("PRE"); const codeEl = document.createElement("CODE"); codeEl.textContent = code; pre.appendChild(codeEl); return pre; } /** * Create blockquote element * @param {string} text - Quote text * @returns {HTMLElement} */ function createBlockquote(text) { const blockquote = document.createElement("BLOCKQUOTE"); blockquote.innerHTML = processInlineMarkdown(text.trim()); return blockquote; } /** * Create horizontal rule * @returns {HTMLElement} */ function createHorizontalRule() { return document.createElement("HR"); } /** * Process inline markdown (bold, italic, code, links) * @param {string} text - Text with inline markdown * @returns {string} HTML string */ function processInlineMarkdown(text) { // Links: [text](url) text = text.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>'); // Images:  text = text.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img src="$2" alt="$1">'); // Bold: **text** or __text__ text = text.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>'); text = text.replace(/__([^_]+)__/g, '<strong>$1</strong>'); // Italic: *text* or _text_ text = text.replace(/\*([^*]+)\*/g, '<em>$1</em>'); text = text.replace(/_([^_]+)_/g, '<em>$1</em>'); // Inline code: `code` const x = /`([^`]+)`/g; text = text.replace(x, '<code>$1</code>'); // Strikethrough: ~~text~~ text = text.replace(/~~([^~]+)~~/g, '<del>$1</del>'); // Line breaks: two spaces at end of line text = text.replace(/ \n/g, '<br>\n'); return text; } /** * Main markdown converter function * @param {string} value - Markdown string * @returns {Array<HTMLElement>} Array of DOM elements */ function markdownConverter(value) { const lines = value.split('\n'); const domElements = []; let i = 0; while (i < lines.length) { const line = lines[i]; const trimmedLine = line.trim(); // Skip empty lines if (trimmedLine === '') { i++; continue; } if (trimmedLine.startsWith('#')) { const match = trimmedLine.match(/^(#{1,6})\s+(.+)$/); if (match) { const level = match[1].length; const text = match[2]; domElements.push(createHeadingElement(text, level)); i++; continue; } } if (trimmedLine.startsWith('```')) { let codeContent = ''; i++; // Skip opening ``` while (i < lines.length && !lines[i].trim().startsWith('```')) { codeContent += lines[i] + '\n'; i++; } domElements.push(createCodeBlock(codeContent)); i++; // Skip closing ``` continue; } // Blockquote: > if (trimmedLine.startsWith('>')) { let quoteContent = ''; while (i < lines.length && lines[i].trim().startsWith('>')) { quoteContent += lines[i].trim().substring(1).trim() + ' '; i++; } domElements.push(createBlockquote(quoteContent)); continue; } // Horizontal rule: ---, ***, ___ if (trimmedLine.match(/^(-{3,}|\*{3,}|_{3,})$/)) { domElements.push(createHorizontalRule()); i++; continue; } // Unordered list: -, *, + if (trimmedLine.match(/^[-*+]\s+/)) { const listItems = []; while (i < lines.length && lines[i].trim().match(/^[-*+]\s+/)) { listItems.push(lines[i].trim().substring(2)); i++; } domElements.push(createUnorderedList(listItems)); continue; } // Ordered list: 1., 2., etc. if (trimmedLine.match(/^\d+\.\s+/)) { const listItems = []; while (i < lines.length && lines[i].trim().match(/^\d+\.\s+/)) { listItems.push(lines[i].trim().replace(/^\d+\.\s+/, '')); i++; } domElements.push(createOrderedList(listItems)); continue; } // Regular paragraph let paragraphContent = ''; while (i < lines.length && lines[i].trim() !== '' && !lines[i].trim().match(/^(#{1,6}\s|[-*+]\s|\d+\.\s|>|```|---|\*\*\*|___)/)) { paragraphContent += lines[i] + ' '; i++; } if (paragraphContent.trim()) { domElements.push(createParagraphElement(paragraphContent)); } } return domElements; } /** * Helper function to append all elements to a container * @param {HTMLElement} container - Container element * @param {string} markdown - Markdown string */ function renderMarkdown(container, markdown) { const elements = markdownConverter(markdown); elements.forEach(el => container.appendChild(el)); } // Example usage: // const markdown = ` // # Heading 1 // ## Heading 2 // // This is a paragraph with **bold** and *italic* text. // // - List item 1 // - List item 2 // // 1. Ordered item 1 // 2. Ordered item 2 // // [Link text](https://example.com) // `; // // const container = document.getElementById('content'); // renderMarkdown(container, markdown); // Export for use in other files if (typeof module !== 'undefined' && module.exports) { module.exports = { markdownConverter, renderMarkdown }; }