| /** |
| * marked - A markdown parser (https://github.com/chjj/marked) |
| * Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed) |
| */ |
| |
| ;(function() { |
| |
| /** |
| * Block-Level Grammar |
| */ |
| |
| var block = { |
| newline: /^\n+/, |
| code: /^( {4}[^\n]+\n*)+/, |
| fences: noop, |
| hr: /^( *[-*_]){3,} *(?:\n+|$)/, |
| heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, |
| lheading: /^([^\n]+)\n *(=|-){3,} *\n*/, |
| blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, |
| list: /^( *)(bull) [^\0]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, |
| html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/, |
| def: /^ *\[([^\]]+)\]: *([^\s]+)(?: +["(]([^\n]+)[")])? *(?:\n+|$)/, |
| paragraph: /^([^\n]+\n?(?!body))+\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.html = replace(block.html) |
| ('comment', /<!--[^\0]*?-->/) |
| ('closed', /<(tag)[^\0]+?<\/\1>/) |
| ('closing', /<tag(?!:\/|@)\b(?:"[^"]*"|'[^']*'|[^'">])*?>/) |
| (/tag/g, tag()) |
| (); |
| |
| block.paragraph = (function() { |
| var paragraph = block.paragraph.source |
| , body = []; |
| |
| (function push(rule) { |
| rule = block[rule] ? block[rule].source : rule; |
| body.push(rule.replace(/(^|[^\[])\^/g, '$1')); |
| return push; |
| }) |
| ('hr') |
| ('heading') |
| ('lheading') |
| ('blockquote') |
| ('<' + tag()) |
| ('def'); |
| |
| return new |
| RegExp(paragraph.replace('body', body.join('|'))); |
| })(); |
| |
| block.normal = { |
| fences: block.fences, |
| paragraph: block.paragraph |
| }; |
| |
| block.gfm = { |
| fences: /^ *``` *(\w+)? *\n([^\0]+?)\s*``` *(?:\n+|$)/, |
| paragraph: /^/ |
| }; |
| |
| block.gfm.paragraph = replace(block.paragraph) |
| ('(?!', '(?!' + block.gfm.fences.source.replace(/(^|[^\[])\^/g, '$1') + '|') |
| (); |
| |
| /** |
| * Block Lexer |
| */ |
| |
| block.lexer = function(src) { |
| var tokens = []; |
| |
| tokens.links = {}; |
| |
| src = src |
| .replace(/\r\n|\r/g, '\n') |
| .replace(/\t/g, ' '); |
| |
| return block.token(src, tokens, true); |
| }; |
| |
| block.token = function(src, tokens, top) { |
| var src = src.replace(/^ +$/gm, '') |
| , next |
| , loose |
| , cap |
| , item |
| , space |
| , i |
| , l; |
| |
| while (src) { |
| // newline |
| if (cap = block.newline.exec(src)) { |
| src = src.substring(cap[0].length); |
| if (cap[0].length > 1) { |
| tokens.push({ |
| type: 'space' |
| }); |
| } |
| } |
| |
| // code |
| if (cap = block.code.exec(src)) { |
| src = src.substring(cap[0].length); |
| cap = cap[0].replace(/^ {4}/gm, ''); |
| tokens.push({ |
| type: 'code', |
| text: !options.pedantic |
| ? cap.replace(/\n+$/, '') |
| : cap |
| }); |
| continue; |
| } |
| |
| // fences (gfm) |
| if (cap = block.fences.exec(src)) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'code', |
| lang: cap[1], |
| text: cap[2] |
| }); |
| continue; |
| } |
| |
| // heading |
| if (cap = block.heading.exec(src)) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'heading', |
| depth: cap[1].length, |
| text: cap[2] |
| }); |
| continue; |
| } |
| |
| // lheading |
| if (cap = block.lheading.exec(src)) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'heading', |
| depth: cap[2] === '=' ? 1 : 2, |
| text: cap[1] |
| }); |
| continue; |
| } |
| |
| // hr |
| if (cap = block.hr.exec(src)) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'hr' |
| }); |
| continue; |
| } |
| |
| // blockquote |
| if (cap = block.blockquote.exec(src)) { |
| src = src.substring(cap[0].length); |
| |
| 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. |
| block.token(cap, tokens, top); |
| |
| tokens.push({ |
| type: 'blockquote_end' |
| }); |
| |
| continue; |
| } |
| |
| // list |
| if (cap = block.list.exec(src)) { |
| src = src.substring(cap[0].length); |
| |
| tokens.push({ |
| type: 'list_start', |
| ordered: isFinite(cap[2]) |
| }); |
| |
| // Get each top-level item. |
| cap = cap[0].match(block.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 = !options.pedantic |
| ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '') |
| : item.replace(/^ {1,4}/gm, ''); |
| } |
| |
| // 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; |
| } |
| |
| tokens.push({ |
| type: loose |
| ? 'loose_item_start' |
| : 'list_item_start' |
| }); |
| |
| // Recurse. |
| block.token(item, tokens); |
| |
| tokens.push({ |
| type: 'list_item_end' |
| }); |
| } |
| |
| tokens.push({ |
| type: 'list_end' |
| }); |
| |
| continue; |
| } |
| |
| // html |
| if (cap = block.html.exec(src)) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'html', |
| pre: cap[1] === 'pre', |
| text: cap[0] |
| }); |
| continue; |
| } |
| |
| // def |
| if (top && (cap = block.def.exec(src))) { |
| src = src.substring(cap[0].length); |
| tokens.links[cap[1].toLowerCase()] = { |
| href: cap[2], |
| title: cap[3] |
| }; |
| continue; |
| } |
| |
| // top-level paragraph |
| if (top && (cap = block.paragraph.exec(src))) { |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'paragraph', |
| text: cap[0] |
| }); |
| continue; |
| } |
| |
| // text |
| if (cap = block.text.exec(src)) { |
| // Top-level should never reach here. |
| src = src.substring(cap[0].length); |
| tokens.push({ |
| type: 'text', |
| text: cap[0] |
| }); |
| continue; |
| } |
| } |
| |
| return tokens; |
| }; |
| |
| /** |
| * Inline Processing |
| */ |
| |
| var inline = { |
| escape: /^\\([\\`*{}\[\]()#+\-.!_>])/, |
| autolink: /^<([^ >]+(@|:\/)[^ >]+)>/, |
| url: noop, |
| tag: /^<!--[^\0]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/, |
| link: /^!?\[(inside)\]\(href\)/, |
| reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/, |
| nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/, |
| strong: /^__([^\0]+?)__(?!_)|^\*\*([^\0]+?)\*\*(?!\*)/, |
| em: /^\b_((?:__|[^\0])+?)_\b|^\*((?:\*\*|[^\0])+?)\*(?!\*)/, |
| code: /^(`+)([^\0]*?[^`])\1(?!`)/, |
| br: /^ {2,}\n(?!\s*$)/, |
| text: /^[^\0]+?(?=[\\<!\[_*`]| {2,}\n|$)/ |
| }; |
| |
| inline._linkInside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/; |
| inline._linkHref = /\s*<?([^\s]*?)>?(?:\s+['"]([^\0]*?)['"])?\s*/; |
| |
| inline.link = replace(inline.link) |
| ('inside', inline._linkInside) |
| ('href', inline._linkHref) |
| (); |
| |
| inline.reflink = replace(inline.reflink) |
| ('inside', inline._linkInside) |
| (); |
| |
| inline.normal = { |
| url: inline.url, |
| strong: inline.strong, |
| em: inline.em, |
| text: inline.text |
| }; |
| |
| inline.pedantic = { |
| strong: /^__(?=\S)([^\0]*?\S)__(?!_)|^\*\*(?=\S)([^\0]*?\S)\*\*(?!\*)/, |
| em: /^_(?=\S)([^\0]*?\S)_(?!_)|^\*(?=\S)([^\0]*?\S)\*(?!\*)/ |
| }; |
| |
| inline.gfm = { |
| url: /^(https?:\/\/[^\s]+[^.,:;"')\]\s])/, |
| text: /^[^\0]+?(?=[\\<!\[_*`]|https?:\/\/| {2,}\n|$)/ |
| }; |
| |
| /** |
| * Inline Lexer |
| */ |
| |
| inline.lexer = function(src) { |
| var out = '' |
| , links = tokens.links |
| , link |
| , text |
| , href |
| , cap; |
| |
| while (src) { |
| // escape |
| if (cap = inline.escape.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += cap[1]; |
| continue; |
| } |
| |
| // autolink |
| if (cap = inline.autolink.exec(src)) { |
| src = src.substring(cap[0].length); |
| if (cap[2] === '@') { |
| text = cap[1][6] === ':' |
| ? mangle(cap[1].substring(7)) |
| : mangle(cap[1]); |
| href = mangle('mailto:') + text; |
| } else { |
| text = escape(cap[1]); |
| href = text; |
| } |
| out += '<a href="' |
| + href |
| + '">' |
| + text |
| + '</a>'; |
| continue; |
| } |
| |
| // url (gfm) |
| if (cap = inline.url.exec(src)) { |
| src = src.substring(cap[0].length); |
| text = escape(cap[1]); |
| href = text; |
| out += '<a href="' |
| + href |
| + '">' |
| + text |
| + '</a>'; |
| continue; |
| } |
| |
| // tag |
| if (cap = inline.tag.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += options.sanitize |
| ? escape(cap[0]) |
| : cap[0]; |
| continue; |
| } |
| |
| // link |
| if (cap = inline.link.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += outputLink(cap, { |
| href: cap[2], |
| title: cap[3] |
| }); |
| continue; |
| } |
| |
| // reflink, nolink |
| if ((cap = inline.reflink.exec(src)) |
| || (cap = inline.nolink.exec(src))) { |
| src = src.substring(cap[0].length); |
| link = (cap[2] || cap[1]).replace(/\s+/g, ' '); |
| link = links[link.toLowerCase()]; |
| if (!link || !link.href) { |
| out += cap[0][0]; |
| src = cap[0].substring(1) + src; |
| continue; |
| } |
| out += outputLink(cap, link); |
| continue; |
| } |
| |
| // strong |
| if (cap = inline.strong.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += '<strong>' |
| + inline.lexer(cap[2] || cap[1]) |
| + '</strong>'; |
| continue; |
| } |
| |
| // em |
| if (cap = inline.em.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += '<em>' |
| + inline.lexer(cap[2] || cap[1]) |
| + '</em>'; |
| continue; |
| } |
| |
| // code |
| if (cap = inline.code.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += '<code>' |
| + escape(cap[2], true) |
| + '</code>'; |
| continue; |
| } |
| |
| // br |
| if (cap = inline.br.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += '<br>'; |
| continue; |
| } |
| |
| // text |
| if (cap = inline.text.exec(src)) { |
| src = src.substring(cap[0].length); |
| out += escape(cap[0]); |
| continue; |
| } |
| } |
| |
| return out; |
| }; |
| |
| function outputLink(cap, link) { |
| if (cap[0][0] !== '!') { |
| return '<a href="' |
| + escape(link.href) |
| + '"' |
| + (link.title |
| ? ' title="' |
| + escape(link.title) |
| + '"' |
| : '') |
| + '>' |
| + inline.lexer(cap[1]) |
| + '</a>'; |
| } else { |
| return '<img src="' |
| + escape(link.href) |
| + '" alt="' |
| + escape(cap[1]) |
| + '"' |
| + (link.title |
| ? ' title="' |
| + escape(link.title) |
| + '"' |
| : '') |
| + '>'; |
| } |
| } |
| |
| /** |
| * Parsing |
| */ |
| |
| var tokens |
| , token; |
| |
| function next() { |
| return token = tokens.pop(); |
| } |
| |
| function tok() { |
| switch (token.type) { |
| case 'space': { |
| return ''; |
| } |
| case 'hr': { |
| return '<hr>\n'; |
| } |
| case 'heading': { |
| return '<h' |
| + token.depth |
| + '>' |
| + inline.lexer(token.text) |
| + '</h' |
| + token.depth |
| + '>\n'; |
| } |
| case 'code': { |
| if (options.highlight) { |
| token.code = options.highlight(token.text, token.lang); |
| if (token.code != null && token.code !== token.text) { |
| token.escaped = true; |
| token.text = token.code; |
| } |
| } |
| |
| if (!token.escaped) { |
| token.text = escape(token.text, true); |
| } |
| |
| return '<pre><code' |
| + (token.lang |
| ? ' class="lang-' |
| + token.lang |
| + '"' |
| : '') |
| + '>' |
| + token.text |
| + '</code></pre>\n'; |
| } |
| case 'blockquote_start': { |
| var body = ''; |
| |
| while (next().type !== 'blockquote_end') { |
| body += tok(); |
| } |
| |
| return '<blockquote>\n' |
| + body |
| + '</blockquote>\n'; |
| } |
| case 'list_start': { |
| var type = token.ordered ? 'ol' : 'ul' |
| , body = ''; |
| |
| while (next().type !== 'list_end') { |
| body += tok(); |
| } |
| |
| return '<' |
| + type |
| + '>\n' |
| + body |
| + '</' |
| + type |
| + '>\n'; |
| } |
| case 'list_item_start': { |
| var body = ''; |
| |
| while (next().type !== 'list_item_end') { |
| body += token.type === 'text' |
| ? parseText() |
| : tok(); |
| } |
| |
| return '<li>' |
| + body |
| + '</li>\n'; |
| } |
| case 'loose_item_start': { |
| var body = ''; |
| |
| while (next().type !== 'list_item_end') { |
| body += tok(); |
| } |
| |
| return '<li>' |
| + body |
| + '</li>\n'; |
| } |
| case 'html': { |
| if (options.sanitize) { |
| return inline.lexer(token.text); |
| } |
| return !token.pre && !options.pedantic |
| ? inline.lexer(token.text) |
| : token.text; |
| } |
| case 'paragraph': { |
| return '<p>' |
| + inline.lexer(token.text) |
| + '</p>\n'; |
| } |
| case 'text': { |
| return '<p>' |
| + parseText() |
| + '</p>\n'; |
| } |
| } |
| } |
| |
| function parseText() { |
| var body = token.text |
| , top; |
| |
| while ((top = tokens[tokens.length-1]) |
| && top.type === 'text') { |
| body += '\n' + next().text; |
| } |
| |
| return inline.lexer(body); |
| } |
| |
| function parse(src) { |
| tokens = src.reverse(); |
| |
| var out = ''; |
| while (next()) { |
| out += tok(); |
| } |
| |
| tokens = null; |
| token = null; |
| |
| return out; |
| } |
| |
| /** |
| * Helpers |
| */ |
| |
| function escape(html, encode) { |
| return html |
| .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&') |
| .replace(/</g, '<') |
| .replace(/>/g, '>') |
| .replace(/"/g, '"') |
| .replace(/'/g, '''); |
| } |
| |
| function mangle(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; |
| } |
| |
| function tag() { |
| var 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+'; |
| |
| return tag; |
| } |
| |
| function replace(regex, opt) { |
| regex = regex.source; |
| opt = opt || ''; |
| return function self(name, val) { |
| if (!name) return new RegExp(regex, opt); |
| regex = regex.replace(name, val.source || val); |
| return self; |
| }; |
| } |
| |
| function noop() {} |
| noop.exec = noop; |
| |
| /** |
| * Marked |
| */ |
| |
| function marked(src, opt) { |
| setOptions(opt); |
| return parse(block.lexer(src)); |
| } |
| |
| /** |
| * Options |
| */ |
| |
| var options |
| , defaults; |
| |
| function setOptions(opt) { |
| if (!opt) opt = defaults; |
| if (options === opt) return; |
| options = opt; |
| |
| if (options.gfm) { |
| block.fences = block.gfm.fences; |
| block.paragraph = block.gfm.paragraph; |
| inline.text = inline.gfm.text; |
| inline.url = inline.gfm.url; |
| } else { |
| block.fences = block.normal.fences; |
| block.paragraph = block.normal.paragraph; |
| inline.text = inline.normal.text; |
| inline.url = inline.normal.url; |
| } |
| |
| if (options.pedantic) { |
| inline.em = inline.pedantic.em; |
| inline.strong = inline.pedantic.strong; |
| } else { |
| inline.em = inline.normal.em; |
| inline.strong = inline.normal.strong; |
| } |
| } |
| |
| marked.options = |
| marked.setOptions = function(opt) { |
| defaults = opt; |
| setOptions(opt); |
| return marked; |
| }; |
| |
| marked.setOptions({ |
| gfm: true, |
| pedantic: false, |
| sanitize: false, |
| highlight: null |
| }); |
| |
| /** |
| * Expose |
| */ |
| |
| marked.parser = function(src, opt) { |
| setOptions(opt); |
| return parse(src); |
| }; |
| |
| marked.lexer = function(src, opt) { |
| setOptions(opt); |
| return block.lexer(src); |
| }; |
| |
| marked.parse = marked; |
| |
| if (typeof module !== 'undefined') { |
| module.exports = marked; |
| } else { |
| this.marked = marked; |
| } |
| |
| }).call(function() { |
| return this || (typeof window !== 'undefined' ? window : global); |
| }()); |