Hector Ramos 0a6935618c Add support for syntax highlighting for languages other than JS
Summary:
The current website defaults to using JavaScript for any code block, regardless of language tag. This PR adds syntax highlighting support by passing the necessary language prop and by adding any missing languages to Marked.js.

Depends on #14212, #14065
Closes https://github.com/facebook/react-native/pull/14215

Differential Revision: D5149897

Pulled By: hramos

fbshipit-source-id: 95a817af2168d5743c75dd1ac030d399a68fb93c
2017-05-30 14:36:06 -07:00

1208 lines
25 KiB
JavaScript

/**
* marked - a markdown parser
* https://github.com/chjj/marked
*
* @copyright 2011-2013 Christopher Jeffrey. MIT License.
* @providesModule Marked
* @noflow
*/
/* eslint-disable */
'use strict';
var Header = require('Header');
var Prism = require('Prism');
var React = require('React');
var WebPlayer = require('WebPlayer');
var SnackPlayer = require('SnackPlayer');
/**
* 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 *(=|-){3,} *\n*/,
blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
html: /^ *(?:comment|closed|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+(?=(?: *[-*_]){3,} *(?:\n+|$))/
)();
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';
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: /^/,
});
block.gfm.paragraph = replace(block.paragraph)(
'(?!',
'(?!' +
block.gfm.fences.source.replace('\\1', '\\2') +
'|'
)();
/**
* 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) {
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'),
};
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);
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[item.length - 1] === '\n';
if (!loose) loose = next;
}
this.tokens.push({
type: loose
? 'loose_item_start'
: 'list_item_start',
});
// Recurse.
this.token(item, false);
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: cap[1] === 'pre',
text: cap[0],
});
continue;
}
// def
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)
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++) {
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
if (top && (cap = this.rules.paragraph.exec(src))) {
src = src.substring(cap[0].length);
this.tokens.push({
type: 'paragraph',
text: cap[1][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_((?:__|[\s\S])+?)_\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*/;
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;
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.push(cap[1]);
continue;
}
// autolink
if ((cap = this.rules.autolink.exec(src))) {
src = src.substring(cap[0].length);
if (cap[2] === '@') {
text = cap[1][6] === ':'
? cap[1].substring(7)
: cap[1];
href = 'mailto:' + text;
} else {
text = cap[1];
href = text;
}
out.push(
React.DOM.a({ href: this.sanitizeUrl(href) }, text)
);
continue;
}
// url (gfm)
if ((cap = this.rules.url.exec(src))) {
src = src.substring(cap[0].length);
text = cap[1];
href = text;
out.push(
React.DOM.a({ href: this.sanitizeUrl(href) }, text)
);
continue;
}
// tag
if ((cap = this.rules.tag.exec(src))) {
src = src.substring(cap[0].length);
var color = cap[0].match('<color ([^ ]+) />');
if (color) {
out.push(
React.DOM.span({
className: 'color',
style: { backgroundColor: color[1] },
})
);
continue;
}
// TODO(alpert): Don't escape if sanitize is false
out.push(cap[0]);
continue;
}
// link
if ((cap = this.rules.link.exec(src))) {
src = src.substring(cap[0].length);
out.push(
this.outputLink(cap, {
href: cap[2],
title: cap[3],
})
);
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.push.apply(out, this.output(cap[0][0]));
src = cap[0].substring(1) + src;
continue;
}
out.push(this.outputLink(cap, link));
continue;
}
// strong
if ((cap = this.rules.strong.exec(src))) {
src = src.substring(cap[0].length);
out.push(
React.DOM.strong(
null,
this.output(cap[2] || cap[1])
)
);
continue;
}
// em
if ((cap = this.rules.em.exec(src))) {
src = src.substring(cap[0].length);
out.push(
React.DOM.em(null, this.output(cap[2] || cap[1]))
);
continue;
}
// code
if ((cap = this.rules.code.exec(src))) {
src = src.substring(cap[0].length);
out.push(React.DOM.code(null, cap[2]));
continue;
}
// br
if ((cap = this.rules.br.exec(src))) {
src = src.substring(cap[0].length);
out.push(React.DOM.br(null, null));
continue;
}
// del (gfm)
if ((cap = this.rules.del.exec(src))) {
src = src.substring(cap[0].length);
out.push(React.DOM.del(null, this.output(cap[1])));
continue;
}
// text
if ((cap = this.rules.text.exec(src))) {
src = src.substring(cap[0].length);
out.push(this.smartypants(cap[0]));
continue;
}
if (src) {
throw new Error(
'Infinite loop on byte: ' + src.charCodeAt(0)
);
}
}
return out;
};
/**
* Sanitize a URL for a link or image
*/
InlineLexer.prototype.sanitizeUrl = function(url) {
if (this.options.sanitize) {
try {
var prot = decodeURIComponent(url)
.replace(/[^A-Za-z0-9:]/g, '')
.toLowerCase();
if (prot.indexOf('javascript:') === 0) {
return '#';
}
} catch (e) {
return '#';
}
}
return url;
};
/**
* Compile Link
*/
InlineLexer.prototype.outputLink = function(cap, link) {
if (cap[0][0] !== '!') {
var shouldOpenInNewWindow = link.href.charAt(0) !==
'/' && link.href.charAt(0) !== '#';
return React.DOM.a(
{
href: this.sanitizeUrl(link.href),
title: link.title,
target: shouldOpenInNewWindow ? '_blank' : '',
},
this.output(cap[1])
);
} else {
return React.DOM.img(
{
src: this.sanitizeUrl(link.href),
alt: cap[1],
title: link.title,
},
null
);
}
};
/**
* Smartypants Transformations
*/
InlineLexer.prototype.smartypants = function(text) {
if (!this.options.smartypants) return text;
return text
.replace(/--/g, '\u2014')
.replace(/'([^']*)'/g, '\u2018$1\u2019')
.replace(/"([^"]*)"/g, '\u201C$1\u201D')
.replace(/\.{3}/g, '\u2026');
};
/**
* Parsing & Compiling
*/
function Parser(options) {
this.tokens = [];
this.token = null;
this.options = options || marked.defaults;
}
/**
* Static Parse Method
*/
Parser.parse = function(src, options) {
var parser = new Parser(options);
return parser.parse(src);
};
/**
* Parse Loop
*/
Parser.prototype.parse = function(src) {
this.inline = new InlineLexer(src.links, this.options);
this.tokens = src.reverse();
var out = [];
while (this.next()) {
out.push(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 React.DOM.hr(null, null);
}
case 'heading': {
return (
<Header
level={this.token.depth}
toSlug={this.token.text}
>
{this.inline.output(this.token.text)}
</Header>
);
}
case 'code': {
var lang = this.token.lang, text = this.token.text;
if (
lang && lang.indexOf('ReactNativeWebPlayer') === 0
) {
return (
<WebPlayer params={lang.split('?')[1]}>
{text}
</WebPlayer>
);
}
if (lang && lang.indexOf('SnackPlayer') === 0) {
return (
<SnackPlayer params={lang.split('?')[1]}>
{text}
</SnackPlayer>
);
}
if (lang) {
return <Prism language={lang}>{text}</Prism>;
}
return <Prism>{text}</Prism>;
}
case 'table': {
var table = [],
body = [],
row = [],
heading,
i,
cells,
j;
// header
for (i = 0; i < this.token.header.length; i++) {
heading = this.inline.output(this.token.header[i]);
row.push(
React.DOM.th(
this.token.align[i]
? {
style: { textAlign: this.token.align[i] },
}
: null,
heading
)
);
}
table.push(
React.DOM.thead(null, React.DOM.tr(null, row))
);
// body
for (i = 0; i < this.token.cells.length; i++) {
row = [];
cells = this.token.cells[i];
for (j = 0; j < cells.length; j++) {
row.push(
React.DOM.td(
this.token.align[j]
? {
style: {
textAlign: this.token.align[j],
},
}
: null,
this.inline.output(cells[j])
)
);
}
body.push(React.DOM.tr(null, row));
}
table.push(React.DOM.thead(null, body));
return React.DOM.table(null, table);
}
case 'blockquote_start': {
var body = [];
while (this.next().type !== 'blockquote_end') {
body.push(this.tok());
}
return React.DOM.blockquote(null, body);
}
case 'list_start': {
var type = this.token.ordered ? 'ol' : 'ul',
body = [];
while (this.next().type !== 'list_end') {
body.push(this.tok());
}
return React.DOM[type](null, body);
}
case 'list_item_start': {
var body = [];
while (this.next().type !== 'list_item_end') {
body.push(
this.token.type === 'text'
? this.parseText()
: this.tok()
);
}
return React.DOM.li(null, body);
}
case 'loose_item_start': {
var body = [];
while (this.next().type !== 'list_item_end') {
body.push(this.tok());
}
return React.DOM.li(null, body);
}
case 'html': {
return !this.token.pre && !this.options.pedantic
? React.DOM.span({
dangerouslySetInnerHTML: {
__html: this.token.text,
},
})
: this.token.text;
}
case 'paragraph': {
return this.options.paragraphFn
? this.options.paragraphFn.call(
null,
this.inline.output(this.token.text)
)
: React.DOM.p(
null,
this.inline.output(this.token.text)
);
}
case 'text': {
return this.options.paragraphFn
? this.options.paragraphFn.call(
null,
this.parseText()
)
: React.DOM.p(null, this.parseText());
}
}
};
/**
* Helpers
*/
function escape(html, encode) {
return html
.replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
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;
}
if (opt) 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(hi) {
var out, err;
if (hi !== true) {
delete opt.highlight;
}
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(true);
}
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 (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 [
React.DOM.p(null, 'An error occurred:'),
React.DOM.pre(null, e.message),
];
}
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,
smartLists: false,
silent: false,
highlight: null,
langPrefix: 'lang-',
smartypants: false,
paragraphFn: null,
};
/**
* Expose
*/
marked.Parser = Parser;
marked.parser = Parser.parse;
marked.Lexer = Lexer;
marked.lexer = Lexer.lex;
marked.InlineLexer = InlineLexer;
marked.inlineLexer = InlineLexer.output;
marked.parse = marked;
var Marked = React.createClass({
render: function() {
return this.props.children
? React.DOM.div(
null,
marked(this.props.children, this.props)
)
: null;
},
});
module.exports = Marked;