marked/lib/marked.js

979 lines
21 KiB
JavaScript
Raw Normal View History

2011-07-24 08:15:35 -05:00
/**
* marked - A markdown parser (https://github.com/chjj/marked)
2012-01-03 00:35:47 -06:00
* Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed)
2011-07-24 08:15:35 -05:00
*/
2011-08-13 23:19:46 -05:00
;(function() {
2011-07-24 08:15:35 -05:00
/**
* Block-Level Grammar
*/
2013-01-03 02:55:10 -06:00
/**
* Normal Block Grammar
*/
2011-08-13 23:19:46 -05:00
var block = {
2011-08-18 18:59:42 -05:00
newline: /^\n+/,
2012-03-05 21:32:48 -06:00
code: /^( {4}[^\n]+\n*)+/,
2012-02-19 20:29:35 -06:00
fences: noop,
hr: /^( *[-*_]){3,} *(?:\n+|$)/,
2012-01-20 06:07:41 -06:00
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
2013-01-03 03:22:48 -06:00
nptable: noop,
lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
2012-01-28 06:01:21 -06:00
blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
2012-11-23 10:54:18 +00:00
list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
2012-01-06 14:41:27 -06:00
html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
def: /^ *\[([^\]]+)\]: *([^\s]+)(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
2013-01-03 03:22:48 -06:00
table: noop,
2012-09-05 13:20:06 -05:00
paragraph: /^([^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+\n*/,
2012-01-06 14:41:27 -06:00
text: /^[^\n]+/
2011-07-24 08:15:35 -05:00
};
2012-04-22 17:19:08 -05:00
block.bullet = /(?:[*+-]|\d+\.)/;
block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
2012-04-21 18:03:57 -05:00
block.item = replace(block.item, 'gm')
2012-04-22 17:19:08 -05:00
(/bull/g, block.bullet)
2012-04-21 18:03:57 -05:00
();
2012-03-05 15:44:55 -06:00
block.list = replace(block.list)
2012-04-22 17:19:08 -05:00
(/bull/g, block.bullet)
2012-04-26 09:18:41 -05:00
('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
2012-03-05 15:44:55 -06:00
();
2012-01-10 14:13:57 -06:00
2013-01-03 01:42:48 -06:00
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+(?!:/|@)\\b';
2012-03-05 15:44:55 -06:00
block.html = replace(block.html)
2012-11-23 10:54:18 +00:00
('comment', /<!--[\s\S]*?-->/)
('closed', /<(tag)[\s\S]+?<\/\1>/)
('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
2013-01-03 01:42:48 -06:00
(/tag/g, block._tag)
2012-03-05 15:44:55 -06:00
();
2012-09-05 13:20:06 -05:00
block.paragraph = replace(block.paragraph)
('hr', block.hr)
('heading', block.heading)
('lheading', block.lheading)
('blockquote', block.blockquote)
2013-01-03 01:42:48 -06:00
('tag', '<' + block._tag)
2012-09-05 13:20:06 -05:00
('def', block.def)
();
2013-01-03 02:55:10 -06:00
block.normal = merge({}, block);
/**
* GFM Block Grammar
*/
2012-02-19 20:29:35 -06:00
2013-01-03 02:55:10 -06:00
block.gfm = merge({}, block.normal, {
fences: /^ *(`{3,}|~{3,}) *(\w+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
2013-01-03 02:55:10 -06:00
paragraph: /^/
});
2012-02-19 20:29:35 -06:00
2012-03-05 15:44:55 -06:00
block.gfm.paragraph = replace(block.paragraph)
2013-01-03 04:14:16 -06:00
('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
2012-03-05 15:44:55 -06:00
();
2012-02-19 20:29:35 -06:00
2013-01-03 02:55:10 -06:00
/**
* GFM + Tables Block Grammar
*/
block.tables = merge({}, block.gfm, {
2013-01-03 03:22:48 -06:00
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*\n)*)\n*/,
2013-01-03 04:14:16 -06:00
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*\n)*)\n*/,
paragraph: /^/
2013-01-03 02:55:10 -06:00
});
2013-01-03 04:14:16 -06:00
block.tables.paragraph = replace(block.gfm.paragraph)
('$)|', '$)|' + block.gfm.table.source + '|' + block.gfm.nptable.source + '|')
();
2011-07-24 08:15:35 -05:00
/**
2011-10-22 23:39:04 -05:00
* Block Lexer
2011-07-24 08:15:35 -05:00
*/
function Lexer(options) {
this.tokens = [];
this.tokens.links = {};
2013-01-03 00:45:07 -06:00
this.options = options || marked.defaults;
2013-01-03 02:55:10 -06:00
this.rules = block.normal;
2013-01-03 00:45:07 -06:00
if (this.options.gfm) {
2013-01-03 02:55:10 -06:00
if (this.options.tables) {
this.rules = block.tables;
} else {
this.rules = block.gfm;
}
}
}
2011-08-13 17:06:08 -05:00
2013-01-03 01:40:10 -06:00
Lexer.rules = block;
Lexer.lex = function(src, options) {
var lexer = new Lexer(options);
return lexer.lex(src);
};
Lexer.prototype.lex = function(src) {
2013-01-03 01:06:58 -06:00
src = src
.replace(/\r\n|\r/g, '\n')
.replace(/\t/g, ' ')
.replace(/\u00a0/g, ' ')
.replace(/\u2424/g, '\n');
2011-07-24 08:15:35 -05:00
2013-01-03 01:06:58 -06:00
return this.token(src, true);
2011-08-13 17:06:08 -05:00
};
2011-08-13 16:53:15 -05:00
Lexer.prototype.token = function(src, top) {
var src = src.replace(/^ +$/gm, '')
2012-01-28 22:33:42 -06:00
, next
2011-10-22 06:09:28 -05:00
, loose
2011-10-22 07:49:11 -05:00
, cap
, item
, space
, i
, l;
2011-10-22 06:09:28 -05:00
while (src) {
2011-10-22 23:39:04 -05:00
// newline
2013-01-03 02:55:10 -06:00
if (cap = this.rules.newline.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
if (cap[0].length > 1) {
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'space'
});
2011-08-14 01:02:14 -05:00
}
2011-10-22 06:09:28 -05:00
}
2011-10-22 23:39:04 -05:00
// code
2013-01-03 02:55:10 -06:00
if (cap = this.rules.code.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
cap = cap[0].replace(/^ {4}/gm, '');
this.tokens.push({
2011-10-22 08:05:31 -05:00
type: 'code',
text: !this.options.pedantic
2012-02-19 03:00:03 -06:00
? cap.replace(/\n+$/, '')
: cap
2011-10-22 06:09:28 -05:00
});
continue;
}
2012-02-19 20:29:35 -06:00
// fences (gfm)
2013-01-03 02:55:10 -06:00
if (cap = this.rules.fences.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
2012-01-02 04:10:23 -06:00
type: 'code',
2012-08-17 10:24:12 -07:00
lang: cap[2],
text: cap[3]
2012-01-02 04:10:23 -06:00
});
continue;
}
2011-10-22 23:39:04 -05:00
// heading
2013-01-03 02:55:10 -06:00
if (cap = this.rules.heading.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'heading',
depth: cap[1].length,
text: cap[2]
});
continue;
}
// table no leading pipe (gfm)
2013-01-03 02:55:10 -06:00
if (top && (cap = this.rules.nptable.exec(src))) {
2012-08-24 07:26:12 +01:00
src = src.substring(cap[0].length);
2013-01-02 15:07:58 -06:00
item = {
2012-08-24 07:26:12 +01:00
type: 'table',
2013-01-02 14:34:19 -06:00
header: cap[1].replace(/(^ *| *\| *$)/g, '').split(/ *\| */),
2013-01-02 15:07:58 -06:00
align: cap[2].replace(/(^ *|\| *$)/g, '').split(/ *\| */),
cells: cap[3].replace(/\n$/, '').split('\n')
};
for (i = 0; i < item.align.length; i++) {
2013-01-02 22:04:56 -06:00
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;
}
2013-01-02 15:07:58 -06:00
}
for (i = 0; i < item.cells.length; i++) {
item.cells[i] = item.cells[i].split(/ *\| */);
}
this.tokens.push(item);
2013-01-02 15:07:58 -06:00
2012-08-24 07:26:12 +01:00
continue;
}
2011-10-22 23:39:04 -05:00
// lheading
2013-01-03 02:55:10 -06:00
if (cap = this.rules.lheading.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'heading',
depth: cap[2] === '=' ? 1 : 2,
text: cap[1]
});
continue;
}
2011-10-22 23:39:04 -05:00
// hr
2013-01-03 02:55:10 -06:00
if (cap = this.rules.hr.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'hr'
});
continue;
}
2011-10-22 23:39:04 -05:00
// blockquote
2013-01-03 02:55:10 -06:00
if (cap = this.rules.blockquote.exec(src)) {
src = src.substring(cap[0].length);
2012-02-19 03:00:03 -06:00
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'blockquote_start'
});
2012-01-06 16:44:48 -06:00
2012-01-20 22:51:25 -06:00
cap = cap[0].replace(/^ *> ?/gm, '');
// Pass `top` to keep the current
// "toplevel" state. This is exactly
// how markdown.pl works.
this.token(cap, top);
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'blockquote_end'
});
2012-02-19 03:00:03 -06:00
2011-10-22 06:09:28 -05:00
continue;
2011-08-14 01:02:14 -05:00
}
2011-10-22 06:09:28 -05:00
2011-10-22 23:39:04 -05:00
// list
2013-01-03 02:55:10 -06:00
if (cap = this.rules.list.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'list_start',
ordered: isFinite(cap[2])
});
// Get each top-level item.
2013-01-03 02:55:10 -06:00
cap = cap[0].match(this.rules.item);
2011-10-22 06:09:28 -05:00
2012-01-28 22:33:42 -06:00
next = false;
2011-10-22 23:39:04 -05:00
l = cap.length;
2012-01-28 22:33:42 -06:00
i = 0;
2011-10-22 07:49:11 -05:00
for (; i < l; i++) {
item = cap[i];
// Remove the list item's bullet
// so it is seen as the next token.
space = item.length;
2012-03-05 21:32:48 -06:00
item = item.replace(/^ *([*+-]|\d+\.) +/, '');
// Outdent whatever the
// list item contains. Hacky.
if (~item.indexOf('\n ')) {
space -= item.length;
item = !this.options.pedantic
2012-02-19 03:00:03 -06:00
? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
: item.replace(/^ {1,4}/gm, '');
2011-08-18 18:59:42 -05:00
}
2012-01-28 22:33:42 -06:00
// 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[item.length-1] === '\n';
if (!loose) loose = next;
}
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: loose
? 'loose_item_start'
: 'list_item_start'
2011-08-14 01:04:35 -05:00
});
// Recurse.
this.token(item);
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'list_item_end'
2011-08-14 01:04:35 -05:00
});
2011-10-22 07:49:11 -05:00
}
2011-10-22 06:09:28 -05:00
this.tokens.push({
2011-10-22 06:09:28 -05:00
type: 'list_end'
});
continue;
}
2011-10-22 23:39:04 -05:00
// html
2013-01-03 02:55:10 -06:00
if (cap = this.rules.html.exec(src)) {
src = src.substring(cap[0].length);
this.tokens.push({
type: this.options.sanitize
? 'paragraph'
: 'html',
pre: cap[1] === 'pre',
2011-10-22 06:09:28 -05:00
text: cap[0]
});
continue;
}
// def
2013-01-03 02:55:10 -06:00
if (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)
2013-01-03 02:55:10 -06:00
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')
};
for (i = 0; i < item.align.length; i++) {
2013-01-02 22:04:56 -06:00
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]
.replace(/(^ *\| *| *\| *$)/g, '')
.split(/ *\| */);
}
this.tokens.push(item);
continue;
}
// top-level paragraph
2013-01-03 02:55:10 -06:00
if (top && (cap = this.rules.paragraph.exec(src))) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'paragraph',
2011-10-22 06:09:28 -05:00
text: cap[0]
});
continue;
2011-08-14 01:04:35 -05:00
}
2012-01-03 02:25:47 -06:00
// text
2013-01-03 02:55:10 -06:00
if (cap = this.rules.text.exec(src)) {
// Top-level should never reach here.
src = src.substring(cap[0].length);
this.tokens.push({
2012-01-03 02:25:47 -06:00
type: 'text',
text: cap[0]
});
continue;
}
2012-11-23 10:33:47 -06:00
if (src) {
throw new
Error('Infinite loop on byte: ' + src.charCodeAt(0));
}
2011-08-13 16:53:15 -05:00
}
2011-07-24 08:15:35 -05:00
return this.tokens;
2011-07-24 08:15:35 -05:00
};
/**
2013-01-03 02:55:10 -06:00
* Inline-Level Grammar
*/
/**
* Normal Inline Grammar
2011-07-24 08:15:35 -05:00
*/
2011-08-13 23:19:46 -05:00
var inline = {
2012-08-24 07:55:49 +01:00
escape: /^\\([\\`*{}\[\]()#+\-.!_>|])/,
2011-08-18 19:31:53 -05:00
autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
2012-02-19 20:29:35 -06:00
url: noop,
2012-11-23 10:54:18 +00:00
tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
2012-03-05 21:32:48 -06:00
link: /^!?\[(inside)\]\(href\)/,
2012-03-05 15:44:55 -06:00
reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
2011-08-22 11:02:21 -05:00
nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
2012-11-23 10:54:18 +00:00
strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
code: /^(`+)([\s\S]*?[^`])\1(?!`)/,
br: /^ {2,}\n(?!\s*$)/,
2012-11-23 10:54:18 +00:00
text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
2012-02-19 20:29:35 -06:00
};
2012-03-05 21:32:48 -06:00
inline._linkInside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
2012-11-23 10:54:18 +00:00
inline._linkHref = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
2012-03-05 15:44:55 -06:00
inline.link = replace(inline.link)
2012-03-05 21:32:48 -06:00
('inside', inline._linkInside)
('href', inline._linkHref)
2012-03-05 15:44:55 -06:00
();
inline.reflink = replace(inline.reflink)
2012-03-05 21:32:48 -06:00
('inside', inline._linkInside)
2012-03-05 15:44:55 -06:00
();
2013-01-03 02:55:10 -06:00
inline.normal = merge({}, inline);
2012-02-19 20:29:35 -06:00
2013-01-03 02:55:10 -06:00
/**
* Pedantic Inline Grammar
*/
inline.pedantic = merge({}, inline.normal, {
2012-11-23 10:54:18 +00:00
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
2013-01-03 02:55:10 -06:00
});
2012-02-19 20:29:35 -06:00
2013-01-03 02:55:10 -06:00
/**
* GFM Inline Grammar
*/
inline.gfm = merge({}, inline.normal, {
2012-02-29 22:29:36 -06:00
url: /^(https?:\/\/[^\s]+[^.,:;"')\]\s])/,
2012-11-23 10:54:18 +00:00
text: /^[\s\S]+?(?=[\\<!\[_*`]|https?:\/\/| {2,}\n|$)/
2013-01-03 02:55:10 -06:00
});
/**
* GFM + Line Breaks Inline Grammar
*/
2011-08-13 21:04:18 -05:00
2013-01-03 02:55:10 -06:00
inline.breaks = merge({}, inline.gfm, {
2013-01-03 01:54:15 -06:00
br: replace(inline.br)('{2,}', '*')(),
text: replace(inline.gfm.text)('{2,}', '*')()
2013-01-03 02:55:10 -06:00
});
2013-01-03 01:54:15 -06:00
2011-08-13 23:19:46 -05:00
/**
2013-01-03 01:40:10 -06:00
* Inline Lexer & Compiler
2011-08-13 23:19:46 -05:00
*/
2013-01-03 01:40:10 -06:00
function InlineLexer(links, options) {
this.options = options || marked.defaults;
this.links = links || {};
2013-01-03 02:55:10 -06:00
this.rules = inline.normal;
2013-01-03 01:40:10 -06:00
if (this.options.gfm) {
2013-01-03 02:55:10 -06:00
if (this.options.breaks) {
this.rules = inline.breaks;
2013-01-03 01:54:15 -06:00
} else {
2013-01-03 02:55:10 -06:00
this.rules = inline.gfm;
2013-01-03 01:54:15 -06:00
}
2013-01-03 02:55:10 -06:00
} else if (this.options.pedantic) {
this.rules = inline.pedantic;
2013-01-03 01:40:10 -06:00
}
}
InlineLexer.rules = inline;
2013-01-03 04:14:16 -06:00
InlineLexer.output = function(src, links, opt) {
var inline = new InlineLexer(links, opt);
return inline.output(src);
};
2013-01-03 01:40:10 -06:00
InlineLexer.prototype.output = function(src) {
2011-08-14 01:02:14 -05:00
var out = ''
2013-01-03 01:40:10 -06:00
, links = this.links
2011-08-18 19:26:23 -05:00
, link
2011-08-14 01:02:14 -05:00
, text
2011-10-22 06:09:28 -05:00
, href
2011-08-14 01:02:14 -05:00
, cap;
2011-08-13 23:36:44 -05:00
while (src) {
2011-10-22 23:39:04 -05:00
// escape
2013-01-03 02:55:10 -06:00
if (cap = this.rules.escape.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += cap[1];
continue;
}
2011-10-22 23:39:04 -05:00
// autolink
2013-01-03 02:55:10 -06:00
if (cap = this.rules.autolink.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
if (cap[2] === '@') {
text = cap[1][6] === ':'
2013-01-03 01:40:10 -06:00
? this.mangle(cap[1].substring(7))
: this.mangle(cap[1]);
href = this.mangle('mailto:') + text;
2011-10-22 06:09:28 -05:00
} else {
text = escape(cap[1]);
href = text;
2011-08-13 21:04:18 -05:00
}
2011-10-22 08:05:31 -05:00
out += '<a href="'
+ href
+ '">'
2011-10-22 06:09:28 -05:00
+ text
+ '</a>';
continue;
2011-08-13 21:04:18 -05:00
}
2011-10-22 06:09:28 -05:00
2012-02-19 20:29:35 -06:00
// url (gfm)
2013-01-03 02:55:10 -06:00
if (cap = this.rules.url.exec(src)) {
src = src.substring(cap[0].length);
text = escape(cap[1]);
href = text;
out += '<a href="'
+ href
+ '">'
+ text
+ '</a>';
continue;
}
2011-10-22 23:39:04 -05:00
// tag
2013-01-03 02:55:10 -06:00
if (cap = this.rules.tag.exec(src)) {
src = src.substring(cap[0].length);
2013-01-03 00:45:07 -06:00
out += this.options.sanitize
2012-02-19 03:00:03 -06:00
? escape(cap[0])
: cap[0];
2011-10-22 06:09:28 -05:00
continue;
}
2011-10-22 23:39:04 -05:00
// link
2013-01-03 02:55:10 -06:00
if (cap = this.rules.link.exec(src)) {
src = src.substring(cap[0].length);
out += this.outputLink(cap, {
2012-03-05 15:44:55 -06:00
href: cap[2],
2012-03-10 16:47:38 -06:00
title: cap[3]
2012-01-21 09:28:53 -06:00
});
2011-10-22 06:09:28 -05:00
continue;
}
2011-10-22 23:39:04 -05:00
// reflink, nolink
2013-01-03 02:55:10 -06:00
if ((cap = this.rules.reflink.exec(src))
|| (cap = this.rules.nolink.exec(src))) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
link = links[link.toLowerCase()];
2012-01-14 03:33:28 -06:00
if (!link || !link.href) {
2011-10-22 06:09:28 -05:00
out += cap[0][0];
src = cap[0].substring(1) + src;
2011-10-22 06:09:28 -05:00
continue;
}
out += this.outputLink(cap, link);
2011-10-22 06:09:28 -05:00
continue;
}
2011-10-22 23:39:04 -05:00
// strong
2013-01-03 02:55:10 -06:00
if (cap = this.rules.strong.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += '<strong>'
2013-01-03 01:40:10 -06:00
+ this.output(cap[2] || cap[1])
2011-10-22 06:09:28 -05:00
+ '</strong>';
continue;
}
2011-10-22 23:39:04 -05:00
// em
2013-01-03 02:55:10 -06:00
if (cap = this.rules.em.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += '<em>'
2013-01-03 01:40:10 -06:00
+ this.output(cap[2] || cap[1])
2011-10-22 06:09:28 -05:00
+ '</em>';
continue;
}
2011-10-22 23:39:04 -05:00
// code
2013-01-03 02:55:10 -06:00
if (cap = this.rules.code.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += '<code>'
+ escape(cap[2], true)
2011-10-22 06:09:28 -05:00
+ '</code>';
continue;
}
2011-10-22 23:39:04 -05:00
// br
2013-01-03 02:55:10 -06:00
if (cap = this.rules.br.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += '<br>';
continue;
}
2011-10-22 23:39:04 -05:00
// text
2013-01-03 02:55:10 -06:00
if (cap = this.rules.text.exec(src)) {
src = src.substring(cap[0].length);
2011-10-22 06:09:28 -05:00
out += escape(cap[0]);
continue;
2011-08-14 01:04:35 -05:00
}
2012-11-23 10:33:47 -06:00
if (src) {
throw new
Error('Infinite loop on byte: ' + src.charCodeAt(0));
}
2011-08-13 23:19:46 -05:00
}
2011-08-13 21:04:18 -05:00
2011-08-13 23:19:46 -05:00
return out;
};
2011-08-13 21:04:18 -05:00
2013-01-03 01:40:10 -06:00
InlineLexer.prototype.outputLink = function(cap, link) {
2011-10-22 06:09:28 -05:00
if (cap[0][0] !== '!') {
return '<a href="'
+ escape(link.href)
+ '"'
+ (link.title
2012-01-14 03:27:06 -06:00
? ' title="'
+ escape(link.title)
+ '"'
: '')
2011-10-22 06:09:28 -05:00
+ '>'
2013-01-03 01:40:10 -06:00
+ this.output(cap[1])
2011-10-22 06:09:28 -05:00
+ '</a>';
} else {
return '<img src="'
+ escape(link.href)
+ '" alt="'
+ escape(cap[1])
+ '"'
+ (link.title
2012-01-14 03:27:06 -06:00
? ' title="'
+ escape(link.title)
+ '"'
: '')
2011-10-22 06:09:28 -05:00
+ '>';
}
2013-01-03 01:40:10 -06:00
};
InlineLexer.prototype.mangle = function(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;
};
2011-10-22 06:09:28 -05:00
2011-07-24 08:15:35 -05:00
/**
2013-01-03 01:40:10 -06:00
* Parsing & Compiling
2011-07-24 08:15:35 -05:00
*/
function Parser(options) {
this.tokens = [];
this.token = null;
2013-01-03 00:45:07 -06:00
this.options = options || marked.defaults;
}
2011-07-24 08:15:35 -05:00
Parser.parse = function(src, options) {
var parser = new Parser(options);
return parser.parse(src);
};
Parser.prototype.parse = function(src) {
2013-01-03 01:40:10 -06:00
this.inline = new InlineLexer(src.links, this.options);
this.tokens = src.reverse();
var out = '';
while (this.next()) {
out += this.tok();
}
return out;
};
Parser.prototype.next = function() {
return this.token = this.tokens.pop();
};
2013-01-03 01:06:58 -06:00
Parser.prototype.peek = function() {
return this.tokens[this.tokens.length-1] || 0;
};
Parser.prototype.parseText = function() {
2013-01-03 01:06:58 -06:00
var body = this.token.text;
2013-01-03 01:06:58 -06:00
while (this.peek().type === 'text') {
body += '\n' + this.next().text;
}
2013-01-03 01:40:10 -06:00
return this.inline.output(body);
};
Parser.prototype.tok = function() {
switch (this.token.type) {
2011-10-22 06:18:15 -05:00
case 'space': {
2011-08-18 18:59:42 -05:00
return '';
2011-10-22 06:18:15 -05:00
}
case 'hr': {
return '<hr>\n';
2011-10-22 06:18:15 -05:00
}
case 'heading': {
return '<h'
+ this.token.depth
2011-10-22 06:18:15 -05:00
+ '>'
2013-01-03 01:40:10 -06:00
+ this.inline.output(this.token.text)
2011-10-22 06:18:15 -05:00
+ '</h'
+ this.token.depth
+ '>\n';
2011-10-22 06:18:15 -05:00
}
2011-10-22 08:05:31 -05:00
case 'code': {
if (this.options.highlight) {
this.token.code = this.options.highlight(this.token.text, this.token.lang);
if (this.token.code != null && this.token.code !== this.token.text) {
this.token.escaped = true;
this.token.text = this.token.code;
}
}
if (!this.token.escaped) {
this.token.text = escape(this.token.text, true);
}
return '<pre><code'
+ (this.token.lang
? ' class="lang-'
+ this.token.lang
2012-01-14 03:27:06 -06:00
+ '"'
: '')
+ '>'
+ this.token.text
+ '</code></pre>\n';
2011-10-22 06:18:15 -05:00
}
2012-08-24 07:26:12 +01:00
case 'table': {
2013-01-02 15:25:10 -06:00
var thead
2013-01-02 15:07:58 -06:00
, heading
, i
, tbody
, row
, cell
, j;
2013-01-02 15:25:10 -06:00
// header
2013-01-02 15:26:54 -06:00
thead = '<thead>\n<tr>\n';
for (i = 0; i < this.token.header.length; i++) {
heading = this.inline.output(this.token.header[i]);
thead += this.token.align[i]
? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
2013-01-02 15:29:57 -06:00
: '<th>' + heading + '</th>\n';
2013-01-02 15:07:58 -06:00
}
2013-01-02 15:23:50 -06:00
thead += '</tr>\n</thead>\n';
2012-08-24 07:26:12 +01:00
2013-01-02 15:25:10 -06:00
// body
2013-01-02 15:23:50 -06:00
tbody = '<tbody>\n'
for (i = 0; i < this.token.cells.length; i++) {
row = this.token.cells[i];
2013-01-02 15:26:54 -06:00
tbody += '<tr>\n';
2013-01-02 15:07:58 -06:00
for (j = 0; j < row.length; j++) {
cell = this.inline.output(row[j]);
tbody += this.token.align[j]
? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
2013-01-02 15:29:57 -06:00
: '<td>' + cell + '</td>\n';
2013-01-02 15:07:58 -06:00
}
2012-08-24 07:26:12 +01:00
tbody += '</tr>\n';
2013-01-02 15:07:58 -06:00
}
2013-01-02 15:23:50 -06:00
tbody += '</tbody>\n';
2012-08-24 07:26:12 +01:00
return '<table>\n'
+ thead
+ tbody
+ '</table>\n'
}
2011-10-22 06:18:15 -05:00
case 'blockquote_start': {
var body = '';
2011-10-22 06:18:15 -05:00
while (this.next().type !== 'blockquote_end') {
body += this.tok();
2011-07-24 08:15:35 -05:00
}
2011-10-22 06:18:15 -05:00
return '<blockquote>\n'
+ body
+ '</blockquote>\n';
2011-10-22 06:18:15 -05:00
}
case 'list_start': {
var type = this.token.ordered ? 'ol' : 'ul'
, body = '';
2011-10-22 06:18:15 -05:00
while (this.next().type !== 'list_end') {
body += this.tok();
2011-07-24 08:15:35 -05:00
}
2011-10-22 06:18:15 -05:00
return '<'
+ type
+ '>\n'
+ body
2011-10-22 06:18:15 -05:00
+ '</'
+ type
+ '>\n';
2011-10-22 06:18:15 -05:00
}
case 'list_item_start': {
var body = '';
2011-10-22 06:18:15 -05:00
while (this.next().type !== 'list_item_end') {
body += this.token.type === 'text'
? this.parseText()
: this.tok();
2011-07-24 08:15:35 -05:00
}
2011-10-22 06:18:15 -05:00
2011-10-04 17:32:59 -05:00
return '<li>'
+ body
+ '</li>\n';
2011-10-22 06:18:15 -05:00
}
case 'loose_item_start': {
var body = '';
2011-10-22 06:18:15 -05:00
while (this.next().type !== 'list_item_end') {
body += this.tok();
2011-08-20 08:59:47 -05:00
}
2011-10-22 06:18:15 -05:00
2011-10-04 17:32:59 -05:00
return '<li>'
+ body
+ '</li>\n';
2011-10-22 06:18:15 -05:00
}
case 'html': {
return !this.token.pre && !this.options.pedantic
2013-01-03 01:40:10 -06:00
? this.inline.output(this.token.text)
: this.token.text;
2011-10-22 06:18:15 -05:00
}
case 'paragraph': {
return '<p>'
2013-01-03 01:40:10 -06:00
+ this.inline.output(this.token.text)
+ '</p>\n';
}
2011-10-22 06:18:15 -05:00
case 'text': {
2011-10-22 06:09:28 -05:00
return '<p>'
+ this.parseText()
+ '</p>\n';
2011-10-22 06:18:15 -05:00
}
}
};
2011-07-24 08:15:35 -05:00
/**
* Helpers
*/
function escape(html, encode) {
2011-07-24 08:15:35 -05:00
return html
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
2011-07-24 08:15:35 -05:00
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
2011-07-24 08:15:35 -05:00
2012-04-21 18:03:57 -05:00
function replace(regex, opt) {
2012-03-05 15:44:55 -06:00
regex = regex.source;
2012-04-21 18:03:57 -05:00
opt = opt || '';
2012-03-05 15:44:55 -06:00
return function self(name, val) {
2012-04-21 18:03:57 -05:00
if (!name) return new RegExp(regex, opt);
2012-09-05 13:20:06 -05:00
val = val.source || val;
val = val.replace(/(^|[^\[])\^/g, '$1');
regex = regex.replace(name, val);
2012-03-05 15:44:55 -06:00
return self;
};
}
2012-02-19 20:29:35 -06:00
function noop() {}
noop.exec = noop;
2013-01-03 02:55:10 -06:00
function merge(obj) {
var i = 1
, target
, key;
for (i = 1; i < arguments.length; i++) {
target = arguments[i];
for (key in target) {
if (Object.prototype.hasOwnProperty.call(target, key)) {
obj[key] = target[key];
}
}
}
return obj;
}
2011-07-24 08:15:35 -05:00
/**
2012-02-19 03:00:03 -06:00
* Marked
2011-07-24 08:15:35 -05:00
*/
function marked(src, opt) {
2012-11-23 10:33:47 -06:00
try {
return Parser.parse(Lexer.lex(src, opt), opt);
2012-11-23 10:33:47 -06:00
} catch (e) {
e.message += '\nPlease report this to https://github.com/chjj/marked.';
2013-01-03 00:45:07 -06:00
if ((opt || marked.defaults).silent) {
2013-01-03 01:40:10 -06:00
return 'An error occured:\n' + e.message;
2012-11-23 10:33:47 -06:00
}
throw e;
}
}
2011-07-24 08:15:35 -05:00
2012-02-19 20:29:35 -06:00
/**
* Options
*/
marked.options =
marked.setOptions = function(opt) {
2013-01-03 00:45:07 -06:00
marked.defaults = opt;
return marked;
2012-02-19 20:29:35 -06:00
};
2013-01-03 00:45:07 -06:00
marked.defaults = {
2012-02-19 03:00:03 -06:00
gfm: true,
tables: true,
2013-01-03 01:54:15 -06:00
breaks: false,
2012-02-19 03:00:03 -06:00
pedantic: false,
sanitize: false,
2013-01-03 01:40:10 -06:00
silent: false,
highlight: null
2013-01-03 00:45:07 -06:00
};
2012-02-19 03:00:03 -06:00
/**
* Expose
*/
2013-01-03 00:45:07 -06:00
marked.Parser = Parser;
2013-01-03 02:55:10 -06:00
marked.parser = Parser.parse;
2012-02-19 20:29:35 -06:00
2013-01-03 00:45:07 -06:00
marked.Lexer = Lexer;
2013-01-03 02:55:10 -06:00
marked.lexer = Lexer.lex;
2011-08-13 23:19:46 -05:00
2013-01-03 01:40:10 -06:00
marked.InlineLexer = InlineLexer;
2013-01-03 02:55:10 -06:00
marked.inlineLexer = InlineLexer.output;
2013-01-03 01:40:10 -06:00
2011-11-26 22:12:02 -06:00
marked.parse = marked;
2011-08-13 23:19:46 -05:00
if (typeof module !== 'undefined') {
module.exports = marked;
} else if(typeof define === 'function' && define.amd) {
define(function() { return marked; });
2011-08-13 23:19:46 -05:00
} else {
this.marked = marked;
}
2011-07-24 08:15:35 -05:00
2012-04-12 02:27:41 -05:00
}).call(function() {
return this || (typeof window !== 'undefined' ? window : global);
}());