"use strict"; /* * hopdown.ts * - Configurable markdown <=> HTML converter. */ Object.defineProperty(exports, "__esModule", { value: true }); exports.HopDown = void 0; exports.escapeHtml = escapeHtml; const defaults_1 = require("./defaults"); const tokenizer_1 = require("./tokenizer"); function escapeHtml(source) { return source .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"'); } /** * A configurable markdown <=> HTML converter. * * Examples: * * const hopdown = new HopDown(); * * const html = hopdown.toHTML('**bold**'); * html === '

bold

'; * * const markdown = hopdown.toMarkdown(html); * markdown === '**bold**'; * * const node = hopdown.toEditorNode('**bold**'); * (node as HTMLElement).outerHTML === ( * "
" + * "**" + * "bold" + * "" + * "
" * ); */ class HopDown { tags; referenceLinks = new Map(); tokenizer; constructor(tags) { this.tags = tags || defaults_1.defaultTags; this.tokenizer = new tokenizer_1.Tokenizer(this.tags); } /** * Convert a markdown string to tokens. */ tokenize(source) { let text = source; // Resolve reference links before tokenizing text = this.resolveReferenceLinks(text); // Normalize _ emphasis to * text = this.normalizeUnderscores(text); const tokens = this.tokenizer.tokenize(text); return tokens; } /** * Convert an HTML string or a DocumentFragment to markdown. */ toMarkdown = (html) => { var md = ''; if (typeof (html) == 'string') { const container = document.createElement('div'); container.innerHTML = html; md = this.nodeToMarkdown(container); } else { md = this.nodeToMarkdown(html); } return md.replace(/\n{3,}/g, '\n\n').trim(); }; /** * Convert a markdown string to an HTML string. */ toHTML = (markdown) => { const output = []; const preprocessed = this.preprocessMarkdown(markdown); if (!preprocessed.lines) { return ''; } const blockTags = this.tags.ordered.filter(t => { return t.isBlock && t.constructor.name !== 'Paragraph'; }); const pTag = this.tags.getByElementName('p'); if (pTag === undefined) { throw new Error("Cannot parse without a paragraph tag!"); } let lineIndex = 0; while (lineIndex < preprocessed.lines.length) { let context; let matched = false; let token; for (const tag of blockTags) { context = { lines: preprocessed.lines, index: lineIndex, text: '', offset: 0, }; token = tag.match(context, this.tags); if (!token) { continue; } output.push(tag.toHTML(token, this.inlineToHTML)); lineIndex += token.consumed; matched = true; break; } if (matched) { continue; } /* always check the paragraph tag last */ token = pTag.match(context, this.tags); if (token) { output.push(pTag.toHTML(token, this.inlineToHTML)); lineIndex += token.consumed; continue; } lineIndex++; } return output.join('\n'); }; /** * Convert a markdown string to a DocumentFragment consisting of wysiwyg editor nodes. */ toEditorNode = (markdown) => { const tokens = this.tokenize(markdown); return this.tokensToEditorNodes(tokens); }; /** * Recursive entrypoint for converting markdown to html. */ inlineToHTML = (markdown) => { const tokens = this.tokenize(markdown); return this.tokensToHTML(tokens); }; /** * Recursive entrypoint for converting markdown to editor nodes. */ inlineToEditorNodes = (markdown) => { const tokens = this.tokenize(markdown); return this.tokensToEditorNodes(tokens); }; /** * Process a markdown string by splitting it into lines, stripping empty lines, and capturing reference links. */ preprocessMarkdown(markdown) { const blankLine = /^\s*$/; const refDefinition = /^\[(?