ES
apps/jumphp-ide-remote / ui / vendor / ide-md.js · commit b81e652
// Tiny DOM helper + the IDE's safe Markdown renderer (adapted from ide/ui/js/markdown.js): DOM nodes only, never innerHTML.// Classic script: window.IdeMd = {el, clear, renderMarkdown}; also CommonJS-exportable for node tests.(function (root, factory) {  const api = factory();  if (typeof module === 'object' && module.exports) module.exports = api; else root.IdeMd = api;})(typeof window !== 'undefined' ? window : globalThis, function () {  /** el('div.a.b', {attrs, onclick}, ...children) */  function el(spec, attrs, ...kids) {    const m = /^([a-z0-9]*)((?:\.[\w-]+)*)$/i.exec(spec);    const node = document.createElement((m && m[1]) || 'div');    if (m && m[2]) node.className = m[2].slice(1).replace(/\./g, ' ');    for (const [k, v] of Object.entries(attrs || {})) {      if (v === undefined || v === null || v === false) continue;      if (k.startsWith('on') && typeof v === 'function') node.addEventListener(k.slice(2), v);      else if (k === 'text') node.textContent = v;      else node.setAttribute(k, v === true ? '' : v);    }    for (const kid of kids.flat()) if (kid !== null && kid !== undefined && kid !== false) node.append(kid);    return node;  }  const clear = (n) => { while (n.firstChild) n.removeChild(n.firstChild); return n; };  const INLINE = /(`[^`]+`|\*\*[^*]+\*\*|\*[^*\s][^*]*\*|\[[^\]]+\]\([^)\s]+\))/g;  function inline(text) {    const out = [];    for (const [n, chunk] of text.split(INLINE).entries()) {      if (n % 2 === 0) { if (chunk) out.push(chunk); continue; }      if (chunk.startsWith('`')) out.push(el('code', {}, chunk.slice(1, -1)));      else if (chunk.startsWith('**')) out.push(el('b', {}, chunk.slice(2, -2)));      else if (chunk.startsWith('*')) out.push(el('i', {}, chunk.slice(1, -1)));      else {        const m = /^\[([^\]]+)\]\(([^)\s]+)\)$/.exec(chunk);        if (m && /^https?:\/\//.test(m[2])) out.push(el('a', { href: m[2], target: '_blank', rel: 'noopener noreferrer' }, m[1]));        else out.push(m ? m[1] : chunk);      }    }    return out;  }  function renderMarkdown(text) {    const nodes = [];    const lines = String(text || '').replace(/\r\n?/g, '\n').split('\n');    const LIST = /^\s*([-*]|\d+\.)\s+/;    let i = 0;    while (i < lines.length) {      const line = lines[i];      if (/^```/.test(line)) {        const code = [];        for (i++; i < lines.length && !/^```/.test(lines[i]); i++) code.push(lines[i]);        i++;        nodes.push(el('pre.code', {}, code.join('\n')));      } else if (/^#{1,4}\s/.test(line)) {        const level = /^#+/.exec(line)[0].length;        nodes.push(el(`h${Math.min(5, level + 1)}`, {}, ...inline(line.replace(/^#+\s*/, ''))));        i++;      } else if (/^\s*\|.*\|\s*$/.test(line) && /^\s*\|[\s:|-]+\|\s*$/.test(lines[i + 1] || '')) {        const cells = (row) => row.trim().replace(/^\||\|$/g, '').split('|').map((c) => c.trim());        const head = cells(line);        const rows = [];        for (i += 2; i < lines.length && /^\s*\|.*\|\s*$/.test(lines[i]); i++) rows.push(cells(lines[i]));        nodes.push(el('div.md-table-wrap', {}, el('table.md-table', {}, el('thead', {}, el('tr', {}, ...head.map((c) => el('th', {}, ...inline(c))))),          el('tbody', {}, ...rows.map((r) => el('tr', {}, ...r.map((c) => el('td', {}, ...inline(c)))))))));      } else if (LIST.test(line)) {        const ordered = /^\s*\d+\./.test(line);        const items = [];        for (; i < lines.length && LIST.test(lines[i]); i++) {          let item = lines[i].replace(LIST, '');          while (i + 1 < lines.length && /^\s{2,}\S/.test(lines[i + 1]) && !LIST.test(lines[i + 1])) item += ' ' + lines[++i].trim();          items.push(el('li', {}, ...inline(item)));        }        nodes.push(el(ordered ? 'ol' : 'ul', {}, ...items));      } else if (/^\s*([-*_])\s*\1\s*\1[-*_\s]*$/.test(line)) {        nodes.push(el('hr')); i++;      } else if (/^>\s?/.test(line)) {        const quote = [];        for (; i < lines.length && /^>\s?/.test(lines[i]); i++) quote.push(lines[i].replace(/^>\s?/, ''));        nodes.push(el('blockquote', {}, ...renderMarkdown(quote.join('\n'))));      } else if (!line.trim()) {        i++;      } else {        const para = [];        for (; i < lines.length && lines[i].trim() && !/^(```|>|#{1,4}\s|\s*([-*]|\d+\.)\s+|\s*\|)/.test(lines[i]); i++) para.push(lines[i].trim());        nodes.push(el('p', {}, ...inline(para.join(' '))));      }    }    return nodes;  }  return { el, clear, renderMarkdown };});