From 17d81796da405eabea098fc36965ab2b5cb96169 Mon Sep 17 00:00:00 2001
From: Tony Brix
';
+ return '' + (escaped ? _code : escape$2(_code, true)) + '
';
}
- return '' + (escaped ? _code : escape$1(_code, true)) + '
\n';
+ return '' + (escaped ? _code : escape$2(_code, true)) + '
\n';
};
_proto.blockquote = function blockquote(quote) {
@@ -1475,7 +1906,7 @@
return text;
}
- var out = 'An error occurred:' + (escaped ? _code : escape$1(_code, true)) + '
' + escape$3(e.message + '', true) + ''; + return '
An error occurred:
' + escape$2(e.message + '', true) + ''; } throw e; @@ -2029,7 +2460,7 @@ }; marked.getDefaults = getDefaults; - marked.defaults = defaults$4; + marked.defaults = defaults$5; /** * Expose */ @@ -2040,6 +2471,7 @@ marked.TextRenderer = TextRenderer_1; marked.Lexer = Lexer_1; marked.lexer = Lexer_1.lex; + marked.Tokenizer = Tokenizer_1; marked.Slugger = Slugger_1; marked.parse = marked; var marked_1 = marked; diff --git a/marked.min.js b/marked.min.js index 085feedc0b..3ff5ab9b28 100644 --- a/marked.min.js +++ b/marked.min.js @@ -3,4 +3,4 @@ * Copyright (c) 2011-2020, Christopher Jeffrey. (MIT Licensed) * https://github.com/markedjs/marked */ -!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).marked=t()}(this,function(){"use strict";function i(e,t){for(var n=0;n
'+(n?e:X(e,!0))+"
\n":""+(n?e:X(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+Y(e.message+"",!0)+"";throw e}}return re.options=re.setOptions=function(e){return Q(re.defaults,e),te(re.defaults),re},re.getDefaults=ee,re.defaults=ne,re.Parser=K,re.parser=K.parse,re.Renderer=G,re.TextRenderer=M,re.Lexer=U,re.lexer=U.lex,re.Slugger=V,re.parse=re}); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).marked=t()}(this,function(){"use strict";function i(e,t){for(var n=0;n
'+(n?e:X(e,!0))+"
\n":""+(n?e:X(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+Y(e.message+"",!0)+"";throw e}}return re.options=re.setOptions=function(e){return Q(re.defaults,e),te(re.defaults),re},re.getDefaults=ee,re.defaults=ne,re.Parser=K,re.parser=K.parse,re.Renderer=G,re.TextRenderer=M,re.Lexer=U,re.lexer=U.lex,re.Slugger=V,re.parse=re}); diff --git a/src/Lexer.js b/src/Lexer.js index b119241591..7ad44a2a3a 100644 --- a/src/Lexer.js +++ b/src/Lexer.js @@ -1,11 +1,5 @@ +const Tokenizer = require('./Tokenizer.js'); const { defaults } = require('./defaults.js'); -const { block, inline } = require('./rules.js'); -const { - rtrim, - splitCells, - escape, - findClosingBracket -} = require('./helpers.js'); /** * Block Lexer @@ -15,32 +9,17 @@ module.exports = class Lexer { this.tokens = []; this.tokens.links = Object.create(null); this.options = options || defaults; - this.rules = { - block: block.normal, - inline: inline.normal - }; - - if (this.options.pedantic) { - this.rules.block = block.pedantic; - this.rules.inline = inline.pedantic; - } else if (this.options.gfm) { - this.rules.block = block.gfm; - if (this.options.breaks) { - this.rules.inline = inline.breaks; - } else { - this.rules.inline = inline.gfm; - } - } + this.options.tokenizer = this.options.tokenizer || new Tokenizer(); + this.tokenizer = this.options.tokenizer; + this.tokenizer.options = this.options; + this.tokenizer.initialize(); } /** * Expose Block Rules */ static get rules() { - return { - block, - inline - }; + return Tokenizer.rules; } /** @@ -59,7 +38,7 @@ module.exports = class Lexer { .replace(/\r\n|\r/g, '\n') .replace(/\t/g, ' '); - this.blockTokens(src, this.tokens); + this.blockTokens(src, this.tokens, true); this.inline(this.tokens); @@ -69,347 +48,113 @@ module.exports = class Lexer { /** * Lexing */ - blockTokens(src, tokens, top = true) { + blockTokens(src, tokens = [], top = true) { src = src.replace(/^ +$/gm, ''); - let next, - loose, - cap, - bull, - b, - item, - list, - space, - i, - tag, - l, - isordered, - istask, - ischecked, - lastToken, - addBack, - raw; + let token; while (src) { // newline - if (cap = this.rules.block.newline.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - if (cap[0].length > 1) { - tokens.push({ - type: 'space', - raw - }); + if (token = this.tokenizer.space(this, src, tokens, top)) { + src = src.substring(token.raw.length); + if (token.type) { + tokens.push(token); } + continue; } // code - if (cap = this.rules.block.code.exec(src)) { - lastToken = tokens[tokens.length - 1]; - src = src.substring(cap[0].length); - raw = cap[0]; - // An indented code block cannot interrupt a paragraph. - if (lastToken && lastToken.type === 'paragraph') { - lastToken.text += '\n' + cap[0].trimRight(); - lastToken.raw += '\n' + raw; - } else { - cap = cap[0].replace(/^ {4}/gm, ''); - tokens.push({ - type: 'code', - raw, - codeBlockStyle: 'indented', - text: !this.options.pedantic - ? rtrim(cap, '\n') - : cap - }); - } + if (token = this.tokenizer.code(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // fences - if (cap = this.rules.block.fences.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'code', - raw, - lang: cap[2] ? cap[2].trim() : cap[2], - text: cap[3] || '' - }); + if (token = this.tokenizer.fences(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // heading - if (cap = this.rules.block.heading.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'heading', - raw, - depth: cap[1].length, - text: cap[2] - }); + if (token = this.tokenizer.heading(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // table no leading pipe (gfm) - if (cap = this.rules.block.nptable.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; - - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); - raw = cap[0]; - item.raw = raw; - - l = item.align.length; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells(item.cells[i], item.header.length); - } - - tokens.push(item); - - continue; - } + if (token = this.tokenizer.nptable(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; } // hr - if (cap = this.rules.block.hr.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'hr', - raw - }); + if (token = this.tokenizer.hr(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // blockquote - if (cap = this.rules.block.blockquote.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - - cap = cap[0].replace(/^ *> ?/gm, ''); - - tokens.push({ - type: 'blockquote', - raw, - tokens: this.blockTokens(cap, [], top) - }); - + if (token = this.tokenizer.blockquote(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // list - if (cap = this.rules.block.list.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - bull = cap[2]; - isordered = bull.length > 1; - - list = { - type: 'list', - raw, - ordered: isordered, - start: isordered ? +bull : '', - loose: false, - items: [] - }; - - tokens.push(list); - - // Get each top-level item. - cap = cap[0].match(this.rules.block.item); - - next = false; - - l = cap.length; - for (i = 0; i < l; i++) { - item = cap[i]; - raw = item.trim(); - - // Remove the list item's bullet - // so it is seen as the next token. - space = item.length; - item = item.replace(/^ *([*+-]|\d+\.) */, ''); - - // Outdent whatever the - // list item contains. Hacky. - if (~item.indexOf('\n ')) { - space -= item.length; - item = !this.options.pedantic - ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') - : item.replace(/^ {1,4}/gm, ''); - } - - // Determine whether the next list item belongs here. - // Backpedal if it does not belong in this list. - if (i !== l - 1) { - b = block.bullet.exec(cap[i + 1])[0]; - if (bull.length > 1 ? b.length === 1 - : (b.length > 1 || (this.options.smartLists && b !== bull))) { - addBack = cap.slice(i + 1).join('\n'); - src = addBack + src; - list.raw = list.raw.substring(list.raw.length - addBack.length); - i = l - 1; - } - } - - // Determine whether item is loose or not. - // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ - // for discount behavior. - loose = next || /\n\n(?!\s*$)/.test(item); - if (i !== l - 1) { - next = item.charAt(item.length - 1) === '\n'; - if (!loose) loose = next; - } - - if (loose) { - list.loose = true; - } - - // Check for task list items - istask = /^\[[ xX]\] /.test(item); - ischecked = undefined; - if (istask) { - ischecked = item[1] !== ' '; - item = item.replace(/^\[[ xX]\] +/, ''); - } - - list.items.push({ - raw, - task: istask, - checked: ischecked, - loose: loose, - tokens: this.blockTokens(item, [], false) - }); - } - + if (token = this.tokenizer.list(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // html - if (cap = this.rules.block.html.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: this.options.sanitize - ? 'paragraph' - : 'html', - raw, - pre: !this.options.sanitizer - && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), - text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0] - }); + if (token = this.tokenizer.html(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // def - if (top && (cap = this.rules.block.def.exec(src))) { - src = src.substring(cap[0].length); - if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); - tag = cap[1].toLowerCase().replace(/\s+/g, ' '); - if (!this.tokens.links[tag]) { - this.tokens.links[tag] = { - href: cap[2], - title: cap[3] + if (top && (token = this.tokenizer.def(this, src, tokens, top))) { + src = src.substring(token.raw.length); + if (!this.tokens.links[token.tag]) { + this.tokens.links[token.tag] = { + href: token.href, + title: token.title }; } continue; } // table (gfm) - if (cap = this.rules.block.table.exec(src)) { - item = { - type: 'table', - header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), - align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), - cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] - }; - - if (item.header.length === item.align.length) { - src = src.substring(cap[0].length); - item.raw = cap[0]; - - l = item.align.length; - for (i = 0; i < l; i++) { - if (/^ *-+: *$/.test(item.align[i])) { - item.align[i] = 'right'; - } else if (/^ *:-+: *$/.test(item.align[i])) { - item.align[i] = 'center'; - } else if (/^ *:-+ *$/.test(item.align[i])) { - item.align[i] = 'left'; - } else { - item.align[i] = null; - } - } - - l = item.cells.length; - for (i = 0; i < l; i++) { - item.cells[i] = splitCells( - item.cells[i].replace(/^ *\| *| *\| *$/g, ''), - item.header.length); - } - - tokens.push(item); - - continue; - } + if (token = this.tokenizer.table(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); + continue; } // lheading - if (cap = this.rules.block.lheading.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'heading', - raw, - depth: cap[2].charAt(0) === '=' ? 1 : 2, - text: cap[1] - }); + if (token = this.tokenizer.lheading(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // top-level paragraph - if (top && (cap = this.rules.block.paragraph.exec(src))) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'paragraph', - raw, - text: cap[1].charAt(cap[1].length - 1) === '\n' - ? cap[1].slice(0, -1) - : cap[1] - }); + if (top && (token = this.tokenizer.paragraph(this, src, tokens, top))) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // text - if (cap = this.rules.block.text.exec(src)) { - // Top-level should never reach here. - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'text', - raw, - text: cap[0] - }); + if (token = this.tokenizer.text(this, src, tokens, top)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } @@ -417,6 +162,7 @@ module.exports = class Lexer { const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); if (this.options.silent) { console.error(errMsg); + break; } else { throw new Error(errMsg); } @@ -494,280 +240,91 @@ module.exports = class Lexer { /** * Lexing/Compiling */ - inlineTokens(src, tokens) { - let out = '', - link, - text, - newTokens, - href, - title, - cap, - prevCapZero, - lastParenIndex, - start, - linkLen, - raw; + inlineTokens(src, tokens = []) { + let token; while (src) { // escape - if (cap = this.rules.inline.escape.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - text = escape(cap[1]); - out += text; - tokens.push({ - type: 'escape', - raw, - text - }); + if (token = this.tokenizer.escape(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // tag - if (cap = this.rules.inline.tag.exec(src)) { - if (!this.inLink && /^/i.test(cap[0])) { - this.inLink = false; - } - if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = true; - } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { - this.inRawBlock = false; - } - - src = src.substring(cap[0].length); - raw = cap[0]; - text = this.options.sanitize - ? (this.options.sanitizer - ? this.options.sanitizer(cap[0]) - : escape(cap[0])) - : cap[0]; - tokens.push({ - type: this.options.sanitize - ? 'text' - : 'html', - raw, - text - }); - out += text; + if (token = this.tokenizer.tag(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // link - if (cap = this.rules.inline.link.exec(src)) { - lastParenIndex = findClosingBracket(cap[2], '()'); - if (lastParenIndex > -1) { - start = cap[0].indexOf('!') === 0 ? 5 : 4; - linkLen = start + cap[1].length + lastParenIndex; - cap[2] = cap[2].substring(0, lastParenIndex); - cap[0] = cap[0].substring(0, linkLen).trim(); - cap[3] = ''; - } - src = src.substring(cap[0].length); - raw = cap[0]; - this.inLink = true; - href = cap[2]; - if (this.options.pedantic) { - link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); - - if (link) { - href = link[1]; - title = link[3]; - } else { - title = ''; - } - } else { - title = cap[3] ? cap[3].slice(1, -1) : ''; - } - href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); - out += this.outputLink(cap, { - href: this.escapes(href), - title: this.escapes(title) - }, tokens, raw); - this.inLink = false; + if (token = this.tokenizer.link(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // reflink, nolink - if ((cap = this.rules.inline.reflink.exec(src)) - || (cap = this.rules.inline.nolink.exec(src))) { - src = src.substring(cap[0].length); - raw = cap[0]; - link = (cap[2] || cap[1]).replace(/\s+/g, ' '); - link = this.tokens.links[link.toLowerCase()]; - if (!link || !link.href) { - text = cap[0].charAt(0); - out += text; - tokens.push({ - type: 'text', - raw: text, - text - }); - src = cap[0].substring(1) + src; - continue; - } - this.inLink = true; - out += this.outputLink(cap, link, tokens, raw); - this.inLink = false; + if (token = this.tokenizer.reflink(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // strong - if (cap = this.rules.inline.strong.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - newTokens = tokens ? [] : null; - text = this.inlineTokens(cap[4] || cap[3] || cap[2] || cap[1], newTokens); - - tokens.push({ - type: 'strong', - raw, - text, - tokens: newTokens - }); - out += text; + if (token = this.tokenizer.strong(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // em - if (cap = this.rules.inline.em.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - newTokens = tokens ? [] : null; - text = this.inlineTokens(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1], newTokens); - tokens.push({ - type: 'em', - raw, - text, - tokens: newTokens - }); - out += text; + if (token = this.tokenizer.em(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // code - if (cap = this.rules.inline.code.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - text = escape(cap[2].trim(), true); - tokens.push({ - type: 'codespan', - raw, - text - }); - out += text; + if (token = this.tokenizer.codespan(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // br - if (cap = this.rules.inline.br.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'br', - raw - }); - out += '\n'; + if (token = this.tokenizer.br(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // del (gfm) - if (cap = this.rules.inline.del.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - newTokens = tokens ? [] : null; - text = this.inlineTokens(cap[1], newTokens); - tokens.push({ - type: 'del', - raw, - text, - tokens: newTokens - }); - out += text; + if (token = this.tokenizer.del(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // autolink - if (cap = this.rules.inline.autolink.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - if (cap[2] === '@') { - text = escape(this.options.mangle ? this.mangle(cap[1]) : cap[1]); - href = 'mailto:' + text; - } else { - text = escape(cap[1]); - href = text; - } - tokens.push({ - type: 'link', - raw, - text, - href, - tokens: [ - { - type: 'text', - raw: text, - text - } - ] - }); - out += text; + if (token = this.tokenizer.autolink(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // url (gfm) - if (!this.inLink && (cap = this.rules.inline.url.exec(src))) { - if (cap[2] === '@') { - text = escape(this.options.mangle ? this.mangle(cap[0]) : cap[0]); - href = 'mailto:' + text; - } else { - // do extended autolink path validation - do { - prevCapZero = cap[0]; - cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; - } while (prevCapZero !== cap[0]); - text = escape(cap[0]); - if (cap[1] === 'www.') { - href = 'http://' + text; - } else { - href = text; - } - } - src = src.substring(cap[0].length); - raw = cap[0]; - tokens.push({ - type: 'link', - raw, - text, - href, - tokens: [ - { - type: 'text', - raw: text, - text - } - ] - }); - out += text; + if (token = this.tokenizer.url(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } // text - if (cap = this.rules.inline.text.exec(src)) { - src = src.substring(cap[0].length); - raw = cap[0]; - if (this.inRawBlock) { - text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]; - } else { - text = escape(this.options.smartypants ? this.smartypants(cap[0]) : cap[0]); - } - tokens.push({ - type: 'text', - raw, - text - }); - out += text; + if (token = this.tokenizer.inlineText(this, src, tokens)) { + src = src.substring(token.raw.length); + tokens.push(token); continue; } @@ -775,89 +332,13 @@ module.exports = class Lexer { const errMsg = 'Infinite loop on byte: ' + src.charCodeAt(0); if (this.options.silent) { console.error(errMsg); + break; } else { throw new Error(errMsg); } } } - return out; - } - - escapes(text) { - return text ? text.replace(inline._escapes, '$1') : text; - } - - /** - * tokenize Link - */ - outputLink(cap, link, tokens, raw) { - const href = link.href; - const title = link.title ? escape(link.title) : null; - const newTokens = tokens ? [] : null; - - if (cap[0].charAt(0) !== '!') { - const text = this.inlineTokens(cap[1], newTokens); - tokens.push({ - type: 'link', - raw, - text, - href, - title, - tokens: newTokens - }); - return text; - } else { - const text = escape(cap[1]); - tokens.push({ - type: 'image', - raw, - text, - href, - title - }); - return text; - } - } - - /** - * Smartypants Transformations - */ - smartypants(text) { - return text - // em-dashes - .replace(/---/g, '\u2014') - // en-dashes - .replace(/--/g, '\u2013') - // opening singles - .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') - // closing singles & apostrophes - .replace(/'/g, '\u2019') - // opening doubles - .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') - // closing doubles - .replace(/"/g, '\u201d') - // ellipses - .replace(/\.{3}/g, '\u2026'); - } - - /** - * Mangle Links - */ - mangle(text) { - let out = '', - i, - ch; - - const l = text.length; - for (i = 0; i < l; i++) { - ch = text.charCodeAt(i); - if (Math.random() > 0.5) { - ch = 'x' + ch.toString(16); - } - out += '' + ch + ';'; - } - - return out; + return tokens; } }; diff --git a/src/Tokenizer.js b/src/Tokenizer.js new file mode 100644 index 0000000000..c8ca37f404 --- /dev/null +++ b/src/Tokenizer.js @@ -0,0 +1,663 @@ +const { defaults } = require('./defaults.js'); +const { + rtrim, + splitCells, + escape, + findClosingBracket +} = require('./helpers.js'); +const { block, inline } = require('./rules.js'); + +function outputLink(cap, link, tokens, raw, lexer) { + const href = link.href; + const title = link.title ? escape(link.title) : null; + + if (cap[0].charAt(0) !== '!') { + return { + type: 'link', + raw, + href, + title, + tokens: lexer.inlineTokens(cap[1]) + }; + } else { + return { + type: 'image', + raw, + text: escape(cap[1]), + href, + title + }; + } +} + +/** + * Tokenizer + */ +module.exports = class Tokenizer { + constructor(options) { + this.options = options || defaults; + this.initialize(); + } + + initialize() { + this.inLink = false; + this.inRawBlock = false; + + this.rules = { + block: block.normal, + inline: inline.normal + }; + + if (this.options.pedantic) { + this.rules.block = block.pedantic; + this.rules.inline = inline.pedantic; + } else if (this.options.gfm) { + this.rules.block = block.gfm; + if (this.options.breaks) { + this.rules.inline = inline.breaks; + } else { + this.rules.inline = inline.gfm; + } + } + } + + /** + * Expose Block Rules + */ + static get rules() { + return { + block, + inline + }; + } + + space(lexer, src, tokens, top) { + const cap = this.rules.block.newline.exec(src); + if (cap) { + if (cap[0].length > 1) { + return { + type: 'space', + raw: cap[0] + }; + } + return { raw: '\n' }; + } + } + + code(lexer, src, tokens, top) { + const cap = this.rules.block.code.exec(src); + if (cap) { + const lastToken = tokens[tokens.length - 1]; + // An indented code block cannot interrupt a paragraph. + if (lastToken && lastToken.type === 'paragraph') { + tokens.pop(); + lastToken.text += '\n' + cap[0].trimRight(); + lastToken.raw += '\n' + cap[0]; + return lastToken; + } else { + const text = cap[0].replace(/^ {4}/gm, ''); + return { + type: 'code', + raw: cap[0], + codeBlockStyle: 'indented', + text: !this.options.pedantic + ? rtrim(text, '\n') + : text + }; + } + } + } + + fences(lexer, src, tokens, top) { + const cap = this.rules.block.fences.exec(src); + if (cap) { + return { + type: 'code', + raw: cap[0], + lang: cap[2] ? cap[2].trim() : cap[2], + text: cap[3] || '' + }; + } + } + + heading(lexer, src, tokens, top) { + const cap = this.rules.block.heading.exec(src); + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[1].length, + text: cap[2] + }; + } + } + + nptable(lexer, src, tokens, top) { + const cap = this.rules.block.nptable.exec(src); + if (cap) { + const item = { + type: 'table', + header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [], + raw: cap[0] + }; + + if (item.header.length === item.align.length) { + let l = item.align.length; + let i; + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + for (i = 0; i < l; i++) { + item.cells[i] = splitCells(item.cells[i], item.header.length); + } + + return item; + } + } + } + + hr(lexer, src, tokens, top) { + const cap = this.rules.block.hr.exec(src); + if (cap) { + return { + type: 'hr', + raw: cap[0] + }; + } + } + + blockquote(lexer, src, tokens, top) { + const cap = this.rules.block.blockquote.exec(src); + if (cap) { + const text = cap[0].replace(/^ *> ?/gm, ''); + + return { + type: 'blockquote', + raw: cap[0], + tokens: lexer.blockTokens(text, [], top) + }; + } + } + + list(lexer, src, tokens, top) { + const cap = this.rules.block.list.exec(src); + if (cap) { + let raw = cap[0]; + const bull = cap[2]; + const isordered = bull.length > 1; + + const list = { + type: 'list', + raw, + ordered: isordered, + start: isordered ? +bull : '', + loose: false, + items: [] + }; + + // Get each top-level item. + const itemMatch = cap[0].match(this.rules.block.item); + + let next = false, + item, + space, + b, + addBack, + loose, + istask, + ischecked; + + const l = itemMatch.length; + for (let i = 0; i < l; i++) { + item = itemMatch[i]; + raw = item; + + // Remove the list item's bullet + // so it is seen as the next token. + space = item.length; + item = item.replace(/^ *([*+-]|\d+\.) */, ''); + + // Outdent whatever the + // list item contains. Hacky. + if (~item.indexOf('\n ')) { + space -= item.length; + item = !this.options.pedantic + ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') + : item.replace(/^ {1,4}/gm, ''); + } + + // Determine whether the next list item belongs here. + // Backpedal if it does not belong in this list. + if (i !== l - 1) { + b = this.rules.block.bullet.exec(itemMatch[i + 1])[0]; + if (bull.length > 1 ? b.length === 1 + : (b.length > 1 || (this.options.smartLists && b !== bull))) { + addBack = itemMatch.slice(i + 1).join('\n'); + list.raw = list.raw.substring(0, list.raw.length - addBack.length); + i = l - 1; + } + } + + // Determine whether item is loose or not. + // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/ + // for discount behavior. + loose = next || /\n\n(?!\s*$)/.test(item); + if (i !== l - 1) { + next = item.charAt(item.length - 1) === '\n'; + if (!loose) loose = next; + } + + if (loose) { + list.loose = true; + } + + // Check for task list items + istask = /^\[[ xX]\] /.test(item); + ischecked = undefined; + if (istask) { + ischecked = item[1] !== ' '; + item = item.replace(/^\[[ xX]\] +/, ''); + } + + list.items.push({ + raw, + task: istask, + checked: ischecked, + loose: loose, + tokens: lexer.blockTokens(item, [], false) + }); + } + + return list; + } + } + + html(lexer, src, tokens, top) { + const cap = this.rules.block.html.exec(src); + if (cap) { + return { + type: this.options.sanitize + ? 'paragraph' + : 'html', + raw: cap[0], + pre: !this.options.sanitizer + && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), + text: this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0] + }; + } + } + + def(lexer, src, tokens, top) { + const cap = this.rules.block.def.exec(src); + if (cap) { + if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1); + const tag = cap[1].toLowerCase().replace(/\s+/g, ' '); + return { + tag, + raw: cap[0], + href: cap[2], + title: cap[3] + }; + } + } + + table(lexer, src, tokens, top) { + const cap = this.rules.block.table.exec(src); + if (cap) { + const item = { + type: 'table', + header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')), + align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), + cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : [] + }; + + if (item.header.length === item.align.length) { + item.raw = cap[0]; + + let l = item.align.length; + let i; + for (i = 0; i < l; i++) { + if (/^ *-+: *$/.test(item.align[i])) { + item.align[i] = 'right'; + } else if (/^ *:-+: *$/.test(item.align[i])) { + item.align[i] = 'center'; + } else if (/^ *:-+ *$/.test(item.align[i])) { + item.align[i] = 'left'; + } else { + item.align[i] = null; + } + } + + l = item.cells.length; + for (i = 0; i < l; i++) { + item.cells[i] = splitCells( + item.cells[i].replace(/^ *\| *| *\| *$/g, ''), + item.header.length); + } + + return item; + } + } + } + + lheading(lexer, src, tokens, top) { + const cap = this.rules.block.lheading.exec(src); + if (cap) { + return { + type: 'heading', + raw: cap[0], + depth: cap[2].charAt(0) === '=' ? 1 : 2, + text: cap[1] + }; + } + } + + paragraph(lexer, src, tokens, top) { + const cap = this.rules.block.paragraph.exec(src); + if (cap) { + return { + type: 'paragraph', + raw: cap[0], + text: cap[1].charAt(cap[1].length - 1) === '\n' + ? cap[1].slice(0, -1) + : cap[1] + }; + } + } + + text(lexer, src, tokens, top) { + const cap = this.rules.block.text.exec(src); + if (cap) { + return { + type: 'text', + raw: cap[0], + text: cap[0] + }; + } + } + + escape(lexer, src, tokens, top) { + const cap = this.rules.inline.escape.exec(src); + if (cap) { + return { + type: 'escape', + raw: cap[0], + text: escape(cap[1]) + }; + } + } + + tag(lexer, src, tokens, top) { + const cap = this.rules.inline.tag.exec(src); + if (cap) { + if (!this.inLink && /^/i.test(cap[0])) { + this.inLink = false; + } + if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.inRawBlock = true; + } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) { + this.inRawBlock = false; + } + + return { + type: this.options.sanitize + ? 'text' + : 'html', + raw: cap[0], + text: this.options.sanitize + ? (this.options.sanitizer + ? this.options.sanitizer(cap[0]) + : escape(cap[0])) + : cap[0] + }; + } + } + + link(lexer, src, tokens, top) { + const cap = this.rules.inline.link.exec(src); + if (cap) { + const lastParenIndex = findClosingBracket(cap[2], '()'); + if (lastParenIndex > -1) { + const start = cap[0].indexOf('!') === 0 ? 5 : 4; + const linkLen = start + cap[1].length + lastParenIndex; + cap[2] = cap[2].substring(0, lastParenIndex); + cap[0] = cap[0].substring(0, linkLen).trim(); + cap[3] = ''; + } + this.inLink = true; + let href = cap[2]; + let title = ''; + if (this.options.pedantic) { + const link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href); + + if (link) { + href = link[1]; + title = link[3]; + } else { + title = ''; + } + } else { + title = cap[3] ? cap[3].slice(1, -1) : ''; + } + href = href.trim().replace(/^<([\s\S]*)>$/, '$1'); + const token = outputLink(cap, { + href: href ? href.replace(this.rules.inline._escapes, '$1') : href, + title: title ? title.replace(this.rules.inline._escapes, '$1') : title + }, tokens, cap[0], lexer); + this.inLink = false; + return token; + } + } + + reflink(lexer, src, tokens, top) { + let cap; + if ((cap = this.rules.inline.reflink.exec(src)) + || (cap = this.rules.inline.nolink.exec(src))) { + let link = (cap[2] || cap[1]).replace(/\s+/g, ' '); + link = lexer.tokens.links[link.toLowerCase()]; + if (!link || !link.href) { + const text = cap[0].charAt(0); + return { + type: 'text', + raw: text, + text + }; + } + this.inLink = true; + const token = outputLink(cap, link, tokens, cap[0], lexer); + this.inLink = false; + return token; + } + } + + strong(lexer, src, tokens, top) { + const cap = this.rules.inline.strong.exec(src); + if (cap) { + return { + type: 'strong', + raw: cap[0], + tokens: lexer.inlineTokens(cap[4] || cap[3] || cap[2] || cap[1]) + }; + } + } + + em(lexer, src, tokens, top) { + const cap = this.rules.inline.em.exec(src); + if (cap) { + return { + type: 'em', + raw: cap[0], + tokens: lexer.inlineTokens(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]) + }; + } + } + + codespan(lexer, src, tokens, top) { + const cap = this.rules.inline.code.exec(src); + if (cap) { + return { + type: 'codespan', + raw: cap[0], + text: escape(cap[2].trim(), true) + }; + } + } + + br(lexer, src, tokens, top) { + const cap = this.rules.inline.br.exec(src); + if (cap) { + return { + type: 'br', + raw: cap[0] + }; + } + } + + del(lexer, src, tokens, top) { + const cap = this.rules.inline.del.exec(src); + if (cap) { + return { + type: 'del', + raw: cap[0], + tokens: lexer.inlineTokens(cap[1]) + }; + } + } + + autolink(lexer, src, tokens, top) { + const cap = this.rules.inline.autolink.exec(src); + if (cap) { + let text, href; + if (cap[2] === '@') { + text = escape(this.options.mangle ? this.mangle(cap[1]) : cap[1]); + href = 'mailto:' + text; + } else { + text = escape(cap[1]); + href = text; + } + + return { + type: 'link', + raw: cap[0], + text, + href, + tokens: [ + { + type: 'text', + raw: text, + text + } + ] + }; + } + } + + url(lexer, src, tokens, top) { + let cap; + if (!this.inLink && (cap = this.rules.inline.url.exec(src))) { + let text, href; + if (cap[2] === '@') { + text = escape(this.options.mangle ? this.mangle(cap[0]) : cap[0]); + href = 'mailto:' + text; + } else { + // do extended autolink path validation + let prevCapZero; + do { + prevCapZero = cap[0]; + cap[0] = this.rules.inline._backpedal.exec(cap[0])[0]; + } while (prevCapZero !== cap[0]); + text = escape(cap[0]); + if (cap[1] === 'www.') { + href = 'http://' + text; + } else { + href = text; + } + } + return { + type: 'link', + raw: cap[0], + text, + href, + tokens: [ + { + type: 'text', + raw: text, + text + } + ] + }; + } + } + + inlineText(lexer, src, tokens, top) { + const cap = this.rules.inline.text.exec(src); + if (cap) { + let text; + if (this.inRawBlock) { + text = this.options.sanitize ? (this.options.sanitizer ? this.options.sanitizer(cap[0]) : escape(cap[0])) : cap[0]; + } else { + text = escape(this.options.smartypants ? this.smartypants(cap[0]) : cap[0]); + } + return { + type: 'text', + raw: cap[0], + text + }; + } + } + + /** + * Smartypants Transformations + */ + smartypants(text) { + return text + // em-dashes + .replace(/---/g, '\u2014') + // en-dashes + .replace(/--/g, '\u2013') + // opening singles + .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018') + // closing singles & apostrophes + .replace(/'/g, '\u2019') + // opening doubles + .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c') + // closing doubles + .replace(/"/g, '\u201d') + // ellipses + .replace(/\.{3}/g, '\u2026'); + } + + /** + * Mangle Links + */ + mangle(text) { + let out = '', + i, + ch; + + const l = text.length; + for (i = 0; i < l; i++) { + ch = text.charCodeAt(i); + if (Math.random() > 0.5) { + ch = 'x' + ch.toString(16); + } + out += '' + ch + ';'; + } + + return out; + } +}; diff --git a/src/defaults.js b/src/defaults.js index 8d0be95580..0153bb4334 100644 --- a/src/defaults.js +++ b/src/defaults.js @@ -15,6 +15,7 @@ function getDefaults() { silent: false, smartLists: false, smartypants: false, + tokenizer: null, xhtml: false }; } diff --git a/src/marked.js b/src/marked.js index b575eec23d..8a77ddcef6 100644 --- a/src/marked.js +++ b/src/marked.js @@ -1,5 +1,6 @@ const Lexer = require('./Lexer.js'); const Parser = require('./Parser.js'); +const Tokenizer = require('./Tokenizer.js'); const Renderer = require('./Renderer.js'); const TextRenderer = require('./TextRenderer.js'); const Slugger = require('./Slugger.js'); @@ -139,6 +140,8 @@ marked.TextRenderer = TextRenderer; marked.Lexer = Lexer; marked.lexer = Lexer.lex; +marked.Tokenizer = Tokenizer; + marked.Slugger = Slugger; marked.parse = marked; diff --git a/test/unit/Lexer-spec.js b/test/unit/Lexer-spec.js index 0d334358a0..dd0bdfef2c 100644 --- a/test/unit/Lexer-spec.js +++ b/test/unit/Lexer-spec.js @@ -9,15 +9,12 @@ function expectTokens({ md, options, tokens = [], links = {} }) { expect(actual).toEqual(expected); } -function expectInlineTokens({ md, options, output = jasmine.any(String), tokens = jasmine.any(Array), links = {} }) { +function expectInlineTokens({ md, options, tokens = jasmine.any(Array), links = {} }) { const lexer = new Lexer(options); lexer.tokens.links = links; const outTokens = []; - const outOutput = lexer.inlineTokens(md, outTokens); - expect({ - output: outOutput, - tokens: outTokens - }).toEqual({ output, tokens }); + lexer.inlineTokens(md, outTokens); + expect(outTokens).toEqual(tokens); } function expectInline({ token, options, tokens }) { @@ -321,7 +318,7 @@ a | b }] }, { - raw: '- item 2', + raw: '- item 2\n', task: false, checked: undefined, loose: false, @@ -355,7 +352,7 @@ a | b raw: '1. item 1' }), jasmine.objectContaining({ - raw: '2. item 2' + raw: '2. item 2\n' }) ] }) @@ -380,7 +377,7 @@ a | b raw: '2. item 1' }), jasmine.objectContaining({ - raw: '3. item 2' + raw: '3. item 2\n' }) ] }) @@ -422,7 +419,7 @@ a | b checked: false }), jasmine.objectContaining({ - raw: '- [x] item 2', + raw: '- [x] item 2\n', task: true, checked: true }) @@ -504,7 +501,7 @@ a | b }); describe('inline', () => { - describe('tokens', () => { + describe('inline', () => { it('paragraph', () => { expectInline({ token: { type: 'paragraph', text: 'text' }, @@ -563,11 +560,10 @@ a | b }); }); - describe('output', () => { + describe('inlineTokens', () => { it('escape', () => { expectInlineTokens({ md: '\\>', - output: '>', tokens: [ { type: 'escape', raw: '\\>', text: '>' } ] @@ -577,7 +573,6 @@ a | b it('html', () => { expectInlineTokens({ md: '
latext code
'+(n?e:X(e,!0))+"
\n":""+(n?e:X(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+Y(e.message+"",!0)+"";throw e}}return re.options=re.setOptions=function(e){return Q(re.defaults,e),te(re.defaults),re},re.getDefaults=ee,re.defaults=ne,re.Parser=K,re.parser=K.parse,re.Renderer=G,re.TextRenderer=M,re.Lexer=U,re.lexer=U.lex,re.Slugger=V,re.parse=re}); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).marked=t()}(this,function(){"use strict";function r(e,t){for(var n=0;n
'+(n?e:H(e,!0))+"
\n":""+(n?e:H(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+re(e.message+"",!0)+"";throw e}}return ae.options=ae.setOptions=function(e){return te(ae.defaults,e),se(ae.defaults),ae},ae.getDefaults=ie,ae.defaults=le,ae.Parser=ee,ae.parser=ee.parse,ae.Renderer=J,ae.TextRenderer=K,ae.Lexer=G,ae.lexer=G.lex,ae.Tokenizer=N,ae.Slugger=Q,ae.parse=ae}); \ No newline at end of file From 4947ce507859b0475646793e87ce6cd0e4a04141 Mon Sep 17 00:00:00 2001 From: Tony Brix
Already linked: http://example.com/.
+Already linked: http://example.com/.
+Already linked: http://example.com/.
+ +Already linked: http://example.com/.
+ +Already linked: http://example.com/.
Already linked: http://example.com/.
+ +Already linked: http://example.com/.
diff --git a/test/specs/new/double_link.md b/test/specs/new/double_link.md index 50216736c1..895d22f6d9 100644 --- a/test/specs/new/double_link.md +++ b/test/specs/new/double_link.md @@ -2,4 +2,16 @@ Already linked: [http://example.com/](http://example.com/). +Already linked: http://example.com/. + Already linked: **http://example.com/**. + +Already linked: *http://example.com/*. + +Already linked: ~~http://example.com/~~. + +Already linked: [http://example.com/]. + +Already linked: [http://example.com/][]. + +[http://example.com/]: http://example.com/ diff --git a/test/unit/Lexer-spec.js b/test/unit/Lexer-spec.js index dd0bdfef2c..d8a802061b 100644 --- a/test/unit/Lexer-spec.js +++ b/test/unit/Lexer-spec.js @@ -276,6 +276,7 @@ a | b { type: 'blockquote', raw: '> blockquote', + text: 'blockquote', tokens: [{ type: 'paragraph', raw: 'blockquote', @@ -310,6 +311,7 @@ a | b task: false, checked: undefined, loose: false, + text: 'item 1', tokens: [{ type: 'text', raw: 'item 1', @@ -322,6 +324,7 @@ a | b task: false, checked: undefined, loose: false, + text: 'item 2\n', tokens: [{ type: 'text', raw: 'item 2', @@ -574,9 +577,9 @@ a | b expectInlineTokens({ md: ''+(n?e:H(e,!0))+"
\n":""+(n?e:H(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+re(e.message+"",!0)+"";throw e}}return ae.options=ae.setOptions=function(e){return te(ae.defaults,e),se(ae.defaults),ae},ae.getDefaults=ie,ae.defaults=le,ae.Parser=ee,ae.parser=ee.parse,ae.Renderer=J,ae.TextRenderer=K,ae.Lexer=G,ae.lexer=G.lex,ae.Tokenizer=N,ae.Slugger=Q,ae.parse=ae}); +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).marked=t()}(this,function(){"use strict";function r(e,t){for(var n=0;n
'+(n?e:H(e,!0))+"
\n":""+(n?e:H(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+re(e.message+"",!0)+"";throw e}}return ae.options=ae.setOptions=function(e){return te(ae.defaults,e),se(ae.defaults),ae},ae.getDefaults=ie,ae.defaults=le,ae.Parser=ee,ae.parser=ee.parse,ae.Renderer=J,ae.TextRenderer=K,ae.Lexer=G,ae.lexer=G.lex,ae.Tokenizer=N,ae.Slugger=Q,ae.parse=ae}); \ No newline at end of file From 4117ba271f7adb4d70e5be95ce048deaababddd9 Mon Sep 17 00:00:00 2001 From: Tony Brix
'+(n?e:H(e,!0))+"
\n":""+(n?e:H(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+re(e.message+"",!0)+"";throw e}}return ae.options=ae.setOptions=function(e){return te(ae.defaults,e),se(ae.defaults),ae},ae.getDefaults=ie,ae.defaults=le,ae.Parser=ee,ae.parser=ee.parse,ae.Renderer=J,ae.TextRenderer=K,ae.Lexer=G,ae.lexer=G.lex,ae.Tokenizer=N,ae.Slugger=Q,ae.parse=ae}); \ No newline at end of file +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e=e||self).marked=t()}(this,function(){"use strict";function s(e,t){for(var n=0;n
'+(n?e:Q(e,!0))+"
\n":""+(n?e:Q(e,!0))+"
"},t.blockquote=function(e){return"\n"+e+"\n"},t.html=function(e){return e},t.heading=function(e,t,n,r){return this.options.headerIds?"
"+e+"
\n"},t.table=function(e,t){return""+e+"
"},t.br=function(){return this.options.xhtml?""+le(e.message+"",!0)+"";throw e}}return he.options=he.setOptions=function(e){return ie(he.defaults,e),oe(he.defaults),he},he.getDefaults=ae,he.defaults=ce,he.Parser=re,he.parser=re.parse,he.Renderer=W,he.TextRenderer=Y,he.Lexer=H,he.lexer=H.lex,he.Tokenizer=E,he.Slugger=ee,he.parse=he}); \ No newline at end of file From 0f0586094be32b196370bd5fe72f160acd2d369b Mon Sep 17 00:00:00 2001 From: Tony Brix