Created
October 12, 2016 09:08
-
-
Save swcho/c6dda4f555e2e20ec549a23d66d4774c to your computer and use it in GitHub Desktop.
marked.js for table width generation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* marked - a markdown parser | |
* Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) | |
* https://github.com/chjj/marked | |
*/ | |
;(function() { | |
/** | |
* Block-Level Grammar | |
*/ | |
var block = { | |
newline: /^\n+/, | |
code: /^( {4}[^\n]+\n*)+/, | |
fences: noop, | |
hr: /^( *[-*_]){3,} *(?:\n+|$)/, | |
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, | |
nptable: noop, | |
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, | |
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, | |
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, | |
html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/, | |
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, | |
table: noop, | |
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/, | |
text: /^[^\n]+/ | |
}; | |
block.bullet = /(?:[*+-]|\d+\.)/; | |
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/; | |
block.item = replace(block.item, 'gm') | |
(/bull/g, block.bullet) | |
(); | |
block.list = replace(block.list) | |
(/bull/g, block.bullet) | |
('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))') | |
('def', '\\n+(?=' + block.def.source + ')') | |
(); | |
block.blockquote = replace(block.blockquote) | |
('def', block.def) | |
(); | |
block._tag = '(?!(?:' | |
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code' | |
+ '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo' | |
+ '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b'; | |
block.html = replace(block.html) | |
('comment', /<!--[\s\S]*?-->/) | |
('closed', /<(tag)[\s\S]+?<\/\1>/) | |
('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/) | |
(/tag/g, block._tag) | |
(); | |
block.paragraph = replace(block.paragraph) | |
('hr', block.hr) | |
('heading', block.heading) | |
('lheading', block.lheading) | |
('blockquote', block.blockquote) | |
('tag', '<' + block._tag) | |
('def', block.def) | |
(); | |
/** | |
* Normal Block Grammar | |
*/ | |
block.normal = merge({}, block); | |
/** | |
* GFM Block Grammar | |
*/ | |
block.gfm = merge({}, block.normal, { | |
fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/, | |
paragraph: /^/, | |
heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/ | |
}); | |
block.gfm.paragraph = replace(block.paragraph) | |
('(?!', '(?!' | |
+ block.gfm.fences.source.replace('\\1', '\\2') + '|' | |
+ block.list.source.replace('\\1', '\\3') + '|') | |
(); | |
/** | |
* GFM + Tables Block Grammar | |
*/ | |
block.tables = merge({}, block.gfm, { | |
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/, | |
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/ | |
}); | |
/** | |
* Block Lexer | |
*/ | |
function Lexer(options) { | |
this.tokens = []; | |
this.tokens.links = {}; | |
this.options = options || marked.defaults; | |
this.rules = block.normal; | |
if (this.options.gfm) { | |
if (this.options.tables) { | |
this.rules = block.tables; | |
} else { | |
this.rules = block.gfm; | |
} | |
} | |
} | |
/** | |
* Expose Block Rules | |
*/ | |
Lexer.rules = block; | |
/** | |
* Static Lex Method | |
*/ | |
Lexer.lex = function(src, options) { | |
var lexer = new Lexer(options); | |
return lexer.lex(src); | |
}; | |
/** | |
* Preprocessing | |
*/ | |
Lexer.prototype.lex = function(src) { | |
src = src | |
.replace(/\r\n|\r/g, '\n') | |
.replace(/\t/g, ' ') | |
.replace(/\u00a0/g, ' ') | |
.replace(/\u2424/g, '\n'); | |
return this.token(src, true); | |
}; | |
/** | |
* Lexing | |
*/ | |
Lexer.prototype.token = function(src, top, bq) { | |
var src = src.replace(/^ +$/gm, '') | |
, next | |
, loose | |
, cap | |
, bull | |
, b | |
, item | |
, space | |
, i | |
, l; | |
while (src) { | |
// newline | |
if (cap = this.rules.newline.exec(src)) { | |
src = src.substring(cap[0].length); | |
if (cap[0].length > 1) { | |
this.tokens.push({ | |
type: 'space' | |
}); | |
} | |
} | |
// code | |
if (cap = this.rules.code.exec(src)) { | |
src = src.substring(cap[0].length); | |
cap = cap[0].replace(/^ {4}/gm, ''); | |
this.tokens.push({ | |
type: 'code', | |
text: !this.options.pedantic | |
? cap.replace(/\n+$/, '') | |
: cap | |
}); | |
continue; | |
} | |
// fences (gfm) | |
if (cap = this.rules.fences.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'code', | |
lang: cap[2], | |
text: cap[3] || '' | |
}); | |
continue; | |
} | |
// heading | |
if (cap = this.rules.heading.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'heading', | |
depth: cap[1].length, | |
text: cap[2] | |
}); | |
continue; | |
} | |
// table no leading pipe (gfm) | |
if (top && (cap = this.rules.nptable.exec(src))) { | |
src = src.substring(cap[0].length); | |
item = { | |
type: 'table', | |
header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), | |
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), | |
cells: cap[3].replace(/\n$/, '').split('\n') | |
}; | |
console.log('markted', item); | |
for (i = 0; i < item.align.length; 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; | |
} | |
} | |
for (i = 0; i < item.cells.length; i++) { | |
item.cells[i] = item.cells[i].split(/ *\| */); | |
} | |
this.tokens.push(item); | |
continue; | |
} | |
// lheading | |
if (cap = this.rules.lheading.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'heading', | |
depth: cap[2] === '=' ? 1 : 2, | |
text: cap[1] | |
}); | |
continue; | |
} | |
// hr | |
if (cap = this.rules.hr.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'hr' | |
}); | |
continue; | |
} | |
// blockquote | |
if (cap = this.rules.blockquote.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'blockquote_start' | |
}); | |
cap = cap[0].replace(/^ *> ?/gm, ''); | |
// Pass `top` to keep the current | |
// "toplevel" state. This is exactly | |
// how markdown.pl works. | |
this.token(cap, top, true); | |
this.tokens.push({ | |
type: 'blockquote_end' | |
}); | |
continue; | |
} | |
// list | |
if (cap = this.rules.list.exec(src)) { | |
src = src.substring(cap[0].length); | |
bull = cap[2]; | |
this.tokens.push({ | |
type: 'list_start', | |
ordered: bull.length > 1 | |
}); | |
// Get each top-level item. | |
cap = cap[0].match(this.rules.item); | |
next = false; | |
l = cap.length; | |
i = 0; | |
for (; i < l; i++) { | |
item = cap[i]; | |
// 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 (this.options.smartLists && i !== l - 1) { | |
b = block.bullet.exec(cap[i + 1])[0]; | |
if (bull !== b && !(bull.length > 1 && b.length > 1)) { | |
src = cap.slice(i + 1).join('\n') + src; | |
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; | |
} | |
this.tokens.push({ | |
type: loose | |
? 'loose_item_start' | |
: 'list_item_start' | |
}); | |
// Recurse. | |
this.token(item, false, bq); | |
this.tokens.push({ | |
type: 'list_item_end' | |
}); | |
} | |
this.tokens.push({ | |
type: 'list_end' | |
}); | |
continue; | |
} | |
// html | |
if (cap = this.rules.html.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: this.options.sanitize | |
? 'paragraph' | |
: 'html', | |
pre: !this.options.sanitizer | |
&& (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'), | |
text: cap[0] | |
}); | |
continue; | |
} | |
// def | |
if ((!bq && top) && (cap = this.rules.def.exec(src))) { | |
src = src.substring(cap[0].length); | |
this.tokens.links[cap[1].toLowerCase()] = { | |
href: cap[2], | |
title: cap[3] | |
}; | |
continue; | |
} | |
// table (gfm) | |
if (top && (cap = this.rules.table.exec(src))) { | |
src = src.substring(cap[0].length); | |
item = { | |
type: 'table', | |
header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */), | |
align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */), | |
cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n'), | |
width: [] | |
}; | |
var widthSum = 0; | |
var width; | |
for (i = 0; i < item.align.length; i++) { | |
width = item.align[i].split('-').length - 1; | |
widthSum += width; | |
item.width[i] = width; | |
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; | |
} | |
} | |
for (i = 0; i < item.width.length; i++) { | |
item.width[i] = item.width[i] / widthSum * 100; | |
} | |
for (i = 0; i < item.cells.length; i++) { | |
item.cells[i] = item.cells[i] | |
.replace(/^ *\| *| *\| *$/g, '') | |
.split(/ *\| */); | |
} | |
this.tokens.push(item); | |
continue; | |
} | |
// top-level paragraph | |
if (top && (cap = this.rules.paragraph.exec(src))) { | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'paragraph', | |
text: cap[1].charAt(cap[1].length - 1) === '\n' | |
? cap[1].slice(0, -1) | |
: cap[1] | |
}); | |
continue; | |
} | |
// text | |
if (cap = this.rules.text.exec(src)) { | |
// Top-level should never reach here. | |
src = src.substring(cap[0].length); | |
this.tokens.push({ | |
type: 'text', | |
text: cap[0] | |
}); | |
continue; | |
} | |
if (src) { | |
throw new | |
Error('Infinite loop on byte: ' + src.charCodeAt(0)); | |
} | |
} | |
return this.tokens; | |
}; | |
/** | |
* Inline-Level Grammar | |
*/ | |
var inline = { | |
escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, | |
autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, | |
url: noop, | |
tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, | |
link: /^!?\[(inside)\]\(href\)/, | |
reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, | |
nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, | |
strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/, | |
em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/, | |
code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/, | |
br: /^ {2,}\n(?!\s*$)/, | |
del: noop, | |
text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/ | |
}; | |
inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/; | |
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/; | |
inline.link = replace(inline.link) | |
('inside', inline._inside) | |
('href', inline._href) | |
(); | |
inline.reflink = replace(inline.reflink) | |
('inside', inline._inside) | |
(); | |
/** | |
* Normal Inline Grammar | |
*/ | |
inline.normal = merge({}, inline); | |
/** | |
* Pedantic Inline Grammar | |
*/ | |
inline.pedantic = merge({}, inline.normal, { | |
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/, | |
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/ | |
}); | |
/** | |
* GFM Inline Grammar | |
*/ | |
inline.gfm = merge({}, inline.normal, { | |
escape: replace(inline.escape)('])', '~|])')(), | |
url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/, | |
del: /^~~(?=\S)([\s\S]*?\S)~~/, | |
text: replace(inline.text) | |
(']|', '~]|') | |
('|', '|https?://|') | |
() | |
}); | |
/** | |
* GFM + Line Breaks Inline Grammar | |
*/ | |
inline.breaks = merge({}, inline.gfm, { | |
br: replace(inline.br)('{2,}', '*')(), | |
text: replace(inline.gfm.text)('{2,}', '*')() | |
}); | |
/** | |
* Inline Lexer & Compiler | |
*/ | |
function InlineLexer(links, options) { | |
this.options = options || marked.defaults; | |
this.links = links; | |
this.rules = inline.normal; | |
this.renderer = this.options.renderer || new Renderer; | |
this.renderer.options = this.options; | |
if (!this.links) { | |
throw new | |
Error('Tokens array requires a `links` property.'); | |
} | |
if (this.options.gfm) { | |
if (this.options.breaks) { | |
this.rules = inline.breaks; | |
} else { | |
this.rules = inline.gfm; | |
} | |
} else if (this.options.pedantic) { | |
this.rules = inline.pedantic; | |
} | |
} | |
/** | |
* Expose Inline Rules | |
*/ | |
InlineLexer.rules = inline; | |
/** | |
* Static Lexing/Compiling Method | |
*/ | |
InlineLexer.output = function(src, links, options) { | |
var inline = new InlineLexer(links, options); | |
return inline.output(src); | |
}; | |
/** | |
* Lexing/Compiling | |
*/ | |
InlineLexer.prototype.output = function(src) { | |
var out = '' | |
, link | |
, text | |
, href | |
, cap; | |
while (src) { | |
// escape | |
if (cap = this.rules.escape.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += cap[1]; | |
continue; | |
} | |
// autolink | |
if (cap = this.rules.autolink.exec(src)) { | |
src = src.substring(cap[0].length); | |
if (cap[2] === '@') { | |
text = cap[1].charAt(6) === ':' | |
? this.mangle(cap[1].substring(7)) | |
: this.mangle(cap[1]); | |
href = this.mangle('mailto:') + text; | |
} else { | |
text = escape(cap[1]); | |
href = text; | |
} | |
out += this.renderer.link(href, null, text); | |
continue; | |
} | |
// url (gfm) | |
if (!this.inLink && (cap = this.rules.url.exec(src))) { | |
src = src.substring(cap[0].length); | |
text = escape(cap[1]); | |
href = text; | |
out += this.renderer.link(href, null, text); | |
continue; | |
} | |
// tag | |
if (cap = this.rules.tag.exec(src)) { | |
if (!this.inLink && /^<a /i.test(cap[0])) { | |
this.inLink = true; | |
} else if (this.inLink && /^<\/a>/i.test(cap[0])) { | |
this.inLink = false; | |
} | |
src = src.substring(cap[0].length); | |
out += this.options.sanitize | |
? this.options.sanitizer | |
? this.options.sanitizer(cap[0]) | |
: escape(cap[0]) | |
: cap[0] | |
continue; | |
} | |
// link | |
if (cap = this.rules.link.exec(src)) { | |
src = src.substring(cap[0].length); | |
this.inLink = true; | |
out += this.outputLink(cap, { | |
href: cap[2], | |
title: cap[3] | |
}); | |
this.inLink = false; | |
continue; | |
} | |
// reflink, nolink | |
if ((cap = this.rules.reflink.exec(src)) | |
|| (cap = this.rules.nolink.exec(src))) { | |
src = src.substring(cap[0].length); | |
link = (cap[2] || cap[1]).replace(/\s+/g, ' '); | |
link = this.links[link.toLowerCase()]; | |
if (!link || !link.href) { | |
out += cap[0].charAt(0); | |
src = cap[0].substring(1) + src; | |
continue; | |
} | |
this.inLink = true; | |
out += this.outputLink(cap, link); | |
this.inLink = false; | |
continue; | |
} | |
// strong | |
if (cap = this.rules.strong.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.strong(this.output(cap[2] || cap[1])); | |
continue; | |
} | |
// em | |
if (cap = this.rules.em.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.em(this.output(cap[2] || cap[1])); | |
continue; | |
} | |
// code | |
if (cap = this.rules.code.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.codespan(escape(cap[2], true)); | |
continue; | |
} | |
// br | |
if (cap = this.rules.br.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.br(); | |
continue; | |
} | |
// del (gfm) | |
if (cap = this.rules.del.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.del(this.output(cap[1])); | |
continue; | |
} | |
// text | |
if (cap = this.rules.text.exec(src)) { | |
src = src.substring(cap[0].length); | |
out += this.renderer.text(escape(this.smartypants(cap[0]))); | |
continue; | |
} | |
if (src) { | |
throw new | |
Error('Infinite loop on byte: ' + src.charCodeAt(0)); | |
} | |
} | |
return out; | |
}; | |
/** | |
* Compile Link | |
*/ | |
InlineLexer.prototype.outputLink = function(cap, link) { | |
var href = escape(link.href) | |
, title = link.title ? escape(link.title) : null; | |
return cap[0].charAt(0) !== '!' | |
? this.renderer.link(href, title, this.output(cap[1])) | |
: this.renderer.image(href, title, escape(cap[1])); | |
}; | |
/** | |
* Smartypants Transformations | |
*/ | |
InlineLexer.prototype.smartypants = function(text) { | |
if (!this.options.smartypants) return 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 | |
*/ | |
InlineLexer.prototype.mangle = function(text) { | |
if (!this.options.mangle) return text; | |
var out = '' | |
, l = text.length | |
, i = 0 | |
, ch; | |
for (; i < l; i++) { | |
ch = text.charCodeAt(i); | |
if (Math.random() > 0.5) { | |
ch = 'x' + ch.toString(16); | |
} | |
out += '&#' + ch + ';'; | |
} | |
return out; | |
}; | |
/** | |
* Renderer | |
*/ | |
function Renderer(options) { | |
this.options = options || {}; | |
} | |
Renderer.prototype.code = function(code, lang, escaped) { | |
if (this.options.highlight) { | |
var out = this.options.highlight(code, lang); | |
if (out != null && out !== code) { | |
escaped = true; | |
code = out; | |
} | |
} | |
if (!lang) { | |
return '<pre><code>' | |
+ (escaped ? code : escape(code, true)) | |
+ '\n</code></pre>'; | |
} | |
return '<pre><code class="' | |
+ this.options.langPrefix | |
+ escape(lang, true) | |
+ '">' | |
+ (escaped ? code : escape(code, true)) | |
+ '\n</code></pre>\n'; | |
}; | |
Renderer.prototype.blockquote = function(quote) { | |
return '<blockquote>\n' + quote + '</blockquote>\n'; | |
}; | |
Renderer.prototype.html = function(html) { | |
return html; | |
}; | |
Renderer.prototype.heading = function(text, level, raw) { | |
return '<h' | |
+ level | |
+ ' id="' | |
+ this.options.headerPrefix | |
+ raw.toLowerCase().replace(/[^\w]+/g, '-') | |
+ '">' | |
+ text | |
+ '</h' | |
+ level | |
+ '>\n'; | |
}; | |
Renderer.prototype.hr = function() { | |
return this.options.xhtml ? '<hr/>\n' : '<hr>\n'; | |
}; | |
Renderer.prototype.list = function(body, ordered) { | |
var type = ordered ? 'ol' : 'ul'; | |
return '<' + type + '>\n' + body + '</' + type + '>\n'; | |
}; | |
Renderer.prototype.listitem = function(text) { | |
return '<li>' + text + '</li>\n'; | |
}; | |
Renderer.prototype.paragraph = function(text) { | |
return '<p>' + text + '</p>\n'; | |
}; | |
Renderer.prototype.table = function(header, body) { | |
return '<table>\n' | |
+ '<thead>\n' | |
+ header | |
+ '</thead>\n' | |
+ '<tbody>\n' | |
+ body | |
+ '</tbody>\n' | |
+ '</table>\n'; | |
}; | |
Renderer.prototype.tablerow = function(content) { | |
return '<tr>\n' + content + '</tr>\n'; | |
}; | |
Renderer.prototype.tablecell = function(content, flags) { | |
var type = flags.header ? 'th' : 'td'; | |
var width = flags.width ? '; width:' + flags.width + '%': ''; | |
var tag = flags.align | |
? '<' + type + ' style="text-align:' + flags.align + width + '">' | |
: '<' + type + ' style="' + width + '">'; | |
return tag + content + '</' + type + '>\n'; | |
}; | |
// span level renderer | |
Renderer.prototype.strong = function(text) { | |
return '<strong>' + text + '</strong>'; | |
}; | |
Renderer.prototype.em = function(text) { | |
return '<em>' + text + '</em>'; | |
}; | |
Renderer.prototype.codespan = function(text) { | |
return '<code>' + text + '</code>'; | |
}; | |
Renderer.prototype.br = function() { | |
return this.options.xhtml ? '<br/>' : '<br>'; | |
}; | |
Renderer.prototype.del = function(text) { | |
return '<del>' + text + '</del>'; | |
}; | |
Renderer.prototype.link = function(href, title, text) { | |
if (this.options.sanitize) { | |
try { | |
var prot = decodeURIComponent(unescape(href)) | |
.replace(/[^\w:]/g, '') | |
.toLowerCase(); | |
} catch (e) { | |
return ''; | |
} | |
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) { | |
return ''; | |
} | |
} | |
var out = '<a href="' + href + '"'; | |
if (title) { | |
out += ' title="' + title + '"'; | |
} | |
out += '>' + text + '</a>'; | |
return out; | |
}; | |
Renderer.prototype.image = function(href, title, text) { | |
var out = '<img src="' + href + '" alt="' + text + '"'; | |
if (title) { | |
out += ' title="' + title + '"'; | |
} | |
out += this.options.xhtml ? '/>' : '>'; | |
return out; | |
}; | |
Renderer.prototype.text = function(text) { | |
return text; | |
}; | |
/** | |
* Parsing & Compiling | |
*/ | |
function Parser(options) { | |
this.tokens = []; | |
this.token = null; | |
this.options = options || marked.defaults; | |
this.options.renderer = this.options.renderer || new Renderer; | |
this.renderer = this.options.renderer; | |
this.renderer.options = this.options; | |
} | |
/** | |
* Static Parse Method | |
*/ | |
Parser.parse = function(src, options, renderer) { | |
var parser = new Parser(options, renderer); | |
return parser.parse(src); | |
}; | |
/** | |
* Parse Loop | |
*/ | |
Parser.prototype.parse = function(src) { | |
this.inline = new InlineLexer(src.links, this.options, this.renderer); | |
this.tokens = src.reverse(); | |
var out = ''; | |
while (this.next()) { | |
out += this.tok(); | |
} | |
return out; | |
}; | |
/** | |
* Next Token | |
*/ | |
Parser.prototype.next = function() { | |
return this.token = this.tokens.pop(); | |
}; | |
/** | |
* Preview Next Token | |
*/ | |
Parser.prototype.peek = function() { | |
return this.tokens[this.tokens.length - 1] || 0; | |
}; | |
/** | |
* Parse Text Tokens | |
*/ | |
Parser.prototype.parseText = function() { | |
var body = this.token.text; | |
while (this.peek().type === 'text') { | |
body += '\n' + this.next().text; | |
} | |
return this.inline.output(body); | |
}; | |
/** | |
* Parse Current Token | |
*/ | |
Parser.prototype.tok = function() { | |
switch (this.token.type) { | |
case 'space': { | |
return ''; | |
} | |
case 'hr': { | |
return this.renderer.hr(); | |
} | |
case 'heading': { | |
return this.renderer.heading( | |
this.inline.output(this.token.text), | |
this.token.depth, | |
this.token.text); | |
} | |
case 'code': { | |
return this.renderer.code(this.token.text, | |
this.token.lang, | |
this.token.escaped); | |
} | |
case 'table': { | |
var header = '' | |
, body = '' | |
, i | |
, row | |
, cell | |
, flags | |
, j; | |
// header | |
cell = ''; | |
for (i = 0; i < this.token.header.length; i++) { | |
flags = { header: true, align: this.token.align[i] }; | |
cell += this.renderer.tablecell( | |
this.inline.output(this.token.header[i]), | |
{ header: true, align: this.token.align[i], width: this.token.width[i] } | |
); | |
} | |
header += this.renderer.tablerow(cell); | |
for (i = 0; i < this.token.cells.length; i++) { | |
row = this.token.cells[i]; | |
cell = ''; | |
for (j = 0; j < row.length; j++) { | |
cell += this.renderer.tablecell( | |
this.inline.output(row[j]), | |
{ header: false, align: this.token.align[j], width: this.token.width[j]} | |
); | |
} | |
body += this.renderer.tablerow(cell); | |
} | |
return this.renderer.table(header, body); | |
} | |
case 'blockquote_start': { | |
var body = ''; | |
while (this.next().type !== 'blockquote_end') { | |
body += this.tok(); | |
} | |
return this.renderer.blockquote(body); | |
} | |
case 'list_start': { | |
var body = '' | |
, ordered = this.token.ordered; | |
while (this.next().type !== 'list_end') { | |
body += this.tok(); | |
} | |
return this.renderer.list(body, ordered); | |
} | |
case 'list_item_start': { | |
var body = ''; | |
while (this.next().type !== 'list_item_end') { | |
body += this.token.type === 'text' | |
? this.parseText() | |
: this.tok(); | |
} | |
return this.renderer.listitem(body); | |
} | |
case 'loose_item_start': { | |
var body = ''; | |
while (this.next().type !== 'list_item_end') { | |
body += this.tok(); | |
} | |
return this.renderer.listitem(body); | |
} | |
case 'html': { | |
var html = !this.token.pre && !this.options.pedantic | |
? this.inline.output(this.token.text) | |
: this.token.text; | |
return this.renderer.html(html); | |
} | |
case 'paragraph': { | |
return this.renderer.paragraph(this.inline.output(this.token.text)); | |
} | |
case 'text': { | |
return this.renderer.paragraph(this.parseText()); | |
} | |
} | |
}; | |
/** | |
* Helpers | |
*/ | |
function escape(html, encode) { | |
return html | |
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') | |
.replace(/</g, '<') | |
.replace(/>/g, '>') | |
.replace(/"/g, '"') | |
.replace(/'/g, '''); | |
} | |
function unescape(html) { | |
// explicitly match decimal, hex, and named HTML entities | |
return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) { | |
n = n.toLowerCase(); | |
if (n === 'colon') return ':'; | |
if (n.charAt(0) === '#') { | |
return n.charAt(1) === 'x' | |
? String.fromCharCode(parseInt(n.substring(2), 16)) | |
: String.fromCharCode(+n.substring(1)); | |
} | |
return ''; | |
}); | |
} | |
function replace(regex, opt) { | |
regex = regex.source; | |
opt = opt || ''; | |
return function self(name, val) { | |
if (!name) return new RegExp(regex, opt); | |
val = val.source || val; | |
val = val.replace(/(^|[^\[])\^/g, '$1'); | |
regex = regex.replace(name, val); | |
return self; | |
}; | |
} | |
function noop() {} | |
noop.exec = noop; | |
function merge(obj) { | |
var i = 1 | |
, target | |
, key; | |
for (; i < arguments.length; i++) { | |
target = arguments[i]; | |
for (key in target) { | |
if (Object.prototype.hasOwnProperty.call(target, key)) { | |
obj[key] = target[key]; | |
} | |
} | |
} | |
return obj; | |
} | |
/** | |
* Marked | |
*/ | |
function marked(src, opt, callback) { | |
if (callback || typeof opt === 'function') { | |
if (!callback) { | |
callback = opt; | |
opt = null; | |
} | |
opt = merge({}, marked.defaults, opt || {}); | |
var highlight = opt.highlight | |
, tokens | |
, pending | |
, i = 0; | |
try { | |
tokens = Lexer.lex(src, opt) | |
} catch (e) { | |
return callback(e); | |
} | |
pending = tokens.length; | |
var done = function(err) { | |
if (err) { | |
opt.highlight = highlight; | |
return callback(err); | |
} | |
var out; | |
try { | |
out = Parser.parse(tokens, opt); | |
} catch (e) { | |
err = e; | |
} | |
opt.highlight = highlight; | |
return err | |
? callback(err) | |
: callback(null, out); | |
}; | |
if (!highlight || highlight.length < 3) { | |
return done(); | |
} | |
delete opt.highlight; | |
if (!pending) return done(); | |
for (; i < tokens.length; i++) { | |
(function(token) { | |
if (token.type !== 'code') { | |
return --pending || done(); | |
} | |
return highlight(token.text, token.lang, function(err, code) { | |
if (err) return done(err); | |
if (code == null || code === token.text) { | |
return --pending || done(); | |
} | |
token.text = code; | |
token.escaped = true; | |
--pending || done(); | |
}); | |
})(tokens[i]); | |
} | |
return; | |
} | |
try { | |
if (opt) opt = merge({}, marked.defaults, opt); | |
return Parser.parse(Lexer.lex(src, opt), opt); | |
} catch (e) { | |
e.message += '\nPlease report this to https://github.com/chjj/marked.'; | |
if ((opt || marked.defaults).silent) { | |
return '<p>An error occured:</p><pre>' | |
+ escape(e.message + '', true) | |
+ '</pre>'; | |
} | |
throw e; | |
} | |
} | |
/** | |
* Options | |
*/ | |
marked.options = | |
marked.setOptions = function(opt) { | |
merge(marked.defaults, opt); | |
return marked; | |
}; | |
marked.defaults = { | |
gfm: true, | |
tables: true, | |
breaks: false, | |
pedantic: false, | |
sanitize: false, | |
sanitizer: null, | |
mangle: true, | |
smartLists: false, | |
silent: false, | |
highlight: null, | |
langPrefix: 'lang-', | |
smartypants: false, | |
headerPrefix: '', | |
renderer: new Renderer, | |
xhtml: false | |
}; | |
/** | |
* Expose | |
*/ | |
marked.Parser = Parser; | |
marked.parser = Parser.parse; | |
marked.Renderer = Renderer; | |
marked.Lexer = Lexer; | |
marked.lexer = Lexer.lex; | |
marked.InlineLexer = InlineLexer; | |
marked.inlineLexer = InlineLexer.output; | |
marked.parse = marked; | |
if (typeof module !== 'undefined' && typeof exports === 'object') { | |
module.exports = marked; | |
} else if (typeof define === 'function' && define.amd) { | |
define(function() { return marked; }); | |
} else { | |
this.marked = marked; | |
} | |
}).call(function() { | |
return this || (typeof window !== 'undefined' ? window : global); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment