UNPKG

foremark

Version:

A technology for writing semi-plain text documents that extends upon the concept of Markdeep.

644 lines 29.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); var dom_1 = require("../utils/dom"); var string_1 = require("../utils/string"); var table_1 = require("./table"); var blocks_1 = require("./blocks"); var common_1 = require("./common"); var ARROWS = [ ['&lt;==', '⇐'], ['-&gt;', '→'], ['--&gt;', '⟶'], ['==&gt;', '⇒'], ['&lt;-', '←'], ['&lt;--', '⟵'], ['&lt;==&gt;', '⇔'], ['&lt;-&gt;', '↔'], ]; var ARROWS_MAP = new Map(ARROWS); var ARROWS_REGEX = new RegExp('(\\s|^)(' + ARROWS.map(function (e) { return e[0]; }).join('|') + ')(?=\\s)', 'g'); var FENCE_REGEX = /^((?:[ \t]|&gt;)*)(~{3,}|`{3,})\s*([-_0-9a-zA-Z+/\s]*)$/; var DIAGRAM_REGEX = /^((?:[ \t]|&gt;)*)::(.*)$/; var PHRASING_ELEMENTS = [ "mf-ph" /* Placeholder */, "mf-ref" /* Ref */, "mf-eq" /* Equation */, 'addr', 'audio', 'b', 'bdo', 'br', 'button', 'canvas', 'cite', 'code', 'command', 'data', 'datalist', 'dfn', 'em', 'embed', 'i', 'iframe', 'img', 'input', 'kbd', 'keygen', 'label', 'mark', 'math', 'meter', 'noscript', 'object', 'output', 'progress', 'q', 'ruby', 'samp', 'script', 'select', 'small', 'span', 'strong', 'sub', 'sup', 'svg', 'textarea', 'time', 'var', 'video', 'wbr', // The following elements are conditional, but we assume they belong to // this category 'a', 'del', 'ins', ]; var PHRASING_ELEMENTS_MAP = new Map(PHRASING_ELEMENTS.map(function (n) { return [n, true]; })); var VERBATIM_ELEMENTS = [ 'code', "mf-code" /* Code */, 'svg', "mf-eq" /* Equation */, "mf-eq-display" /* DisplayEquation */, "mf-link-target" /* LinkTarget */, "mf-diagram" /* Diagram */, ]; var VERBATIM_ELEMENTS_MAP = new Map(VERBATIM_ELEMENTS.map(function (n) { return [n, true]; })); /** * Expands all `<mf-text>` in a given DOM node. */ function expandMfText(node) { if (node.tagName.toLowerCase() !== "mf-text" /* Text */) { for (var n = node.firstChild; n; n = n.nextSibling) { if (n instanceof dom_1.DomTypes.Element) { expandMfText(n); } } return; } // Merge consecutive text nodes // // Without this step, constructs spanning across multiple text nodes are // not recognized properly. For example, "`]]><![CDATA[]>][`" inside CDATA // (which is intended to display a code fragment "]]>") splits the current // CDATA section into two: one ending with "`]" and one starting with // "]>`". `transformTextNodeWith` calls a supplied function in // per text node basis, so this example won't be recognized as a hyperlink // without this step. dom_1.forEachNodePreorder(node, function (node) { if (!(node instanceof dom_1.DomTypes.Text) || node.nextSibling instanceof dom_1.DomTypes.Text) { return; } // This is the last one of one or more consecutive text nodes. if (!(node.previousSibling instanceof dom_1.DomTypes.Text)) { // One consecutive text node. return; } // Merge all and remove all but the last one. var parts = [node.textContent]; var parent = node.parentElement; for (var n = node.previousSibling; n instanceof dom_1.DomTypes.Text;) { var next = n.previousSibling; parts.unshift(n.textContent); parent.removeChild(n); n = next; } node.textContent = parts.join(''); }); // Fenced code blocks // // This step preserves the indentation of code blocks. For example: // // > ~~~~~~~~~~~~~~~~ // > code... // > ~~~~~~~~~~~~~~~~ // // will be: // // > <mf-code-block><mf-code>code...</mf-code></mf-code-block> // dom_1.transformHtmlWith(node, function (html) { var lines = html.split('\n'); var output = []; var inCodeBlock = false; var currentCodeBlockIndentation = ''; var currentCodeBlockFence = ''; for (var _i = 0, lines_1 = lines; _i < lines_1.length; _i++) { var line = lines_1[_i]; var matches = line.match(FENCE_REGEX); if (matches) { matches[2] = matches[2].substr(0, 1); matches[3] = matches[3].trim(); } if (inCodeBlock) { if (matches && matches[2] === currentCodeBlockFence && matches[1] === currentCodeBlockIndentation) { if (output[output.length - 1] === '\n') { output.pop(); // Remove trailing newline } if (matches[3] === '') { // End of code blocks output.push("</" + "mf-code" /* Code */ + "></" + "mf-codeblock" /* CodeBlock */ + ">\n"); inCodeBlock = false; } else { // Language switch output.push("</" + "mf-code" /* Code */ + "><" + "mf-code" /* Code */ + " type=\"" + matches[3] + "\">"); output.push('\n'); currentCodeBlockIndentation = matches[1]; } } else { output.push(string_1.removePrefix(line, currentCodeBlockIndentation)); output.push('\n'); } } else { if (matches) { output.push(matches[1]); // Preserve indentation output.push("<" + "mf-codeblock" /* CodeBlock */ + ">"); output.push("<" + "mf-code" /* Code */ + " type=\"" + matches[3] + "\">"); inCodeBlock = true; currentCodeBlockFence = matches[2]; currentCodeBlockIndentation = matches[1]; } else { output.push(line); output.push('\n'); } } } if (inCodeBlock) { if (output[output.length - 1] === '\n') { output.pop(); // Remove trailing newline } output.push("</" + "mf-code" /* Code */ + "></" + "mf-codeblock" /* CodeBlock */ + ">"); output.push("<" + "mf-error" /* Error */ + ">Unclosed fenced code block</" + "mf-error" /* Error */ + ">"); } else { output.pop(); // Remove trailing newline } return output.join(''); }); // Diagrams // // This step preserves the indentation of code blocks. For example, // the both of: (1) (the original style) // // > ::: code... // > ::: code... // // and (2) (the style newly introduced in 0.1.2) // // > ::::::::::::: // > :: code... :: // > :: code... :: // > ::::::::::::: // // will be: // // > <mf-diagram>...</mf-diagram> // dom_1.transformHtmlWith(node, function (html) { var lines = html.split('\n'); var output = []; var inDiagram = false; var diagramStart = -1; var currentDiagramIndentation = ''; function closeDiagram() { // Remove borders if any var HORZ_BORDER_RE = /^:{4,}\s*$/; if (HORZ_BORDER_RE.test(output[output.length - 2])) { output.length -= 2; } if (diagramStart < output.length && HORZ_BORDER_RE.test(output[diagramStart])) { output.splice(diagramStart, 2); } // `hasLeftBorder` handles the first example's style. (Note that // `DIAGRAM_REGEX` only removes the two leftmost colons.) // `hasRightBorder` handles the second example's style. var hasLeftBorder = true, hasRightBorder = true; for (var i = diagramStart; i < output.length; i += 2) { hasLeftBorder = hasLeftBorder && output[i].startsWith(':'); hasRightBorder = hasRightBorder && output[i].endsWith('::'); } for (var i = diagramStart; i < output.length; i += 2) { var s = output[i]; output[i] = s.substring(hasLeftBorder ? 1 : 0, hasRightBorder ? s.length - 2 : s.length); } if (output[output.length - 1] === '\n') { output.pop(); // Remove trailing newline } output.push("</" + "mf-diagram" /* Diagram */ + ">\n"); inDiagram = false; } for (var _i = 0, lines_2 = lines; _i < lines_2.length; _i++) { var line = lines_2[_i]; var matches = line.match(DIAGRAM_REGEX); if (inDiagram) { if (!matches || matches[1] !== currentDiagramIndentation) { closeDiagram(); } else { output.push(matches[2]); output.push('\n'); } } if (!inDiagram) { if (matches) { output.push(matches[1]); // Preserve indentation output.push("<" + "mf-diagram" /* Diagram */ + ">"); diagramStart = output.length; output.push(matches[2]); output.push('\n'); inDiagram = true; currentDiagramIndentation = matches[1]; } else { output.push(line); output.push('\n'); } } } if (inDiagram) { closeDiagram(); } else { output.pop(); // Remove trailing newline } return output.join(''); }); // Block quotations var BLOCKQUOTE_LINE_RE = /&gt;.*(?:\n(?!&gt;).*\S+.*$)*/.source; var BLOCKQUOTE_RE = new RegExp("^" + BLOCKQUOTE_LINE_RE + "(\n" + BLOCKQUOTE_LINE_RE + ")*", 'gm'); var replaceSingleLevelBlockquote = function (html) { return ('\n' + html) // Replace the top-level blockquotes in this level .replace(BLOCKQUOTE_RE, function (bq) { return '<blockquote>' + replaceSingleLevelBlockquote(bq.replace(/^[ \t]*&gt; ?/gm, '')) + '</blockquote>'; }).substr(1); }; dom_1.transformHtmlWith(node, replaceSingleLevelBlockquote); var isBlockquote = function (e) { return e.tagName === 'blockquote'; }; // Inline code dom_1.transformTextNodeWith(node, function (html) { return html.replace(/`(.+?(?:\n.+?)?)`(?!\d)/g, '<code>$1</code>'); }, function (e) { return e === node || isBlockquote(e); }, false); // Wrapped URLs var WRAPPED_URL_RE = new RegExp( // url "&lt;([a-z]+://(?:[^&<>]|&(?!gt;|lt;))+)&gt;|" + // mail "&lt;((?:[^&<>@]|&(?!gt;|lt;))+@(?:[^&<>@]|&(?!gt;))+)&gt;", 'g'); dom_1.transformTextNodeWith(node, function (html) { return html.replace(WRAPPED_URL_RE, function (_, url, mail) { var text = dom_1.unescapeXmlText(url || mail); var href = (mail && 'mailto:' + dom_1.unescapeXmlText(mail)) || text; return "<a href=\"" + dom_1.escapeXmlText(href) + "\"><code>" + dom_1.escapeXmlText(text) + "</code></a>"; }); }, function (e) { return e === node || isBlockquote(e); }, false); // Parse HTML comments dom_1.transformHtmlWith(node, function (html) { return html.replace(/&lt;!--(\s[\s\S]*?)--&gt;/g, function (_, inner) { return "<!--" + inner.replace(/--/g, '&#45;&#45;') + "-->"; }); }, function (e) { return e === node || isBlockquote(e); }); // Replace well-formed HTML tags // The regex performs all validation of individual tags. The code checks // if opening tags are matched by closing tags. var ident = '[a-z][-a-z0-9]*'; var permittedEntities = 'amp|quot|lt|gt'; var tagRE = new RegExp("(&lt;" + ("(?:(" + ident + ")(?:\\s+" + ident + "=\"(?:[^\"<>&]|&amp;" + permittedEntities + ";)*\")*") + // ^^^^^^^^ // $2 opening tag name "\\s*/?|" + ( // ^^ // self-closing "/(" + ident + "))&gt;)"), // ^^^^^^^^ // $3 closing tag name 'g'); dom_1.transformHtmlWith(node, function (html) { var output = []; /// A stack tracking unvalidated opening tags. var stack = []; var parts = html.split(tagRE); output.push(parts[0]); for (var i = 1; i < parts.length; i += 4) { var tag = parts[i]; var openingTagName = parts[i + 1]; var closingTagName = parts[i + 2]; var post = parts[i + 3]; if (closingTagName) { // `</a>` var i_1 = stack.length; while (i_1 > 0 && stack[i_1 - 1][0] !== closingTagName) { i_1--; } if (i_1 > 0) { // Found a matching opening tag. Materialize the opening tag, // and forget about potential opening tags (`stack[i..]`) // which turned out to have no closing tags. while (stack.length > i_1) { stack.pop(); } var e = stack.pop(); // `stack[i - 1]` output[e[1]] = dom_1.unescapeXmlText(output[e[1]]); output.push(dom_1.unescapeXmlText(tag)); } else { // A matching opening tag wasn't found. output.push(tag); } } else if (tag.endsWith('/&gt;')) { // `<a />` output.push(dom_1.unescapeXmlText(tag)); } else { // `<a>` stack.push([openingTagName, output.length]); output.push(tag); } output.push(post); } return output.join(''); }, isBlockquote); // LaTeX display equations dom_1.transformTextNodeWith(node, function (html) { return html.replace(/\$\$([^<>]*?)\$\$/g, "<" + "mf-eq-display" /* DisplayEquation */ + ">$1</" + "mf-eq-display" /* DisplayEquation */ + ">"); }, function (e) { return e === node || isBlockquote(e); }, false); dom_1.transformTextNodeWith(node, function (html) { return html.replace(/\\begin{(equation\*?|eqnarray)}[^<>]*?\\end{\1}/g, "<" + "mf-eq-display" /* DisplayEquation */ + ">$&</" + "mf-eq-display" /* DisplayEquation */ + ">"); }, function (e) { return e === node || isBlockquote(e); }, false); // LaTeX inline equations dom_1.transformTextNodeWith(node, function (html) { return html.replace(/(^|[^\w\d])\$(\S(?:[^\$]*?\S(?!US|Can))??)\$(?![\w\d])/g, "$1<" + "mf-eq" /* Equation */ + ">$2</" + "mf-eq" /* Equation */ + ">"); }, function (e) { return e === node || isBlockquote(e); }, false); dom_1.transformTextNodeWith(node, function (html) { return html.replace(/(^|[^\w\d])\$([ \t][^\$]+?[ \t])\$(?![\w\d])/g, "$1<" + "mf-eq" /* Equation */ + ">$2</" + "mf-eq" /* Equation */ + ">"); }, function (e) { return e === node || isBlockquote(e); }, false); // Headings dom_1.transformHtmlWith(node, function (html) { return html.replace(/^(.+?)\n[ \t]*={3,}[ \t]*$/gm, function (_, inner) { return "<h1>" + inner + "</h1>"; }); }, isBlockquote); dom_1.transformHtmlWith(node, function (html) { return html.replace(/^(.+?)\n[ \t]*-{3,}[ \t]*$/gm, function (_, inner) { return "<h2>" + inner + "</h2>"; }); }, isBlockquote); var _loop_1 = function (i) { // ATX-style header (`## h2`) dom_1.transformHtmlWith(node, function (html) { return html.replace(new RegExp("^[ \t]*#{" + i + "," + i + "}(?:[ \t])([^\n]+?)(?:#{" + i + "," + i + "})?[ \t]*$", 'gm'), function (_, inner) { return "<h" + i + ">" + inner + "</h" + i + ">"; }); }, isBlockquote); }; for (var i = 6; i >= 1; --i) { _loop_1(i); } // Horizontal rule: `* * *`, `- - -`, `_ _ _` dom_1.transformHtmlWith(node, function (html) { return html.replace(/^[ \t]*((\*|-|_)[ \t]*){3,}[ \t]*$/gm, '<hr />'); }, isBlockquote); // Nestable block elements // - `<ul>`, `<ol>`, `<dl>` // - `<mf-admonition>` dom_1.transformHtmlWith(node, blocks_1.replaceBlocks, isBlockquote); var isBlock = function (e) { var tagName = e.tagName; return tagName === "mf-admonition" /* Admonition */ || tagName === "mf-figure" /* Figure */ || tagName === "mf-figure-caption" /* FigureCaption */ || tagName === "mf-note" /* Note */ || tagName === "mf-cite" /* Cite */ || tagName === "mf-block" /* Block */ || tagName.match(/^(?:ul|ol|dl|li|dt|dd|blockquote)$/i) != null; }; // Tables dom_1.transformHtmlWith(node, table_1.replaceTables, isBlock); // Paragraphs dom_1.transformHtmlWith(node, function (html) { var TAG = /<([-_a-zA-Z0-9]+)\s+.*?>/g; var lines = html.split('\n'); var output = []; var inParagraph = false; for (var _i = 0, lines_3 = lines; _i < lines_3.length; _i++) { var line = lines_3[_i]; if (line.length === 0) { // Empty line - insert a paragraph break if (inParagraph) { output.push('</p>'); output.push('\n'); inParagraph = false; } } while (line.length > 0) { // Non-phrasing element terminates the current paragraph var until = -1; var restartFrom = 0; var foundVisualElement = false; TAG.lastIndex = 0; var match = void 0; while ((match = TAG.exec(line)) !== null) { until = TAG.lastIndex - match[0].length; if (until > restartFrom) { // A text node was found foundVisualElement = true; } restartFrom = TAG.lastIndex; if (!PHRASING_ELEMENTS_MAP.has(match[1].toLowerCase())) { // Non-phrasing element found - stop right here break; } if (match[1] !== "mf-ph" /* Placeholder */) { // Element possibly including a text node was found foundVisualElement = true; } } if (match === null) { if (line.length > restartFrom) { foundVisualElement = true; } until = line.length; restartFrom = -1; } // A part of the current line to be included in the current paragraph var cur = line.substr(0, until).trim(); if (cur !== '') { // Do not start a paragraph if we only encountered non-visual // nodes such as comments if (foundVisualElement && !inParagraph) { // Start a paragraph output.push('<p>'); output.push('\n'); inParagraph = true; } output.push(cur); output.push('\n'); } // Processs the rest of the line if (restartFrom >= 0) { if (inParagraph) { output.push('</p>'); output.push('\n'); inParagraph = false; } output.push(line.substring(until, restartFrom)); output.push('\n'); line = line.substr(restartFrom); } else { break; } } } if (inParagraph) { output.push('</p>'); } if (output[output.length - 1] === '\n') { output.pop(); // Remove trailing newline } return output.join(''); }, isBlock); var isNonVerbatimElement = function (e) { return !VERBATIM_ELEMENTS_MAP.has(e.tagName.toLowerCase()); }; var isNonVerbatimElementAndNotLink = function (e) { return isNonVerbatimElement(e) && e.tagName !== 'a'; }; // Reference to a figure or endnote: `[^link]` dom_1.transformHtmlWith(node, function (html) { return html.replace(new RegExp("\\[(?:" + ( // id of endnote or figure common_1.FLOATING_SIZE_RE.source + "(" + common_1.ENDNOTE_ID_RE.source + "|" + common_1.FIGURE_ID_RE.source + ")|") + ( // id of citation "#(" + common_1.CITE_ID_RE.source + ")") + ")\\]", 'g'), function (_, id1, id2) { var id = dom_1.unescapeXmlText(id1 || id2); return "<" + "mf-ref" /* Ref */ + " to=\"" + dom_1.escapeXmlText(id) + "\" />"; }); }, isNonVerbatimElement); // Hyperlink: `[text](url)` dom_1.transformHtmlWith(node, function (html) { return html.replace(/(^|[^!])\[([^\[\]]+?)\]\(("?)([^<>\s"]+?)\3(\s+[^\)]*?)?\)/g, function (match, pre, text, maybeQuote, url, attribs) { if (attribs === void 0) { attribs = ''; } url = dom_1.escapeXmlText(url); attribs = dom_1.legalizeAttributes(attribs, ['href'], function (e) { return console.warn(e); }, ['title']); return pre + ("<a href=\"" + url + "\"" + attribs + ">" + text + "</a>"); }); }, isNonVerbatimElement); // Inline media: `![text](url)` dom_1.transformHtmlWith(node, function (html) { return html.replace(new RegExp("!\\[([^\\[\\]]*)]\\s*\\(" + common_1.MEDIA_PARAM_RE.source + "\\)", 'g'), function (_, altRaw, urlRaw, attribsRaw) { if (attribsRaw === void 0) { attribsRaw = ''; } if (urlRaw.startsWith('"')) { urlRaw = urlRaw.substring(1, urlRaw.length - 1); } var alt = dom_1.unescapeXmlText(altRaw); var url = dom_1.unescapeXmlText(urlRaw), attribs = dom_1.unescapeXmlText(attribsRaw); return "<" + "mf-media" /* Media */ + " src=\"" + dom_1.escapeXmlText(url) + "\" alt=\"" + dom_1.escapeXmlText(alt) + "\"" + (dom_1.legalizeAttributes(attribs, ['src', 'alt'], function (m) { return console.warn(m); }, ['title']) + " />"); }); }, isNonVerbatimElement); // Symbolic hyperlink: `[text][linkname]` `![alt][linkname]` var linkSymbolTable = new Map(); dom_1.transformHtmlWith(node, function (html, ctx) { if (linkSymbolTable.size === 0) { return html; } // Can't use `String#replace` here because it only scans a string in // the forward direction. var parts = html.split(/((!)?\[([^\]]+)\]\s*\[([^\^#!<>[\]][^<>[\]]*)?\])/g); // ^ ^^^^^^ ^^^^^^^^^^^^^^^^^^^^ // image? text symbol name if (parts.length === 1) { return html; } var output = []; output.unshift(parts.pop()); for (var i = parts.length; i > 0; i -= 5) { var pre = parts[i - 5]; var source = parts[i - 4]; var isImage = parts[i - 3]; var text = parts[i - 2]; var symbolName = parts[i - 1]; if (symbolName == null) { symbolName = ctx.expand(text); } var linkTarget = linkSymbolTable.get(symbolName); if (linkTarget != null) { linkSymbolTable.delete(symbolName); linkTarget = linkTarget.trim(); var match = linkTarget.match(/^(?:(")?([^"]*?)\1)( .*)?$/); if (match) { var _1 = match[0], _2 = match[1], url = match[2], attribs = match[3]; if (isImage) { attribs = dom_1.legalizeAttributes(attribs || '', ['src', 'alt'], function (e) { return console.warn(e); }, ['title']); output.unshift("<" + "mf-media" /* Media */ + " src=\"" + dom_1.escapeXmlText(url) + "\" alt=\"" + dom_1.escapeXmlText(text) + "\"" + attribs + " />"); } else { attribs = dom_1.legalizeAttributes(attribs || '', ['href'], function (e) { return console.warn(e); }, ['title']); output.unshift("<a href=\"" + dom_1.escapeXmlText(url) + "\"" + attribs + ">" + text + "</a>"); } } else { output.unshift("<" + "mf-error" /* Error */ + ">Failed to parse the link target: <code>" + dom_1.escapeXmlText(linkTarget) + ("</code></" + "mf-error" /* Error */ + ">")); } } else { // Link target wasn't found; treat the fragment as a normal text output.unshift(source); } output.unshift(pre); } return output.join(''); }, function (element) { if (element.tagName === "mf-link-target" /* LinkTarget */) { var symbolName = element.getAttribute('link-id'); var linkTarget = element.textContent.replace(/[\r\n]/g, ''); linkSymbolTable.set(symbolName, linkTarget); // Consume `<TextInternalTagNames.LinkTarget>` element.parentElement.removeChild(element); return false; } return isNonVerbatimElementAndNotLink(element); }, // Traverse in the reverse order so that true); // Bare URLs var PROTOCOL_RE = /(?:https?|ftps?|sftp|ipfs|ipns|dweb|mailto):\/\//.source; var MAIL_CHR_RE = /[-0-9a-zA-Z#%+=_]/.source; // doesn't handle uncommon cases var MAIL_CHR_INNER_RE = "(?:" + MAIL_CHR_RE + "|\\.(?!\\.))"; var URL_RE = new RegExp( // url "(" + PROTOCOL_RE + "(?:[^ \n\t<>&]|&amp;)*)|" + ( // mail "(" + MAIL_CHR_RE + MAIL_CHR_INNER_RE + "*@" + MAIL_CHR_INNER_RE + "*" + MAIL_CHR_RE + ")"), 'g'); dom_1.transformTextNodeWith(node, function (html) { return html.replace(URL_RE, function (_, url, mail) { var text = dom_1.unescapeXmlText(url || mail); var href = (mail && 'mailto:' + dom_1.unescapeXmlText(mail)) || text; return "<a href=\"" + dom_1.escapeXmlText(href) + "\"><code>" + dom_1.escapeXmlText(text) + "</code></a>"; }); }, isNonVerbatimElementAndNotLink, false); dom_1.transformTextNodeWith(node, function (html) { // Arrows html = html.replace(ARROWS_REGEX, function (_, start, arrow) { return start + ARROWS_MAP.get(arrow); }); // Em dash: `---` html = html.replace(/\b---\b/g, '—'); // En dash: `--` html = html.replace(/\b--\b/g, '–'); // Number x number html = html.replace(/\b(\d+\s?)x(\s?\d+)\b/g, '$1×$2'); return html; }, isNonVerbatimElement, false); // Strikethrough dom_1.transformHtmlWith(node, function (html) { return html.replace(/~~([^]+?)~~/g, '<del>$1</del>'); }, isNonVerbatimElement); // Boldfaces dom_1.transformHtmlWith(node, function (html) { return html.replace(/\*\*([^]+?)\*\*/g, '<b>$1</b>'); }, isNonVerbatimElement); dom_1.transformHtmlWith(node, function (html) { return html.replace(/__([^]+?)__/g, '<b>$1</b>'); }, isNonVerbatimElement); // Italics dom_1.transformHtmlWith(node, function (html) { return html.replace(/\*([^]+?)\*/g, '<i>$1</i>'); }, isNonVerbatimElement); dom_1.transformHtmlWith(node, function (html) { return html.replace(/_([^]+?)_/g, '<i>$1</i>'); }, isNonVerbatimElement); // The first paragraph starting with `<b>...</b>` is treated as title (function () { var firstPara = node.getElementsByTagName('p')[0]; if (!firstPara) { return; } var bold = null; for (var n = firstPara.firstChild; n; n = n.nextSibling) { if ((n instanceof dom_1.DomTypes.Text) && n.textContent.match(/^\s*$/)) { continue; } if (!(n instanceof dom_1.DomTypes.Element) || (n.tagName !== 'B' && n.tagName !== 'b')) { return; } bold = n; break; } if (!bold) { return; } // Replace `<p><b>...</b></p>` with `<mf-title>...</mf-title>` var title = node.ownerDocument.createElement("mf-title" /* Title */); for (var n = bold.firstChild; n;) { var next = n.nextSibling; title.appendChild(n); n = next; } firstPara.parentElement.insertBefore(title, firstPara); firstPara.parentElement.removeChild(firstPara); // If there are remaining contents, put them in `<mf-lead>...</mf-lead>` var lead = node.ownerDocument.createElement("mf-lead" /* Lead */); var isEmpty = true; for (var n = bold.nextSibling; n;) { if (!(n instanceof dom_1.DomTypes.Text) || !n.textContent.match(/^\s*$/)) { isEmpty = false; } var next = n.nextSibling; lead.appendChild(n); n = next; } if (isEmpty) { return; } title.parentElement.insertBefore(lead, title.nextSibling); })(); // Unwrap `<mf-text>` var parent = node.parentElement; if (!parent) { throw new Error("mf-text" /* Text */ + " must bave a parent element."); } while (node.firstChild) { parent.insertBefore(node.firstChild, node); } parent.removeChild(node); } exports.expandMfText = expandMfText; //# sourceMappingURL=mftext.js.map