UNPKG

commonmark

Version:

a strongly specified, highly compatible variant of Markdown

1,460 lines (1,298 loc) 167 kB
/* commonmark 0.29 https://github.com/CommonMark/commonmark.js @license BSD3 */ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.commonmark = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ "use strict"; var Node = require('./node'); var unescapeString = require('./common').unescapeString; var OPENTAG = require('./common').OPENTAG; var CLOSETAG = require('./common').CLOSETAG; var CODE_INDENT = 4; var C_TAB = 9; var C_NEWLINE = 10; var C_GREATERTHAN = 62; var C_LESSTHAN = 60; var C_SPACE = 32; var C_OPEN_BRACKET = 91; var InlineParser = require('./inlines'); var reHtmlBlockOpen = [ /./, // dummy for 0 /^<(?:script|pre|style)(?:\s|>|$)/i, /^<!--/, /^<[?]/, /^<![A-Z]/, /^<!\[CDATA\[/, /^<[/]?(?:address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[123456]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul)(?:\s|[/]?[>]|$)/i, new RegExp('^(?:' + OPENTAG + '|' + CLOSETAG + ')\\s*$', 'i') ]; var reHtmlBlockClose = [ /./, // dummy for 0 /<\/(?:script|pre|style)>/i, /-->/, /\?>/, />/, /\]\]>/ ]; var reThematicBreak = /^(?:(?:\*[ \t]*){3,}|(?:_[ \t]*){3,}|(?:-[ \t]*){3,})[ \t]*$/; var reMaybeSpecial = /^[#`~*+_=<>0-9-]/; var reNonSpace = /[^ \t\f\v\r\n]/; var reBulletListMarker = /^[*+-]/; var reOrderedListMarker = /^(\d{1,9})([.)])/; var reATXHeadingMarker = /^#{1,6}(?:[ \t]+|$)/; var reCodeFence = /^`{3,}(?!.*`)|^~{3,}/; var reClosingCodeFence = /^(?:`{3,}|~{3,})(?= *$)/; var reSetextHeadingLine = /^(?:=+|-+)[ \t]*$/; var reLineEnding = /\r\n|\n|\r/; // Returns true if string contains only space characters. var isBlank = function(s) { return !(reNonSpace.test(s)); }; var isSpaceOrTab = function(c) { return c === C_SPACE || c === C_TAB; }; var peek = function(ln, pos) { if (pos < ln.length) { return ln.charCodeAt(pos); } else { return -1; } }; // DOC PARSER // These are methods of a Parser object, defined below. // Returns true if block ends with a blank line, descending if needed // into lists and sublists. var endsWithBlankLine = function(block) { while (block) { if (block._lastLineBlank) { return true; } var t = block.type; if (!block._lastLineChecked && (t === 'list' || t === 'item')) { block._lastLineChecked = true; block = block._lastChild; } else { block._lastLineChecked = true; break; } } return false; }; // Add a line to the block at the tip. We assume the tip // can accept lines -- that check should be done before calling this. var addLine = function() { if (this.partiallyConsumedTab) { this.offset += 1; // skip over tab // add space characters: var charsToTab = 4 - (this.column % 4); this.tip._string_content += (' '.repeat(charsToTab)); } this.tip._string_content += this.currentLine.slice(this.offset) + '\n'; }; // Add block of type tag as a child of the tip. If the tip can't // accept children, close and finalize it and try its parent, // and so on til we find a block that can accept children. var addChild = function(tag, offset) { while (!this.blocks[this.tip.type].canContain(tag)) { this.finalize(this.tip, this.lineNumber - 1); } var column_number = offset + 1; // offset 0 = column 1 var newBlock = new Node(tag, [[this.lineNumber, column_number], [0, 0]]); newBlock._string_content = ''; this.tip.appendChild(newBlock); this.tip = newBlock; return newBlock; }; // Parse a list marker and return data on the marker (type, // start, delimiter, bullet character, padding) or null. var parseListMarker = function(parser, container) { var rest = parser.currentLine.slice(parser.nextNonspace); var match; var nextc; var spacesStartCol; var spacesStartOffset; var data = { type: null, tight: true, // lists are tight by default bulletChar: null, start: null, delimiter: null, padding: null, markerOffset: parser.indent }; if (parser.indent >= 4) { return null; } if ((match = rest.match(reBulletListMarker))) { data.type = 'bullet'; data.bulletChar = match[0][0]; } else if ((match = rest.match(reOrderedListMarker)) && (container.type !== 'paragraph' || match[1] === '1')) { data.type = 'ordered'; data.start = parseInt(match[1]); data.delimiter = match[2]; } else { return null; } // make sure we have spaces after nextc = peek(parser.currentLine, parser.nextNonspace + match[0].length); if (!(nextc === -1 || nextc === C_TAB || nextc === C_SPACE)) { return null; } // if it interrupts paragraph, make sure first line isn't blank if (container.type === 'paragraph' && !parser.currentLine.slice(parser.nextNonspace + match[0].length).match(reNonSpace)) { return null; } // we've got a match! advance offset and calculate padding parser.advanceNextNonspace(); // to start of marker parser.advanceOffset(match[0].length, true); // to end of marker spacesStartCol = parser.column; spacesStartOffset = parser.offset; do { parser.advanceOffset(1, true); nextc = peek(parser.currentLine, parser.offset); } while (parser.column - spacesStartCol < 5 && isSpaceOrTab(nextc)); var blank_item = peek(parser.currentLine, parser.offset) === -1; var spaces_after_marker = parser.column - spacesStartCol; if (spaces_after_marker >= 5 || spaces_after_marker < 1 || blank_item) { data.padding = match[0].length + 1; parser.column = spacesStartCol; parser.offset = spacesStartOffset; if (isSpaceOrTab(peek(parser.currentLine, parser.offset))) { parser.advanceOffset(1, true); } } else { data.padding = match[0].length + spaces_after_marker; } return data; }; // Returns true if the two list items are of the same type, // with the same delimiter and bullet character. This is used // in agglomerating list items into lists. var listsMatch = function(list_data, item_data) { return (list_data.type === item_data.type && list_data.delimiter === item_data.delimiter && list_data.bulletChar === item_data.bulletChar); }; // Finalize and close any unmatched blocks. var closeUnmatchedBlocks = function() { if (!this.allClosed) { // finalize any blocks not matched while (this.oldtip !== this.lastMatchedContainer) { var parent = this.oldtip._parent; this.finalize(this.oldtip, this.lineNumber - 1); this.oldtip = parent; } this.allClosed = true; } }; // 'finalize' is run when the block is closed. // 'continue' is run to check whether the block is continuing // at a certain line and offset (e.g. whether a block quote // contains a `>`. It returns 0 for matched, 1 for not matched, // and 2 for "we've dealt with this line completely, go to next." var blocks = { document: { continue: function() { return 0; }, finalize: function() { return; }, canContain: function(t) { return (t !== 'item'); }, acceptsLines: false }, list: { continue: function() { return 0; }, finalize: function(parser, block) { var item = block._firstChild; while (item) { // check for non-final list item ending with blank line: if (endsWithBlankLine(item) && item._next) { block._listData.tight = false; break; } // recurse into children of list item, to see if there are // spaces between any of them: var subitem = item._firstChild; while (subitem) { if (endsWithBlankLine(subitem) && (item._next || subitem._next)) { block._listData.tight = false; break; } subitem = subitem._next; } item = item._next; } }, canContain: function(t) { return (t === 'item'); }, acceptsLines: false }, block_quote: { continue: function(parser) { var ln = parser.currentLine; if (!parser.indented && peek(ln, parser.nextNonspace) === C_GREATERTHAN) { parser.advanceNextNonspace(); parser.advanceOffset(1, false); if (isSpaceOrTab(peek(ln, parser.offset))) { parser.advanceOffset(1, true); } } else { return 1; } return 0; }, finalize: function() { return; }, canContain: function(t) { return (t !== 'item'); }, acceptsLines: false }, item: { continue: function(parser, container) { if (parser.blank) { if (container._firstChild == null) { // Blank line after empty list item return 1; } else { parser.advanceNextNonspace(); } } else if (parser.indent >= container._listData.markerOffset + container._listData.padding) { parser.advanceOffset(container._listData.markerOffset + container._listData.padding, true); } else { return 1; } return 0; }, finalize: function() { return; }, canContain: function(t) { return (t !== 'item'); }, acceptsLines: false }, heading: { continue: function() { // a heading can never container > 1 line, so fail to match: return 1; }, finalize: function() { return; }, canContain: function() { return false; }, acceptsLines: false }, thematic_break: { continue: function() { // a thematic break can never container > 1 line, so fail to match: return 1; }, finalize: function() { return; }, canContain: function() { return false; }, acceptsLines: false }, code_block: { continue: function(parser, container) { var ln = parser.currentLine; var indent = parser.indent; if (container._isFenced) { // fenced var match = (indent <= 3 && ln.charAt(parser.nextNonspace) === container._fenceChar && ln.slice(parser.nextNonspace).match(reClosingCodeFence)); if (match && match[0].length >= container._fenceLength) { // closing fence - we're at end of line, so we can return parser.lastLineLength = match[0].length; parser.finalize(container, parser.lineNumber); return 2; } else { // skip optional spaces of fence offset var i = container._fenceOffset; while (i > 0 && isSpaceOrTab(peek(ln, parser.offset))) { parser.advanceOffset(1, true); i--; } } } else { // indented if (indent >= CODE_INDENT) { parser.advanceOffset(CODE_INDENT, true); } else if (parser.blank) { parser.advanceNextNonspace(); } else { return 1; } } return 0; }, finalize: function(parser, block) { if (block._isFenced) { // fenced // first line becomes info string var content = block._string_content; var newlinePos = content.indexOf('\n'); var firstLine = content.slice(0, newlinePos); var rest = content.slice(newlinePos + 1); block.info = unescapeString(firstLine.trim()); block._literal = rest; } else { // indented block._literal = block._string_content.replace(/(\n *)+$/, '\n'); } block._string_content = null; // allow GC }, canContain: function() { return false; }, acceptsLines: true }, html_block: { continue: function(parser, container) { return ((parser.blank && (container._htmlBlockType === 6 || container._htmlBlockType === 7)) ? 1 : 0); }, finalize: function(parser, block) { block._literal = block._string_content.replace(/(\n *)+$/, ''); block._string_content = null; // allow GC }, canContain: function() { return false; }, acceptsLines: true }, paragraph: { continue: function(parser) { return (parser.blank ? 1 : 0); }, finalize: function(parser, block) { var pos; var hasReferenceDefs = false; // try parsing the beginning as link reference definitions: while (peek(block._string_content, 0) === C_OPEN_BRACKET && (pos = parser.inlineParser.parseReference(block._string_content, parser.refmap))) { block._string_content = block._string_content.slice(pos); hasReferenceDefs = true; } if (hasReferenceDefs && isBlank(block._string_content)) { block.unlink(); } }, canContain: function() { return false; }, acceptsLines: true } }; // block start functions. Return values: // 0 = no match // 1 = matched container, keep going // 2 = matched leaf, no more block starts var blockStarts = [ // block quote function(parser) { if (!parser.indented && peek(parser.currentLine, parser.nextNonspace) === C_GREATERTHAN) { parser.advanceNextNonspace(); parser.advanceOffset(1, false); // optional following space if (isSpaceOrTab(peek(parser.currentLine, parser.offset))) { parser.advanceOffset(1, true); } parser.closeUnmatchedBlocks(); parser.addChild('block_quote', parser.nextNonspace); return 1; } else { return 0; } }, // ATX heading function(parser) { var match; if (!parser.indented && (match = parser.currentLine.slice(parser.nextNonspace).match(reATXHeadingMarker))) { parser.advanceNextNonspace(); parser.advanceOffset(match[0].length, false); parser.closeUnmatchedBlocks(); var container = parser.addChild('heading', parser.nextNonspace); container.level = match[0].trim().length; // number of #s // remove trailing ###s: container._string_content = parser.currentLine.slice(parser.offset).replace(/^[ \t]*#+[ \t]*$/, '').replace(/[ \t]+#+[ \t]*$/, ''); parser.advanceOffset(parser.currentLine.length - parser.offset); return 2; } else { return 0; } }, // Fenced code block function(parser) { var match; if (!parser.indented && (match = parser.currentLine.slice(parser.nextNonspace).match(reCodeFence))) { var fenceLength = match[0].length; parser.closeUnmatchedBlocks(); var container = parser.addChild('code_block', parser.nextNonspace); container._isFenced = true; container._fenceLength = fenceLength; container._fenceChar = match[0][0]; container._fenceOffset = parser.indent; parser.advanceNextNonspace(); parser.advanceOffset(fenceLength, false); return 2; } else { return 0; } }, // HTML block function(parser, container) { if (!parser.indented && peek(parser.currentLine, parser.nextNonspace) === C_LESSTHAN) { var s = parser.currentLine.slice(parser.nextNonspace); var blockType; for (blockType = 1; blockType <= 7; blockType++) { if (reHtmlBlockOpen[blockType].test(s) && (blockType < 7 || container.type !== 'paragraph')) { parser.closeUnmatchedBlocks(); // We don't adjust parser.offset; // spaces are part of the HTML block: var b = parser.addChild('html_block', parser.offset); b._htmlBlockType = blockType; return 2; } } } return 0; }, // Setext heading function(parser, container) { var match; if (!parser.indented && container.type === 'paragraph' && ((match = parser.currentLine.slice(parser.nextNonspace).match(reSetextHeadingLine)))) { parser.closeUnmatchedBlocks(); // resolve reference link definitiosn var pos; while (peek(container._string_content, 0) === C_OPEN_BRACKET && (pos = parser.inlineParser.parseReference( container._string_content, parser.refmap))) { container._string_content = container._string_content.slice(pos); } if (container._string_content.length > 0) { var heading = new Node('heading', container.sourcepos); heading.level = match[0][0] === '=' ? 1 : 2; heading._string_content = container._string_content; container.insertAfter(heading); container.unlink(); parser.tip = heading; parser.advanceOffset(parser.currentLine.length - parser.offset, false); return 2; } else { return 0; } } else { return 0; } }, // thematic break function(parser) { if (!parser.indented && reThematicBreak.test(parser.currentLine.slice(parser.nextNonspace))) { parser.closeUnmatchedBlocks(); parser.addChild('thematic_break', parser.nextNonspace); parser.advanceOffset(parser.currentLine.length - parser.offset, false); return 2; } else { return 0; } }, // list item function(parser, container) { var data; if ((!parser.indented || container.type === 'list') && (data = parseListMarker(parser, container))) { parser.closeUnmatchedBlocks(); // add the list if needed if (parser.tip.type !== 'list' || !(listsMatch(container._listData, data))) { container = parser.addChild('list', parser.nextNonspace); container._listData = data; } // add the list item container = parser.addChild('item', parser.nextNonspace); container._listData = data; return 1; } else { return 0; } }, // indented code block function(parser) { if (parser.indented && parser.tip.type !== 'paragraph' && !parser.blank) { // indented code parser.advanceOffset(CODE_INDENT, true); parser.closeUnmatchedBlocks(); parser.addChild('code_block', parser.offset); return 2; } else { return 0; } } ]; var advanceOffset = function(count, columns) { var currentLine = this.currentLine; var charsToTab, charsToAdvance; var c; while (count > 0 && (c = currentLine[this.offset])) { if (c === '\t') { charsToTab = 4 - (this.column % 4); if (columns) { this.partiallyConsumedTab = charsToTab > count; charsToAdvance = charsToTab > count ? count : charsToTab; this.column += charsToAdvance; this.offset += this.partiallyConsumedTab ? 0 : 1; count -= charsToAdvance; } else { this.partiallyConsumedTab = false; this.column += charsToTab; this.offset += 1; count -= 1; } } else { this.partiallyConsumedTab = false; this.offset += 1; this.column += 1; // assume ascii; block starts are ascii count -= 1; } } }; var advanceNextNonspace = function() { this.offset = this.nextNonspace; this.column = this.nextNonspaceColumn; this.partiallyConsumedTab = false; }; var findNextNonspace = function() { var currentLine = this.currentLine; var i = this.offset; var cols = this.column; var c; while ((c = currentLine.charAt(i)) !== '') { if (c === ' ') { i++; cols++; } else if (c === '\t') { i++; cols += (4 - (cols % 4)); } else { break; } } this.blank = (c === '\n' || c === '\r' || c === ''); this.nextNonspace = i; this.nextNonspaceColumn = cols; this.indent = this.nextNonspaceColumn - this.column; this.indented = this.indent >= CODE_INDENT; }; // Analyze a line of text and update the document appropriately. // We parse markdown text by calling this on each line of input, // then finalizing the document. var incorporateLine = function(ln) { var all_matched = true; var t; var container = this.doc; this.oldtip = this.tip; this.offset = 0; this.column = 0; this.blank = false; this.partiallyConsumedTab = false; this.lineNumber += 1; // replace NUL characters for security if (ln.indexOf('\u0000') !== -1) { ln = ln.replace(/\0/g, '\uFFFD'); } this.currentLine = ln; // For each containing block, try to parse the associated line start. // Bail out on failure: container will point to the last matching block. // Set all_matched to false if not all containers match. var lastChild; while ((lastChild = container._lastChild) && lastChild._open) { container = lastChild; this.findNextNonspace(); switch (this.blocks[container.type].continue(this, container)) { case 0: // we've matched, keep going break; case 1: // we've failed to match a block all_matched = false; break; case 2: // we've hit end of line for fenced code close and can return return; default: throw 'continue returned illegal value, must be 0, 1, or 2'; } if (!all_matched) { container = container._parent; // back up to last matching block break; } } this.allClosed = (container === this.oldtip); this.lastMatchedContainer = container; var matchedLeaf = container.type !== 'paragraph' && blocks[container.type].acceptsLines; var starts = this.blockStarts; var startsLen = starts.length; // Unless last matched container is a code block, try new container starts, // adding children to the last matched container: while (!matchedLeaf) { this.findNextNonspace(); // this is a little performance optimization: if (!this.indented && !reMaybeSpecial.test(ln.slice(this.nextNonspace))) { this.advanceNextNonspace(); break; } var i = 0; while (i < startsLen) { var res = starts[i](this, container); if (res === 1) { container = this.tip; break; } else if (res === 2) { container = this.tip; matchedLeaf = true; break; } else { i++; } } if (i === startsLen) { // nothing matched this.advanceNextNonspace(); break; } } // What remains at the offset is a text line. Add the text to the // appropriate container. // First check for a lazy paragraph continuation: if (!this.allClosed && !this.blank && this.tip.type === 'paragraph') { // lazy paragraph continuation this.addLine(); } else { // not a lazy continuation // finalize any blocks not matched this.closeUnmatchedBlocks(); if (this.blank && container.lastChild) { container.lastChild._lastLineBlank = true; } t = container.type; // Block quote lines are never blank as they start with > // and we don't count blanks in fenced code for purposes of tight/loose // lists or breaking out of lists. We also don't set _lastLineBlank // on an empty list item, or if we just closed a fenced block. var lastLineBlank = this.blank && !(t === 'block_quote' || (t === 'code_block' && container._isFenced) || (t === 'item' && !container._firstChild && container.sourcepos[0][0] === this.lineNumber)); // propagate lastLineBlank up through parents: var cont = container; while (cont) { cont._lastLineBlank = lastLineBlank; cont = cont._parent; } if (this.blocks[t].acceptsLines) { this.addLine(); // if HtmlBlock, check for end condition if (t === 'html_block' && container._htmlBlockType >= 1 && container._htmlBlockType <= 5 && reHtmlBlockClose[container._htmlBlockType].test(this.currentLine.slice(this.offset))) { this.lastLineLength = ln.length; this.finalize(container, this.lineNumber); } } else if (this.offset < ln.length && !this.blank) { // create paragraph container for line container = this.addChild('paragraph', this.offset); this.advanceNextNonspace(); this.addLine(); } } this.lastLineLength = ln.length; }; // Finalize a block. Close it and do any necessary postprocessing, // e.g. creating string_content from strings, setting the 'tight' // or 'loose' status of a list, and parsing the beginnings // of paragraphs for reference definitions. Reset the tip to the // parent of the closed block. var finalize = function(block, lineNumber) { var above = block._parent; block._open = false; block.sourcepos[1] = [lineNumber, this.lastLineLength]; this.blocks[block.type].finalize(this, block); this.tip = above; }; // Walk through a block & children recursively, parsing string content // into inline content where appropriate. var processInlines = function(block) { var node, event, t; var walker = block.walker(); this.inlineParser.refmap = this.refmap; this.inlineParser.options = this.options; while ((event = walker.next())) { node = event.node; t = node.type; if (!event.entering && (t === 'paragraph' || t === 'heading')) { this.inlineParser.parse(node); } } }; var Document = function() { var doc = new Node('document', [[1, 1], [0, 0]]); return doc; }; // The main parsing function. Returns a parsed document AST. var parse = function(input) { this.doc = new Document(); this.tip = this.doc; this.refmap = {}; this.lineNumber = 0; this.lastLineLength = 0; this.offset = 0; this.column = 0; this.lastMatchedContainer = this.doc; this.currentLine = ""; if (this.options.time) { console.time("preparing input"); } var lines = input.split(reLineEnding); var len = lines.length; if (input.charCodeAt(input.length - 1) === C_NEWLINE) { // ignore last blank line created by final newline len -= 1; } if (this.options.time) { console.timeEnd("preparing input"); } if (this.options.time) { console.time("block parsing"); } for (var i = 0; i < len; i++) { this.incorporateLine(lines[i]); } while (this.tip) { this.finalize(this.tip, len); } if (this.options.time) { console.timeEnd("block parsing"); } if (this.options.time) { console.time("inline parsing"); } this.processInlines(this.doc); if (this.options.time) { console.timeEnd("inline parsing"); } return this.doc; }; // The Parser object. function Parser(options){ return { doc: new Document(), blocks: blocks, blockStarts: blockStarts, tip: this.doc, oldtip: this.doc, currentLine: "", lineNumber: 0, offset: 0, column: 0, nextNonspace: 0, nextNonspaceColumn: 0, indent: 0, indented: false, blank: false, partiallyConsumedTab: false, allClosed: true, lastMatchedContainer: this.doc, refmap: {}, lastLineLength: 0, inlineParser: new InlineParser(options), findNextNonspace: findNextNonspace, advanceOffset: advanceOffset, advanceNextNonspace: advanceNextNonspace, addLine: addLine, addChild: addChild, incorporateLine: incorporateLine, finalize: finalize, processInlines: processInlines, closeUnmatchedBlocks: closeUnmatchedBlocks, parse: parse, options: options || {} }; } module.exports = Parser; },{"./common":2,"./inlines":5,"./node":6}],2:[function(require,module,exports){ "use strict"; var encode = require('mdurl/encode'); var C_BACKSLASH = 92; var decodeHTML = require('entities').decodeHTML; var ENTITY = "&(?:#x[a-f0-9]{1,6}|#[0-9]{1,7}|[a-z][a-z0-9]{1,31});"; var TAGNAME = '[A-Za-z][A-Za-z0-9-]*'; var ATTRIBUTENAME = '[a-zA-Z_:][a-zA-Z0-9:._-]*'; var UNQUOTEDVALUE = "[^\"'=<>`\\x00-\\x20]+"; var SINGLEQUOTEDVALUE = "'[^']*'"; var DOUBLEQUOTEDVALUE = '"[^"]*"'; var ATTRIBUTEVALUE = "(?:" + UNQUOTEDVALUE + "|" + SINGLEQUOTEDVALUE + "|" + DOUBLEQUOTEDVALUE + ")"; var ATTRIBUTEVALUESPEC = "(?:" + "\\s*=" + "\\s*" + ATTRIBUTEVALUE + ")"; var ATTRIBUTE = "(?:" + "\\s+" + ATTRIBUTENAME + ATTRIBUTEVALUESPEC + "?)"; var OPENTAG = "<" + TAGNAME + ATTRIBUTE + "*" + "\\s*/?>"; var CLOSETAG = "</" + TAGNAME + "\\s*[>]"; var HTMLCOMMENT = "<!---->|<!--(?:-?[^>-])(?:-?[^-])*-->"; var PROCESSINGINSTRUCTION = "[<][?].*?[?][>]"; var DECLARATION = "<![A-Z]+" + "\\s+[^>]*>"; var CDATA = "<!\\[CDATA\\[[\\s\\S]*?\\]\\]>"; var HTMLTAG = "(?:" + OPENTAG + "|" + CLOSETAG + "|" + HTMLCOMMENT + "|" + PROCESSINGINSTRUCTION + "|" + DECLARATION + "|" + CDATA + ")"; var reHtmlTag = new RegExp('^' + HTMLTAG, 'i'); var reBackslashOrAmp = /[\\&]/; var ESCAPABLE = '[!"#$%&\'()*+,./:;<=>?@[\\\\\\]^_`{|}~-]'; var reEntityOrEscapedChar = new RegExp('\\\\' + ESCAPABLE + '|' + ENTITY, 'gi'); var XMLSPECIAL = '[&<>"]'; var reXmlSpecial = new RegExp(XMLSPECIAL, 'g'); var unescapeChar = function(s) { if (s.charCodeAt(0) === C_BACKSLASH) { return s.charAt(1); } else { return decodeHTML(s); } }; // Replace entities and backslash escapes with literal characters. var unescapeString = function(s) { if (reBackslashOrAmp.test(s)) { return s.replace(reEntityOrEscapedChar, unescapeChar); } else { return s; } }; var normalizeURI = function(uri) { try { return encode(uri); } catch(err) { return uri; } }; var replaceUnsafeChar = function(s) { switch (s) { case '&': return '&amp;'; case '<': return '&lt;'; case '>': return '&gt;'; case '"': return '&quot;'; default: return s; } }; var escapeXml = function(s) { if (reXmlSpecial.test(s)) { return s.replace(reXmlSpecial, replaceUnsafeChar); } else { return s; } }; module.exports = { unescapeString: unescapeString, normalizeURI: normalizeURI, escapeXml: escapeXml, reHtmlTag: reHtmlTag, OPENTAG: OPENTAG, CLOSETAG: CLOSETAG, ENTITY: ENTITY, ESCAPABLE: ESCAPABLE }; },{"entities":11,"mdurl/encode":19}],3:[function(require,module,exports){ "use strict"; // derived from https://github.com/mathiasbynens/String.fromCodePoint /*! http://mths.be/fromcodepoint v0.2.1 by @mathias */ if (String.fromCodePoint) { module.exports = function (_) { try { return String.fromCodePoint(_); } catch (e) { if (e instanceof RangeError) { return String.fromCharCode(0xFFFD); } throw e; } }; } else { var stringFromCharCode = String.fromCharCode; var floor = Math.floor; var fromCodePoint = function() { var MAX_SIZE = 0x4000; var codeUnits = []; var highSurrogate; var lowSurrogate; var index = -1; var length = arguments.length; if (!length) { return ''; } var result = ''; while (++index < length) { var codePoint = Number(arguments[index]); if ( !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity` codePoint < 0 || // not a valid Unicode code point codePoint > 0x10FFFF || // not a valid Unicode code point floor(codePoint) !== codePoint // not an integer ) { return String.fromCharCode(0xFFFD); } if (codePoint <= 0xFFFF) { // BMP code point codeUnits.push(codePoint); } else { // Astral code point; split in surrogate halves // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae codePoint -= 0x10000; highSurrogate = (codePoint >> 10) + 0xD800; lowSurrogate = (codePoint % 0x400) + 0xDC00; codeUnits.push(highSurrogate, lowSurrogate); } if (index + 1 === length || codeUnits.length > MAX_SIZE) { result += stringFromCharCode.apply(null, codeUnits); codeUnits.length = 0; } } return result; }; module.exports = fromCodePoint; } },{}],4:[function(require,module,exports){ "use strict"; // commonmark.js - CommomMark in JavaScript // Copyright (C) 2014 John MacFarlane // License: BSD3. // Basic usage: // // var commonmark = require('commonmark'); // var parser = new commonmark.Parser(); // var renderer = new commonmark.HtmlRenderer(); // console.log(renderer.render(parser.parse('Hello *world*'))); module.exports.Node = require('./node'); module.exports.Parser = require('./blocks'); module.exports.Renderer = require('./render/renderer'); module.exports.HtmlRenderer = require('./render/html'); module.exports.XmlRenderer = require('./render/xml'); },{"./blocks":1,"./node":6,"./render/html":8,"./render/renderer":9,"./render/xml":10}],5:[function(require,module,exports){ "use strict"; var Node = require('./node'); var common = require('./common'); var normalizeReference = require('./normalize-reference'); var normalizeURI = common.normalizeURI; var unescapeString = common.unescapeString; var fromCodePoint = require('./from-code-point.js'); var decodeHTML = require('entities').decodeHTML; require('string.prototype.repeat'); // Polyfill for String.prototype.repeat // Constants for character codes: var C_NEWLINE = 10; var C_ASTERISK = 42; var C_UNDERSCORE = 95; var C_BACKTICK = 96; var C_OPEN_BRACKET = 91; var C_CLOSE_BRACKET = 93; var C_LESSTHAN = 60; var C_BANG = 33; var C_BACKSLASH = 92; var C_AMPERSAND = 38; var C_OPEN_PAREN = 40; var C_CLOSE_PAREN = 41; var C_COLON = 58; var C_SINGLEQUOTE = 39; var C_DOUBLEQUOTE = 34; // Some regexps used in inline parser: var ESCAPABLE = common.ESCAPABLE; var ESCAPED_CHAR = '\\\\' + ESCAPABLE; var ENTITY = common.ENTITY; var reHtmlTag = common.reHtmlTag; var rePunctuation = new RegExp(/[!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F\uFF5B\uFF5D\uFF5F-\uFF65]|\uD800[\uDD00-\uDD02\uDF9F\uDFD0]|\uD801\uDD6F|\uD802[\uDC57\uDD1F\uDD3F\uDE50-\uDE58\uDE7F\uDEF0-\uDEF6\uDF39-\uDF3F\uDF99-\uDF9C]|\uD804[\uDC47-\uDC4D\uDCBB\uDCBC\uDCBE-\uDCC1\uDD40-\uDD43\uDD74\uDD75\uDDC5-\uDDC9\uDDCD\uDDDB\uDDDD-\uDDDF\uDE38-\uDE3D\uDEA9]|\uD805[\uDCC6\uDDC1-\uDDD7\uDE41-\uDE43\uDF3C-\uDF3E]|\uD809[\uDC70-\uDC74]|\uD81A[\uDE6E\uDE6F\uDEF5\uDF37-\uDF3B\uDF44]|\uD82F\uDC9F|\uD836[\uDE87-\uDE8B]/); var reLinkTitle = new RegExp( '^(?:"(' + ESCAPED_CHAR + '|[^"\\x00])*"' + '|' + '\'(' + ESCAPED_CHAR + '|[^\'\\x00])*\'' + '|' + '\\((' + ESCAPED_CHAR + '|[^()\\x00])*\\))'); var reLinkDestinationBraces = /^(?:<(?:[^<>\n\\\x00]|\\.)*>)/; var reEscapable = new RegExp('^' + ESCAPABLE); var reEntityHere = new RegExp('^' + ENTITY, 'i'); var reTicks = /`+/; var reTicksHere = /^`+/; var reEllipses = /\.\.\./g; var reDash = /--+/g; var reEmailAutolink = /^<([a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*)>/; var reAutolink = /^<[A-Za-z][A-Za-z0-9.+-]{1,31}:[^<>\x00-\x20]*>/i; var reSpnl = /^ *(?:\n *)?/; var reWhitespaceChar = /^[ \t\n\x0b\x0c\x0d]/; var reUnicodeWhitespaceChar = /^\s/; var reFinalSpace = / *$/; var reInitialSpace = /^ */; var reSpaceAtEndOfLine = /^ *(?:\n|$)/; var reLinkLabel = /^\[(?:[^\\\[\]]|\\.){0,1000}\]/; // Matches a string of non-special characters. var reMain = /^[^\n`\[\]\\!<&*_'"]+/m; var text = function(s) { var node = new Node('text'); node._literal = s; return node; }; // INLINE PARSER // These are methods of an InlineParser object, defined below. // An InlineParser keeps track of a subject (a string to be // parsed) and a position in that subject. // If re matches at current position in the subject, advance // position in subject and return the match; otherwise return null. var match = function(re) { var m = re.exec(this.subject.slice(this.pos)); if (m === null) { return null; } else { this.pos += m.index + m[0].length; return m[0]; } }; // Returns the code for the character at the current subject position, or -1 // there are no more characters. var peek = function() { if (this.pos < this.subject.length) { return this.subject.charCodeAt(this.pos); } else { return -1; } }; // Parse zero or more space characters, including at most one newline var spnl = function() { this.match(reSpnl); return true; }; // All of the parsers below try to match something at the current position // in the subject. If they succeed in matching anything, they // return the inline matched, advancing the subject. // Attempt to parse backticks, adding either a backtick code span or a // literal sequence of backticks. var parseBackticks = function(block) { var ticks = this.match(reTicksHere); if (ticks === null) { return false; } var afterOpenTicks = this.pos; var matched; var node; var contents; while ((matched = this.match(reTicks)) !== null) { if (matched === ticks) { node = new Node('code'); contents = this.subject.slice(afterOpenTicks, this.pos - ticks.length) .replace(/\n/gm, ' '); if (contents.length > 0 && contents.match(/[^ ]/) !== null && contents[0] == ' ' && contents[contents.length - 1] == ' ') { node._literal = contents.slice(1, contents.length - 1); } else { node._literal = contents; } block.appendChild(node); return true; } } // If we got here, we didn't match a closing backtick sequence. this.pos = afterOpenTicks; block.appendChild(text(ticks)); return true; }; // Parse a backslash-escaped special character, adding either the escaped // character, a hard line break (if the backslash is followed by a newline), // or a literal backslash to the block's children. Assumes current character // is a backslash. var parseBackslash = function(block) { var subj = this.subject; var node; this.pos += 1; if (this.peek() === C_NEWLINE) { this.pos += 1; node = new Node('linebreak'); block.appendChild(node); } else if (reEscapable.test(subj.charAt(this.pos))) { block.appendChild(text(subj.charAt(this.pos))); this.pos += 1; } else { block.appendChild(text('\\')); } return true; }; // Attempt to parse an autolink (URL or email in pointy brackets). var parseAutolink = function(block) { var m; var dest; var node; if ((m = this.match(reEmailAutolink))) { dest = m.slice(1, m.length - 1); node = new Node('link'); node._destination = normalizeURI('mailto:' + dest); node._title = ''; node.appendChild(text(dest)); block.appendChild(node); return true; } else if ((m = this.match(reAutolink))) { dest = m.slice(1, m.length - 1); node = new Node('link'); node._destination = normalizeURI(dest); node._title = ''; node.appendChild(text(dest)); block.appendChild(node); return true; } else { return false; } }; // Attempt to parse a raw HTML tag. var parseHtmlTag = function(block) { var m = this.match(reHtmlTag); if (m === null) { return false; } else { var node = new Node('html_inline'); node._literal = m; block.appendChild(node); return true; } }; // Scan a sequence of characters with code cc, and return information about // the number of delimiters and whether they are positioned such that // they can open and/or close emphasis or strong emphasis. A utility // function for strong/emph parsing. var scanDelims = function(cc) { var numdelims = 0; var char_before, char_after, cc_after; var startpos = this.pos; var left_flanking, right_flanking, can_open, can_close; var after_is_whitespace, after_is_punctuation, before_is_whitespace, before_is_punctuation; if (cc === C_SINGLEQUOTE || cc === C_DOUBLEQUOTE) { numdelims++; this.pos++; } else { while (this.peek() === cc) { numdelims++; this.pos++; } } if (numdelims === 0) { return null; } char_before = startpos === 0 ? '\n' : this.subject.charAt(startpos - 1); cc_after = this.peek(); if (cc_after === -1) { char_after = '\n'; } else { char_after = fromCodePoint(cc_after); } after_is_whitespace = reUnicodeWhitespaceChar.test(char_after); after_is_punctuation = rePunctuation.test(char_after); before_is_whitespace = reUnicodeWhitespaceChar.test(char_before); before_is_punctuation = rePunctuation.test(char_before); left_flanking = !after_is_whitespace && (!after_is_punctuation || before_is_whitespace || before_is_punctuation); right_flanking = !before_is_whitespace && (!before_is_punctuation || after_is_whitespace || after_is_punctuation); if (cc === C_UNDERSCORE) { can_open = left_flanking && (!right_flanking || before_is_punctuation); can_close = right_flanking && (!left_flanking || after_is_punctuation); } else if (cc === C_SINGLEQUOTE || cc === C_DOUBLEQUOTE) { can_open = left_flanking && !right_flanking; can_close = right_flanking; } else { can_open = left_flanking; can_close = right_flanking; } this.pos = startpos; return { numdelims: numdelims, can_open: can_open, can_close: can_close }; }; // Handle a delimiter marker for emphasis or a quote. var handleDelim = function(cc, block) { var res = this.scanDelims(cc); if (!res) { return false; } var numdelims = res.numdelims; var startpos = this.pos; var contents; this.pos += numdelims; if (cc === C_SINGLEQUOTE) { contents = "\u2019"; } else if (cc === C_DOUBLEQUOTE) { contents = "\u201C"; } else { contents = this.subject.slice(startpos, this.pos); } var node = text(contents); block.appendChild(node); // Add entry to stack for this opener if ((res.can_open || res.can_close) && (this.options.smart || cc !== C_SINGLEQUOTE || cc !== C_DOUBLEQUOTE)){ this.delimiters = { cc: cc, numdelims: numdelims, origdelims: numdelims, node: node, previous: this.delimiters, next: null, can_open: res.can_open, can_close: res.can_close }; if (this.delimiters.previous !== null) { this.delimiters.previous.next = this.delimiters; } } return true; }; var removeDelimiter = function(delim) { if (delim.previous !== null) { delim.previous.next = delim.next; } if (delim.next === null) { // top of stack this.delimiters = delim.previous; } else { delim.next.previous = delim.previous; } }; var removeDelimitersBetween = function(bottom, top) { if (bottom.next !== top) { bottom.next = top; top.previous = bottom; } }; var processEmphasis = function(stack_bottom) { var opener, closer, old_closer; var opener_inl, closer_inl; var tempstack; var use_delims; var tmp, next; var opener_found; var openers_bottom = [[],[],[]]; var odd_match = false; for (var i=0; i < 3; i++) { openers_bottom[i][C_UNDERSCORE] = stack_bottom; openers_bottom[i][C_ASTERISK] = stack_bottom; openers_bottom[i][C_SINGLEQUOTE] = stack_bottom; openers_bottom[i][C_DOUBLEQUOTE] = stack_bottom; } // find first closer above stack_bottom: closer = this.delimiters; while (closer !== null && closer.previous !== stack_bottom) { closer = closer.previous; } // move forward, looking for closers, and handling each while (closer !== null) { var closercc = closer.cc; if (!closer.can_close) { closer = closer.next; } else { // found emphasis closer. now look back for first matching opener: opener = closer.previous; opener_found = false; while (opener !== null && opener !== stack_bottom && opener !== openers_bottom[closer.origdelims % 3][closercc]) { odd_match = (closer.can_open || opener.can_close) && closer.origdelims % 3 !== 0 && (opener.origdelims + closer.origdelims) % 3 === 0; if (opener.cc === closer.cc && opener.can_open && !odd_match) { opener_found = true; break; } opener = opener.previous; } old_closer