1 /**2 * marked - a markdown parser3 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)4 * https://github.com/chjj/marked5 */6 7 ;(function() {8 9 /**10 * Block-Level Grammar11 */12 13 var block = {14 newline: /^\n+/,15 code: /^( {4}[^\n]+\n*)+/,16 fences: noop,17 hr: /^( *[-*_]){3,} *(?:\n+|$)/,18 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,19 nptable: noop,20 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,21 blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,22 list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,23 html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,24 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,25 table: noop,26 paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,27 text: /^[^\n]+/28 };29 30 block.bullet = /(?:[*+-]|\d+\.)/;31 block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;32 block.item = replace(block.item, 'gm')33 (/bull/g, block.bullet)34 ();35 36 block.list = replace(block.list)37 (/bull/g, block.bullet)38 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')39 ('def', '\\n+(?=' + block.def.source + ')')40 ();41 42 block.blockquote = replace(block.blockquote)43 ('def', block.def)44 ();45 46 block._tag = '(?!(?:'47 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'48 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'49 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';50 51 block.html = replace(block.html)52 ('comment', /<!--[\s\S]*?-->/)53 ('closed', /<(tag)[\s\S]+?<\/\1>/)54 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)55 (/tag/g, block._tag)56 ();57 58 block.paragraph = replace(block.paragraph)59 ('hr', block.hr)60 ('heading', block.heading)61 ('lheading', block.lheading)62 ('blockquote', block.blockquote)63 ('tag', '<' + block._tag)64 ('def', block.def)65 ();66 67 /**68 * Normal Block Grammar69 */70 71 block.normal = merge({}, block);72 73 /**74 * GFM Block Grammar75 */76 77 block.gfm = merge({}, block.normal, {78 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,79 paragraph: /^/,80 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/81 });82 83 block.gfm.paragraph = replace(block.paragraph)84 ('(?!', '(?!'85 + block.gfm.fences.source.replace('\\1', '\\2') + '|'86 + block.list.source.replace('\\1', '\\3') + '|')87 ();88 89 /**90 * GFM + Tables Block Grammar91 */92 93 block.tables = merge({}, block.gfm, {94 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,95 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/96 });97 98 /**99 * Block Lexer100 */101 102 function Lexer(options) {103 this.tokens = [];104 this.tokens.links = {};105 this.options = options || marked.defaults;106 this.rules = block.normal;107 108 if (this.options.gfm) {109 if (this.options.tables) {110 this.rules = block.tables;111 } else {112 this.rules = block.gfm;113 }114 }115 }116 117 /**118 * Expose Block Rules119 */120 121 Lexer.rules = block;122 123 /**124 * Static Lex Method125 */126 127 Lexer.lex = function(src, options) {128 var lexer = new Lexer(options);129 return lexer.lex(src);130 };131 132 /**133 * Preprocessing134 */135 136 Lexer.prototype.lex = function(src) {137 src = src138 .replace(/\r\n|\r/g, '\n')139 .replace(/\t/g, ' ')140 .replace(/\u00a0/g, ' ')141 .replace(/\u2424/g, '\n');142 143 return this.token(src, true);144 };145 146 /**147 * Lexing148 */149 150 Lexer.prototype.token = function(src, top, bq) {151 var src = src.replace(/^ +$/gm, '')152 , next153 , loose154 , cap155 , bull156 , b157 , item158 , space159 , i160 , l;161 162 while (src) {163 // newline164 if (cap = this.rules.newline.exec(src)) {165 src = src.substring(cap[0].length);166 if (cap[0].length > 1) {167 this.tokens.push({168 type: 'space'169 });170 }171 }172 173 // code174 if (cap = this.rules.code.exec(src)) {175 src = src.substring(cap[0].length);176 cap = cap[0].replace(/^ {4}/gm, '');177 this.tokens.push({178 type: 'code',179 text: !this.options.pedantic180 ? cap.replace(/\n+$/, '')181 : cap182 });183 continue;184 }185 186 // fences (gfm)187 if (cap = this.rules.fences.exec(src)) {188 src = src.substring(cap[0].length);189 this.tokens.push({190 type: 'code',191 lang: cap[2],192 text: cap[3] || ''193 });194 continue;195 }196 197 // heading198 if (cap = this.rules.heading.exec(src)) {199 src = src.substring(cap[0].length);200 this.tokens.push({201 type: 'heading',202 depth: cap[1].length,203 text: cap[2]204 });205 continue;206 }207 208 // table no leading pipe (gfm)209 if (top && (cap = this.rules.nptable.exec(src))) {210 src = src.substring(cap[0].length);211 212 item = {213 type: 'table',214 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),215 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),216 cells: cap[3].replace(/\n$/, '').split('\n')217 };218 219 for (i = 0; i < item.align.length; i++) {220 if (/^ *-+: *$/.test(item.align[i])) {221 item.align[i] = 'right';222 } else if (/^ *:-+: *$/.test(item.align[i])) {223 item.align[i] = 'center';224 } else if (/^ *:-+ *$/.test(item.align[i])) {225 item.align[i] = 'left';226 } else {227 item.align[i] = null;228 }229 }230 231 for (i = 0; i < item.cells.length; i++) {232 item.cells[i] = item.cells[i].split(/ *\| */);233 }234 235 this.tokens.push(item);236 237 continue;238 }239 240 // lheading241 if (cap = this.rules.lheading.exec(src)) {242 src = src.substring(cap[0].length);243 this.tokens.push({244 type: 'heading',245 depth: cap[2] === '=' ? 1 : 2,246 text: cap[1]247 });248 continue;249 }250 251 // hr252 if (cap = this.rules.hr.exec(src)) {253 src = src.substring(cap[0].length);254 this.tokens.push({255 type: 'hr'256 });257 continue;258 }259 260 // blockquote261 if (cap = this.rules.blockquote.exec(src)) {262 src = src.substring(cap[0].length);263 264 this.tokens.push({265 type: 'blockquote_start'266 });267 268 cap = cap[0].replace(/^ *> ?/gm, '');269 270 // Pass `top` to keep the current271 // "toplevel" state. This is exactly272 // how markdown.pl works.273 this.token(cap, top, true);274 275 this.tokens.push({276 type: 'blockquote_end'277 });278 279 continue;280 }281 282 // list283 if (cap = this.rules.list.exec(src)) {284 src = src.substring(cap[0].length);285 bull = cap[2];286 287 this.tokens.push({288 type: 'list_start',289 ordered: bull.length > 1290 });291 292 // Get each top-level item.293 cap = cap[0].match(this.rules.item);294 295 next = false;296 l = cap.length;297 i = 0;298 299 for (; i < l; i++) {300 item = cap[i];301 302 // Remove the list item's bullet303 // so it is seen as the next token.304 space = item.length;305 item = item.replace(/^ *([*+-]|\d+\.) +/, '');306 307 // Outdent whatever the308 // list item contains. Hacky.309 if (~item.indexOf('\n ')) {310 space -= item.length;311 item = !this.options.pedantic312 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')313 : item.replace(/^ {1,4}/gm, '');314 }315 316 // Determine whether the next list item belongs here.317 // Backpedal if it does not belong in this list.318 if (this.options.smartLists && i !== l - 1) {319 b = block.bullet.exec(cap[i + 1])[0];320 if (bull !== b && !(bull.length > 1 && b.length > 1)) {321 src = cap.slice(i + 1).join('\n') + src;322 i = l - 1;323 }324 }325 326 // Determine whether item is loose or not.327 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/328 // for discount behavior.329 loose = next || /\n\n(?!\s*$)/.test(item);330 if (i !== l - 1) {331 next = item.charAt(item.length - 1) === '\n';332 if (!loose) loose = next;333 }334 335 this.tokens.push({336 type: loose337 ? 'loose_item_start'338 : 'list_item_start'339 });340 341 // Recurse.342 this.token(item, false, bq);343 344 this.tokens.push({345 type: 'list_item_end'346 });347 }348 349 this.tokens.push({350 type: 'list_end'351 });352 353 continue;354 }355 356 // html357 if (cap = this.rules.html.exec(src)) {358 src = src.substring(cap[0].length);359 this.tokens.push({360 type: this.options.sanitize361 ? 'paragraph'362 : 'html',363 pre: !this.options.sanitizer364 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),365 text: cap[0]366 });367 continue;368 }369 370 // def371 if ((!bq && top) && (cap = this.rules.def.exec(src))) {372 src = src.substring(cap[0].length);373 this.tokens.links[cap[1].toLowerCase()] = {374 href: cap[2],375 title: cap[3]376 };377 continue;378 }379 380 // table (gfm)381 if (top && (cap = this.rules.table.exec(src))) {382 src = src.substring(cap[0].length);383 384 item = {385 type: 'table',386 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),387 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),388 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')389 };390 391 for (i = 0; i < item.align.length; i++) {392 if (/^ *-+: *$/.test(item.align[i])) {393 item.align[i] = 'right';394 } else if (/^ *:-+: *$/.test(item.align[i])) {395 item.align[i] = 'center';396 } else if (/^ *:-+ *$/.test(item.align[i])) {397 item.align[i] = 'left';398 } else {399 item.align[i] = null;400 }401 }402 403 for (i = 0; i < item.cells.length; i++) {404 item.cells[i] = item.cells[i]405 .replace(/^ *\| *| *\| *$/g, '')406 .split(/ *\| */);407 }408 409 this.tokens.push(item);410 411 continue;412 }413 414 // top-level paragraph415 if (top && (cap = this.rules.paragraph.exec(src))) {416 src = src.substring(cap[0].length);417 this.tokens.push({418 type: 'paragraph',419 text: cap[1].charAt(cap[1].length - 1) === '\n'420 ? cap[1].slice(0, -1)421 : cap[1]422 });423 continue;424 }425 426 // text427 if (cap = this.rules.text.exec(src)) {428 // Top-level should never reach here.429 src = src.substring(cap[0].length);430 this.tokens.push({431 type: 'text',432 text: cap[0]433 });434 continue;435 }436 437 if (src) {438 throw new439 Error('Infinite loop on byte: ' + src.charCodeAt(0));440 }441 }442 443 return this.tokens;444 };445 446 /**447 * Inline-Level Grammar448 */449 450 var inline = {451 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,452 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,453 url: noop,454 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,455 link: /^!?\[(inside)\]\(href\)/,456 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,457 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,458 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,459 em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,460 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,461 br: /^ {2,}\n(?!\s*$)/,462 del: noop,463 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/464 };465 466 inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;467 inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;468 469 inline.link = replace(inline.link)470 ('inside', inline._inside)471 ('href', inline._href)472 ();473 474 inline.reflink = replace(inline.reflink)475 ('inside', inline._inside)476 ();477 478 /**479 * Normal Inline Grammar480 */481 482 inline.normal = merge({}, inline);483 484 /**485 * Pedantic Inline Grammar486 */487 488 inline.pedantic = merge({}, inline.normal, {489 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,490 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/491 });492 493 /**494 * GFM Inline Grammar495 */496 497 inline.gfm = merge({}, inline.normal, {498 escape: replace(inline.escape)('])', '~|])')(),499 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,500 del: /^~~(?=\S)([\s\S]*?\S)~~/,501 text: replace(inline.text)502 (']|', '~]|')503 ('|', '|https?://|')504 ()505 });506 507 /**508 * GFM + Line Breaks Inline Grammar509 */510 511 inline.breaks = merge({}, inline.gfm, {512 br: replace(inline.br)('{2,}', '*')(),513 text: replace(inline.gfm.text)('{2,}', '*')()514 });515 516 /**517 * Inline Lexer & Compiler518 */519 520 function InlineLexer(links, options) {521 this.options = options || marked.defaults;522 this.links = links;523 this.rules = inline.normal;524 this.renderer = this.options.renderer || new Renderer;525 this.renderer.options = this.options;526 527 if (!this.links) {528 throw new529 Error('Tokens array requires a `links` property.');530 }531 532 if (this.options.gfm) {533 if (this.options.breaks) {534 this.rules = inline.breaks;535 } else {536 this.rules = inline.gfm;537 }538 } else if (this.options.pedantic) {539 this.rules = inline.pedantic;540 }541 }542 543 /**544 * Expose Inline Rules545 */546 547 InlineLexer.rules = inline;548 549 /**550 * Static Lexing/Compiling Method551 */552 553 InlineLexer.output = function(src, links, options) {554 var inline = new InlineLexer(links, options);555 return inline.output(src);556 };557 558 /**559 * Lexing/Compiling560 */561 562 InlineLexer.prototype.output = function(src) {563 var out = ''564 , link565 , text566 , href567 , cap;568 569 while (src) {570 // escape571 if (cap = this.rules.escape.exec(src)) {572 src = src.substring(cap[0].length);573 out += cap[1];574 continue;575 }576 577 // autolink578 if (cap = this.rules.autolink.exec(src)) {579 src = src.substring(cap[0].length);580 if (cap[2] === '@') {581 text = cap[1].charAt(6) === ':'582 ? this.mangle(cap[1].substring(7))583 : this.mangle(cap[1]);584 href = this.mangle('mailto:') + text;585 } else {586 text = escape(cap[1]);587 href = text;588 }589 out += this.renderer.link(href, null, text);590 continue;591 }592 593 // url (gfm)594 if (!this.inLink && (cap = this.rules.url.exec(src))) {595 src = src.substring(cap[0].length);596 text = escape(cap[1]);597 href = text;598 out += this.renderer.link(href, null, text);599 continue;600 }601 602 // tag603 if (cap = this.rules.tag.exec(src)) {604 if (!this.inLink && /^<a /i.test(cap[0])) {605 this.inLink = true;606 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {607 this.inLink = false;608 }609 src = src.substring(cap[0].length);610 out += this.options.sanitize611 ? this.options.sanitizer612 ? this.options.sanitizer(cap[0])613 : escape(cap[0])614 : cap[0]615 continue;616 }617 618 // link619 if (cap = this.rules.link.exec(src)) {620 src = src.substring(cap[0].length);621 this.inLink = true;622 out += this.outputLink(cap, {623 href: cap[2],624 title: cap[3]625 });626 this.inLink = false;627 continue;628 }629 630 // reflink, nolink631 if ((cap = this.rules.reflink.exec(src))632 || (cap = this.rules.nolink.exec(src))) {633 src = src.substring(cap[0].length);634 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');635 link = this.links[link.toLowerCase()];636 if (!link || !link.href) {637 out += cap[0].charAt(0);638 src = cap[0].substring(1) + src;639 continue;640 }641 this.inLink = true;642 out += this.outputLink(cap, link);643 this.inLink = false;644 continue;645 }646 647 // strong648 if (cap = this.rules.strong.exec(src)) {649 src = src.substring(cap[0].length);650 out += this.renderer.strong(this.output(cap[2] || cap[1]));651 continue;652 }653 654 // em655 if (cap = this.rules.em.exec(src)) {656 src = src.substring(cap[0].length);657 out += this.renderer.em(this.output(cap[2] || cap[1]));658 continue;659 }660 661 // code662 if (cap = this.rules.code.exec(src)) {663 src = src.substring(cap[0].length);664 out += this.renderer.codespan(escape(cap[2], true));665 continue;666 }667 668 // br669 if (cap = this.rules.br.exec(src)) {670 src = src.substring(cap[0].length);671 out += this.renderer.br();672 continue;673 }674 675 // del (gfm)676 if (cap = this.rules.del.exec(src)) {677 src = src.substring(cap[0].length);678 out += this.renderer.del(this.output(cap[1]));679 continue;680 }681 682 // text683 if (cap = this.rules.text.exec(src)) {684 src = src.substring(cap[0].length);685 out += this.renderer.text(escape(this.smartypants(cap[0])));686 continue;687 }688 689 if (src) {690 throw new691 Error('Infinite loop on byte: ' + src.charCodeAt(0));692 }693 }694 695 return out;696 };697 698 /**699 * Compile Link700 */701 702 InlineLexer.prototype.outputLink = function(cap, link) {703 var href = escape(link.href)704 , title = link.title ? escape(link.title) : null;705 706 return cap[0].charAt(0) !== '!'707 ? this.renderer.link(href, title, this.output(cap[1]))708 : this.renderer.image(href, title, escape(cap[1]));709 };710 711 /**712 * Smartypants Transformations713 */714 715 InlineLexer.prototype.smartypants = function(text) {716 if (!this.options.smartypants) return text;717 return text718 // em-dashes719 .replace(/---/g, '\u2014')720 // en-dashes721 .replace(/--/g, '\u2013')722 // opening singles723 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')724 // closing singles & apostrophes725 .replace(/'/g, '\u2019')726 // opening doubles727 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')728 // closing doubles729 .replace(/"/g, '\u201d')730 // ellipses731 .replace(/\.{3}/g, '\u2026');732 };733 734 /**735 * Mangle Links736 */737 738 InlineLexer.prototype.mangle = function(text) {739 if (!this.options.mangle) return text;740 var out = ''741 , l = text.length742 , i = 0743 , ch;744 745 for (; i < l; i++) {746 ch = text.charCodeAt(i);747 if (Math.random() > 0.5) {748 ch = 'x' + ch.toString(16);749 }750 out += '&#' + ch + ';';751 }752 753 return out;754 };755 756 /**757 * Renderer758 */759 760 function Renderer(options) {761 this.options = options || {};762 }763 764 Renderer.prototype.code = function(code, lang, escaped) {765 if (this.options.highlight) {766 var out = this.options.highlight(code, lang);767 if (out != null && out !== code) {768 escaped = true;769 code = out;770 }771 }772 773 if (!lang) {774 return '<pre><code>'775 + (escaped ? code : escape(code, true))776 + '\n</code></pre>';777 }778 779 return '<pre><code class="'780 + this.options.langPrefix781 + escape(lang, true)782 + '">'783 + (escaped ? code : escape(code, true))784 + '\n</code></pre>\n';785 };786 787 Renderer.prototype.blockquote = function(quote) {788 return '<blockquote>\n' + quote + '</blockquote>\n';789 };790 791 Renderer.prototype.html = function(html) {792 return html;793 };794 795 Renderer.prototype.heading = function(text, level, raw) {796 return '<h'797 + level798 + ' id="'799 + this.options.headerPrefix800 + raw.toLowerCase().replace(/[^\w]+/g, '-')801 + '">'802 + text803 + '</h'804 + level805 + '>\n';806 };807 808 Renderer.prototype.hr = function() {809 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';810 };811 812 Renderer.prototype.list = function(body, ordered) {813 var type = ordered ? 'ol' : 'ul';814 return '<' + type + '>\n' + body + '</' + type + '>\n';815 };816 817 Renderer.prototype.listitem = function(text) {818 return '<li>' + text + '</li>\n';819 };820 821 Renderer.prototype.paragraph = function(text) {822 return '<p>' + text + '</p>\n';823 };824 825 Renderer.prototype.table = function(header, body) {826 return '<table>\n'827 + '<thead>\n'828 + header829 + '</thead>\n'830 + '<tbody>\n'831 + body832 + '</tbody>\n'833 + '</table>\n';834 };835 836 Renderer.prototype.tablerow = function(content) {837 return '<tr>\n' + content + '</tr>\n';838 };839 840 Renderer.prototype.tablecell = function(content, flags) {841 var type = flags.header ? 'th' : 'td';842 var tag = flags.align843 ? '<' + type + ' style="text-align:' + flags.align + '">'844 : '<' + type + '>';845 return tag + content + '</' + type + '>\n';846 };847 848 // span level renderer849 Renderer.prototype.strong = function(text) {850 return '<strong>' + text + '</strong>';851 };852 853 Renderer.prototype.em = function(text) {854 return '<em>' + text + '</em>';855 };856 857 Renderer.prototype.codespan = function(text) {858 return '<code>' + text + '</code>';859 };860 861 Renderer.prototype.br = function() {862 return this.options.xhtml ? '<br/>' : '<br>';863 };864 865 Renderer.prototype.del = function(text) {866 return '<del>' + text + '</del>';867 };868 869 Renderer.prototype.link = function(href, title, text) {870 if (this.options.sanitize) {871 try {872 var prot = decodeURIComponent(unescape(href))873 .replace(/[^\w:]/g, '')874 .toLowerCase();875 } catch (e) {876 return '';877 }878 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {879 return '';880 }881 }882 var out = '<a href="' + href + '"';883 if (title) {884 out += ' title="' + title + '"';885 }886 out += '>' + text + '</a>';887 return out;888 };889 890 Renderer.prototype.image = function(href, title, text) {891 var out = '<img src="' + href + '" alt="' + text + '"';892 if (title) {893 out += ' title="' + title + '"';894 }895 out += this.options.xhtml ? '/>' : '>';896 return out;897 };898 899 Renderer.prototype.text = function(text) {900 return text;901 };902 903 /**904 * Parsing & Compiling905 */906 907 function Parser(options) {908 this.tokens = [];909 this.token = null;910 this.options = options || marked.defaults;911 this.options.renderer = this.options.renderer || new Renderer;912 this.renderer = this.options.renderer;913 this.renderer.options = this.options;914 }915 916 /**917 * Static Parse Method918 */919 920 Parser.parse = function(src, options, renderer) {921 var parser = new Parser(options, renderer);922 return parser.parse(src);923 };924 925 /**926 * Parse Loop927 */928 929 Parser.prototype.parse = function(src) {930 this.inline = new InlineLexer(src.links, this.options, this.renderer);931 this.tokens = src.reverse();932 933 var out = '';934 while (this.next()) {935 out += this.tok();936 }937 938 return out;939 };940 941 /**942 * Next Token943 */944 945 Parser.prototype.next = function() {946 return this.token = this.tokens.pop();947 };948 949 /**950 * Preview Next Token951 */952 953 Parser.prototype.peek = function() {954 return this.tokens[this.tokens.length - 1] || 0;955 };956 957 /**958 * Parse Text Tokens959 */960 961 Parser.prototype.parseText = function() {962 var body = this.token.text;963 964 while (this.peek().type === 'text') {965 body += '\n' + this.next().text;966 }967 968 return this.inline.output(body);969 };970 971 /**972 * Parse Current Token973 */974 975 Parser.prototype.tok = function() {976 switch (this.token.type) {977 case 'space': {978 return '';979 }980 case 'hr': {981 return this.renderer.hr();982 }983 case 'heading': {984 return this.renderer.heading(985 this.inline.output(this.token.text),986 this.token.depth,987 this.token.text);988 }989 case 'code': {990 return this.renderer.code(this.token.text,991 this.token.lang,992 this.token.escaped);993 }994 case 'table': {995 var header = ''996 , body = ''997 , i998 , row999 , cell1000 , flags1001 , j;1002 1003 // header1004 cell = '';1005 for (i = 0; i < this.token.header.length; i++) {1006 flags = { header: true, align: this.token.align[i] };1007 cell += this.renderer.tablecell(1008 this.inline.output(this.token.header[i]),1009 { header: true, align: this.token.align[i] }1010 );1011 }1012 header += this.renderer.tablerow(cell);1013 1014 for (i = 0; i < this.token.cells.length; i++) {1015 row = this.token.cells[i];1016 1017 cell = '';1018 for (j = 0; j < row.length; j++) {1019 cell += this.renderer.tablecell(1020 this.inline.output(row[j]),1021 { header: false, align: this.token.align[j] }1022 );1023 }1024 1025 body += this.renderer.tablerow(cell);1026 }1027 return this.renderer.table(header, body);1028 }1029 case 'blockquote_start': {1030 var body = '';1031 1032 while (this.next().type !== 'blockquote_end') {1033 body += this.tok();1034 }1035 1036 return this.renderer.blockquote(body);1037 }1038 case 'list_start': {1039 var body = ''1040 , ordered = this.token.ordered;1041 1042 while (this.next().type !== 'list_end') {1043 body += this.tok();1044 }1045 1046 return this.renderer.list(body, ordered);1047 }1048 case 'list_item_start': {1049 var body = '';1050 1051 while (this.next().type !== 'list_item_end') {1052 body += this.token.type === 'text'1053 ? this.parseText()1054 : this.tok();1055 }1056 1057 return this.renderer.listitem(body);1058 }1059 case 'loose_item_start': {1060 var body = '';1061 1062 while (this.next().type !== 'list_item_end') {1063 body += this.tok();1064 }1065 1066 return this.renderer.listitem(body);1067 }1068 case 'html': {1069 var html = !this.token.pre && !this.options.pedantic1070 ? this.inline.output(this.token.text)1071 : this.token.text;1072 return this.renderer.html(html);1073 }1074 case 'paragraph': {1075 return this.renderer.paragraph(this.inline.output(this.token.text));1076 }1077 case 'text': {1078 return this.renderer.paragraph(this.parseText());1079 }1080 }1081 };1082 1083 /**1084 * Helpers1085 */1086 1087 function escape(html, encode) {1088 return html1089 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')1090 .replace(/</g, '<')1091 .replace(/>/g, '>')1092 .replace(/"/g, '"')1093 .replace(/'/g, ''');1094 }1095 1096 function unescape(html) {1097 return html.replace(/&([#\w]+);/g, function(_, n) {1098 n = n.toLowerCase();1099 if (n === 'colon') return ':';1100 if (n.charAt(0) === '#') {1101 return n.charAt(1) === 'x'1102 ? String.fromCharCode(parseInt(n.substring(2), 16))1103 : String.fromCharCode(+n.substring(1));1104 }1105 return '';1106 });1107 }1108 1109 function replace(regex, opt) {1110 regex = regex.source;1111 opt = opt || '';1112 return function self(name, val) {1113 if (!name) return new RegExp(regex, opt);1114 val = val.source || val;1115 val = val.replace(/(^|[^\[])\^/g, '$1');1116 regex = regex.replace(name, val);1117 return self;1118 };1119 }1120 1121 function noop() {}1122 noop.exec = noop;1123 1124 function merge(obj) {1125 var i = 11126 , target1127 , key;1128 1129 for (; i < arguments.length; i++) {1130 target = arguments[i];1131 for (key in target) {1132 if (Object.prototype.hasOwnProperty.call(target, key)) {1133 obj[key] = target[key];1134 }1135 }1136 }1137 1138 return obj;1139 }1140 1141 1142 /**1143 * Marked1144 */1145 1146 function marked(src, opt, callback) {1147 if (callback || typeof opt === 'function') {1148 if (!callback) {1149 callback = opt;1150 opt = null;1151 }1152 1153 opt = merge({}, marked.defaults, opt || {});1154 1155 var highlight = opt.highlight1156 , tokens1157 , pending1158 , i = 0;1159 1160 try {1161 tokens = Lexer.lex(src, opt)1162 } catch (e) {1163 return callback(e);1164 }1165 1166 pending = tokens.length;1167 1168 var done = function(err) {1169 if (err) {1170 opt.highlight = highlight;1171 return callback(err);1172 }1173 1174 var out;1175 1176 try {1177 out = Parser.parse(tokens, opt);1178 } catch (e) {1179 err = e;1180 }1181 1182 opt.highlight = highlight;1183 1184 return err1185 ? callback(err)1186 : callback(null, out);1187 };1188 1189 if (!highlight || highlight.length < 3) {1190 return done();1191 }1192 1193 delete opt.highlight;1194 1195 if (!pending) return done();1196 1197 for (; i < tokens.length; i++) {1198 (function(token) {1199 if (token.type !== 'code') {1200 return --pending || done();1201 }1202 return highlight(token.text, token.lang, function(err, code) {1203 if (err) return done(err);1204 if (code == null || code === token.text) {1205 return --pending || done();1206 }1207 token.text = code;1208 token.escaped = true;1209 --pending || done();1210 });1211 })(tokens[i]);1212 }1213 1214 return;1215 }1216 try {1217 if (opt) opt = merge({}, marked.defaults, opt);1218 return Parser.parse(Lexer.lex(src, opt), opt);1219 } catch (e) {1220 e.message += '\nPlease report this to https://github.com/chjj/marked.';1221 if ((opt || marked.defaults).silent) {1222 return '<p>An error occured:</p><pre>'1223 + escape(e.message + '', true)1224 + '</pre>';1225 }1226 throw e;1227 }1228 }1229 1230 /**1231 * Options1232 */1233 1234 marked.options =1235 marked.setOptions = function(opt) {1236 merge(marked.defaults, opt);1237 return marked;1238 };1239 1240 marked.defaults = {1241 gfm: true,1242 tables: true,1243 breaks: false,1244 pedantic: false,1245 sanitize: false,1246 sanitizer: null,1247 mangle: true,1248 smartLists: false,1249 silent: false,1250 highlight: null,1251 langPrefix: 'lang-',1252 smartypants: false,1253 headerPrefix: '',1254 renderer: new Renderer,1255 xhtml: false1256 };1257 1258 /**1259 * Expose1260 */1261 1262 marked.Parser = Parser;1263 marked.parser = Parser.parse;1264 1265 marked.Renderer = Renderer;1266 1267 marked.Lexer = Lexer;1268 marked.lexer = Lexer.lex;1269 1270 marked.InlineLexer = InlineLexer;1271 marked.inlineLexer = InlineLexer.output;1272 1273 marked.parse = marked;1274 1275 if (typeof module !== 'undefined' && typeof exports === 'object') {1276 module.exports = marked;1277 } else if (typeof define === 'function' && define.amd) {1278 define(function() { return marked; });1279 } else {1280 this.marked = marked;1281 }1282 1283 }).call(function() {1284 return this || (typeof window !== 'undefined' ? window : global);1285 }());1286 1287
Enlace
El enlace para compartir es: