diff --git a/.gitignore b/.gitignore index 2b75e92..7d01f8a 100644 --- a/.gitignore +++ b/.gitignore @@ -90,7 +90,6 @@ out # Nuxt.js build / generate output .nuxt -dist # Gatsby files .cache/ diff --git a/dist/js/defaults.js b/dist/js/defaults.js new file mode 100644 index 0000000..bf2801f --- /dev/null +++ b/dist/js/defaults.js @@ -0,0 +1,54 @@ +"use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.defaultTags = void 0; +const tags = __importStar(require("./tags")); +exports.defaultTags = new tags.TagCollection([ + new tags.Bold(), + new tags.Italic(), + new tags.BoldItalic(), + new tags.Strikethrough(), + new tags.Code(), + new tags.Anchor(), + new tags.HardBreak(), + new tags.FencedCode(), + new tags.HorizontalRule(), + new tags.Heading(), + new tags.Blockquote(), + new tags.OrderedList(), + new tags.UnorderedList(), + new tags.Table(), + new tags.Paragraph() +]); diff --git a/dist/js/hopdown.js b/dist/js/hopdown.js new file mode 100644 index 0000000..b7b28b3 --- /dev/null +++ b/dist/js/hopdown.js @@ -0,0 +1,383 @@ +"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 = /^\[(?