vendor/assets/javascripts/marked.js in marked-rails-0.2.10.1 vs vendor/assets/javascripts/marked.js in marked-rails-0.3.2.0

- old
+ new

@@ -1,8 +1,8 @@ /** * marked - a markdown parser - * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) + * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed) * https://github.com/chjj/marked */ ;(function() { @@ -16,12 +16,12 @@ fences: noop, hr: /^( *[-*_]){3,} *(?:\n+|$)/, heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/, nptable: noop, lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/, - blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/, - list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/, + blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/, + list: /^( *)(bull) [\s\S]+?(?:hr|def|\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]+/ @@ -33,17 +33,22 @@ (/bull/g, block.bullet) (); block.list = replace(block.list) (/bull/g, block.bullet) - ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/) + ('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+(?!:/|@)\\b'; + + '|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(?:"[^"]*"|'[^']*'|[^'">])*?>/) @@ -139,11 +144,11 @@ /** * Lexing */ -Lexer.prototype.token = function(src, top) { +Lexer.prototype.token = function(src, top, bq) { var src = src.replace(/^ +$/gm, '') , next , loose , cap , bull @@ -262,11 +267,11 @@ 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.token(cap, top, true); this.tokens.push({ type: 'blockquote_end' }); @@ -331,11 +336,11 @@ ? 'loose_item_start' : 'list_item_start' }); // Recurse. - this.token(item, false); + this.token(item, false, bq); this.tokens.push({ type: 'list_item_end' }); } @@ -359,11 +364,11 @@ }); continue; } // def - if (top && (cap = this.rules.def.exec(src))) { + 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] }; @@ -512,10 +517,12 @@ 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.'); } @@ -575,47 +582,46 @@ href = this.mangle('mailto:') + text; } else { text = escape(cap[1]); href = text; } - out += '<a href="' - + href - + '">' - + text - + '</a>'; + out += this.renderer.link(href, null, text); continue; } // url (gfm) - if (cap = this.rules.url.exec(src)) { + if (!this.inLink && (cap = this.rules.url.exec(src))) { src = src.substring(cap[0].length); text = escape(cap[1]); href = text; - out += '<a href="' - + href - + '">' - + text - + '</a>'; + 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 ? 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)) @@ -626,54 +632,48 @@ 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 += '<strong>' - + this.output(cap[2] || cap[1]) - + '</strong>'; + 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 += '<em>' - + this.output(cap[2] || cap[1]) - + '</em>'; + 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 += '<code>' - + escape(cap[2], true) - + '</code>'; + out += this.renderer.codespan(escape(cap[2], true)); continue; } // br if (cap = this.rules.br.exec(src)) { src = src.substring(cap[0].length); - out += '<br>'; + out += this.renderer.br(); continue; } // del (gfm) if (cap = this.rules.del.exec(src)) { src = src.substring(cap[0].length); - out += '<del>' - + this.output(cap[1]) - + '</del>'; + out += this.renderer.del(this.output(cap[1])); continue; } // text if (cap = this.rules.text.exec(src)) { @@ -694,35 +694,16 @@ /** * Compile Link */ InlineLexer.prototype.outputLink = function(cap, link) { - if (cap[0].charAt(0) !== '!') { - return '<a href="' - + escape(link.href) - + '"' - + (link.title - ? ' title="' - + escape(link.title) - + '"' - : '') - + '>' - + this.output(cap[1]) - + '</a>'; - } else { - return '<img src="' - + escape(link.href) - + '" alt="' - + escape(cap[1]) - + '"' - + (link.title - ? ' title="' - + escape(link.title) - + '"' - : '') - + '>'; - } + 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 */ @@ -764,34 +745,180 @@ 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 tag = flags.align + ? '<' + type + ' style="text-align:' + flags.align + '">' + : '<' + type + '>'; + 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) { + 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; +}; + +/** * 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) { - var parser = new Parser(options); +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.inline = new InlineLexer(src.links, this.options, this.renderer); this.tokens = src.reverse(); var out = ''; while (this.next()) { out += this.tok(); @@ -838,153 +965,108 @@ switch (this.token.type) { case 'space': { return ''; } case 'hr': { - return '<hr>\n'; + return this.renderer.hr(); } case 'heading': { - return '<h' - + this.token.depth - + ' id="' - + this.token.text.toLowerCase().replace(/[^\w]+/g, '-') - + '">' - + this.inline.output(this.token.text) - + '</h' - + this.token.depth - + '>\n'; + return this.renderer.heading( + this.inline.output(this.token.text), + this.token.depth, + this.token.text); } case 'code': { - if (this.options.highlight) { - var code = this.options.highlight(this.token.text, this.token.lang); - if (code != null && code !== this.token.text) { - this.token.escaped = true; - this.token.text = code; - } - } - - if (!this.token.escaped) { - this.token.text = escape(this.token.text, true); - } - - return '<pre><code' - + (this.token.lang - ? ' class="' - + this.options.langPrefix - + this.token.lang - + '"' - : '') - + '>' - + this.token.text - + '</code></pre>\n'; + return this.renderer.code(this.token.text, + this.token.lang, + this.token.escaped); } case 'table': { - var body = '' - , heading + var header = '' + , body = '' , i , row , cell + , flags , j; // header - body += '<thead>\n<tr>\n'; + cell = ''; for (i = 0; i < this.token.header.length; i++) { - heading = this.inline.output(this.token.header[i]); - body += '<th'; - if (this.token.align[i]) { - body += ' style="text-align:' + this.token.align[i] + '"'; - } - body += '>' + heading + '</th>\n'; + 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] } + ); } - body += '</tr>\n</thead>\n'; + header += this.renderer.tablerow(cell); - // body - body += '<tbody>\n' for (i = 0; i < this.token.cells.length; i++) { row = this.token.cells[i]; - body += '<tr>\n'; + + cell = ''; for (j = 0; j < row.length; j++) { - cell = this.inline.output(row[j]); - body += '<td'; - if (this.token.align[j]) { - body += ' style="text-align:' + this.token.align[j] + '"'; - } - body += '>' + cell + '</td>\n'; + cell += this.renderer.tablecell( + this.inline.output(row[j]), + { header: false, align: this.token.align[j] } + ); } - body += '</tr>\n'; - } - body += '</tbody>\n'; - return '<table>\n' - + body - + '</table>\n'; + 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 '<blockquote>\n' - + body - + '</blockquote>\n'; + return this.renderer.blockquote(body); } case 'list_start': { - var type = this.token.ordered ? 'ol' : 'ul' - , body = ''; + var body = '' + , ordered = this.token.ordered; while (this.next().type !== 'list_end') { body += this.tok(); } - return '<' - + type - + '>\n' - + body - + '</' - + type - + '>\n'; + 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 '<li>' - + body - + '</li>\n'; + return this.renderer.listitem(body); } case 'loose_item_start': { var body = ''; while (this.next().type !== 'list_item_end') { body += this.tok(); } - return '<li>' - + body - + '</li>\n'; + return this.renderer.listitem(body); } case 'html': { - return !this.token.pre && !this.options.pedantic + 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 '<p>' - + this.inline.output(this.token.text) - + '</p>\n'; + return this.renderer.paragraph(this.inline.output(this.token.text)); } case 'text': { - return '<p>' - + this.parseText() - + '</p>\n'; + return this.renderer.paragraph(this.parseText()); } } }; /** @@ -998,10 +1080,23 @@ .replace(/>/g, '&gt;') .replace(/"/g, '&quot;') .replace(/'/g, '&#39;'); } +function unescape(html) { + return html.replace(/&([#\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); @@ -1030,10 +1125,11 @@ } return obj; } + /** * Marked */ function marked(src, opt, callback) { @@ -1132,18 +1228,23 @@ sanitize: false, smartLists: false, silent: false, highlight: null, langPrefix: 'lang-', - smartypants: false + 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;