UNPKG

28.7 kBJavaScriptView Raw
1/**
2 * marked - a markdown parser
3 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
4 * https://github.com/chjj/marked
5 */
6
7;(function() {
8
9/**
10 * Block-Level Grammar
11 */
12
13var 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
30block.bullet = /(?:[*+-]|\d+\.)/;
31block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
32block.item = replace(block.item, 'gm')
33 (/bull/g, block.bullet)
34 ();
35
36block.list = replace(block.list)
37 (/bull/g, block.bullet)
38 ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
39 ('def', '\\n+(?=' + block.def.source + ')')
40 ();
41
42block.blockquote = replace(block.blockquote)
43 ('def', block.def)
44 ();
45
46block._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
51block.html = replace(block.html)
52 ('comment', /<!--[\s\S]*?-->/)
53 ('closed', /<(tag)[\s\S]+?<\/\1>/)
54 ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
55 (/tag/g, block._tag)
56 ();
57
58block.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 Grammar
69 */
70
71block.normal = merge({}, block);
72
73/**
74 * GFM Block Grammar
75 */
76
77block.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
83block.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 Grammar
91 */
92
93block.tables = merge({}, block.gfm, {
94 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
95 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
96});
97
98/**
99 * Block Lexer
100 */
101
102function 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 Rules
119 */
120
121Lexer.rules = block;
122
123/**
124 * Static Lex Method
125 */
126
127Lexer.lex = function(src, options) {
128 var lexer = new Lexer(options);
129 return lexer.lex(src);
130};
131
132/**
133 * Preprocessing
134 */
135
136Lexer.prototype.lex = function(src) {
137 src = src
138 .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 * Lexing
148 */
149
150Lexer.prototype.token = function(src, top, bq) {
151 var src = src.replace(/^ +$/gm, '')
152 , next
153 , loose
154 , cap
155 , bull
156 , b
157 , item
158 , space
159 , i
160 , l;
161
162 while (src) {
163 // newline
164 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 // code
174 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.pedantic
180 ? cap.replace(/\n+$/, '')
181 : cap
182 });
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 // heading
198 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 // lheading
241 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 // hr
252 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 // blockquote
261 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 current
271 // "toplevel" state. This is exactly
272 // 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 // list
283 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 > 1
290 });
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 bullet
303 // so it is seen as the next token.
304 space = item.length;
305 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
306
307 // Outdent whatever the
308 // list item contains. Hacky.
309 if (~item.indexOf('\n ')) {
310 space -= item.length;
311 item = !this.options.pedantic
312 ? 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: loose
337 ? '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 // html
357 if (cap = this.rules.html.exec(src)) {
358 src = src.substring(cap[0].length);
359 this.tokens.push({
360 type: this.options.sanitize
361 ? 'paragraph'
362 : 'html',
363 pre: !this.options.sanitizer
364 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
365 text: cap[0]
366 });
367 continue;
368 }
369
370 // def
371 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 paragraph
415 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 // text
427 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 new
439 Error('Infinite loop on byte: ' + src.charCodeAt(0));
440 }
441 }
442
443 return this.tokens;
444};
445
446/**
447 * Inline-Level Grammar
448 */
449
450var 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
466inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
467inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
468
469inline.link = replace(inline.link)
470 ('inside', inline._inside)
471 ('href', inline._href)
472 ();
473
474inline.reflink = replace(inline.reflink)
475 ('inside', inline._inside)
476 ();
477
478/**
479 * Normal Inline Grammar
480 */
481
482inline.normal = merge({}, inline);
483
484/**
485 * Pedantic Inline Grammar
486 */
487
488inline.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 Grammar
495 */
496
497inline.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 Grammar
509 */
510
511inline.breaks = merge({}, inline.gfm, {
512 br: replace(inline.br)('{2,}', '*')(),
513 text: replace(inline.gfm.text)('{2,}', '*')()
514});
515
516/**
517 * Inline Lexer & Compiler
518 */
519
520function 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 new
529 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 Rules
545 */
546
547InlineLexer.rules = inline;
548
549/**
550 * Static Lexing/Compiling Method
551 */
552
553InlineLexer.output = function(src, links, options) {
554 var inline = new InlineLexer(links, options);
555 return inline.output(src);
556};
557
558/**
559 * Lexing/Compiling
560 */
561
562InlineLexer.prototype.output = function(src) {
563 var out = ''
564 , link
565 , text
566 , href
567 , cap;
568
569 while (src) {
570 // escape
571 if (cap = this.rules.escape.exec(src)) {
572 src = src.substring(cap[0].length);
573 out += cap[1];
574 continue;
575 }
576
577 // autolink
578 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 // tag
603 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.sanitize
611 ? this.options.sanitizer
612 ? this.options.sanitizer(cap[0])
613 : escape(cap[0])
614 : cap[0]
615 continue;
616 }
617
618 // link
619 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, nolink
631 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 // strong
648 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 // em
655 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 // code
662 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 // br
669 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 // text
683 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 new
691 Error('Infinite loop on byte: ' + src.charCodeAt(0));
692 }
693 }
694
695 return out;
696};
697
698/**
699 * Compile Link
700 */
701
702InlineLexer.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 Transformations
713 */
714
715InlineLexer.prototype.smartypants = function(text) {
716 if (!this.options.smartypants) return text;
717 return text
718 // em-dashes
719 .replace(/---/g, '\u2014')
720 // en-dashes
721 .replace(/--/g, '\u2013')
722 // opening singles
723 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
724 // closing singles & apostrophes
725 .replace(/'/g, '\u2019')
726 // opening doubles
727 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
728 // closing doubles
729 .replace(/"/g, '\u201d')
730 // ellipses
731 .replace(/\.{3}/g, '\u2026');
732};
733
734/**
735 * Mangle Links
736 */
737
738InlineLexer.prototype.mangle = function(text) {
739 if (!this.options.mangle) return text;
740 var out = ''
741 , l = text.length
742 , i = 0
743 , 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 * Renderer
758 */
759
760function Renderer(options) {
761 this.options = options || {};
762}
763
764Renderer.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.langPrefix
781 + escape(lang, true)
782 + '">'
783 + (escaped ? code : escape(code, true))
784 + '\n</code></pre>\n';
785};
786
787Renderer.prototype.blockquote = function(quote) {
788 return '<blockquote>\n' + quote + '</blockquote>\n';
789};
790
791Renderer.prototype.html = function(html) {
792 return html;
793};
794
795Renderer.prototype.heading = function(text, level, raw) {
796 return '<h'
797 + level
798 + ' id="'
799 + this.options.headerPrefix
800 + raw.toLowerCase().replace(/[^\w]+/g, '-')
801 + '">'
802 + text
803 + '</h'
804 + level
805 + '>\n';
806};
807
808Renderer.prototype.hr = function() {
809 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
810};
811
812Renderer.prototype.list = function(body, ordered) {
813 var type = ordered ? 'ol' : 'ul';
814 return '<' + type + '>\n' + body + '</' + type + '>\n';
815};
816
817Renderer.prototype.listitem = function(text) {
818 return '<li>' + text + '</li>\n';
819};
820
821Renderer.prototype.paragraph = function(text) {
822 return '<p>' + text + '</p>\n';
823};
824
825Renderer.prototype.table = function(header, body) {
826 return '<table>\n'
827 + '<thead>\n'
828 + header
829 + '</thead>\n'
830 + '<tbody>\n'
831 + body
832 + '</tbody>\n'
833 + '</table>\n';
834};
835
836Renderer.prototype.tablerow = function(content) {
837 return '<tr>\n' + content + '</tr>\n';
838};
839
840Renderer.prototype.tablecell = function(content, flags) {
841 var type = flags.header ? 'th' : 'td';
842 var tag = flags.align
843 ? '<' + type + ' style="text-align:' + flags.align + '">'
844 : '<' + type + '>';
845 return tag + content + '</' + type + '>\n';
846};
847
848// span level renderer
849Renderer.prototype.strong = function(text) {
850 return '<strong>' + text + '</strong>';
851};
852
853Renderer.prototype.em = function(text) {
854 return '<em>' + text + '</em>';
855};
856
857Renderer.prototype.codespan = function(text) {
858 return '<code>' + text + '</code>';
859};
860
861Renderer.prototype.br = function() {
862 return this.options.xhtml ? '<br/>' : '<br>';
863};
864
865Renderer.prototype.del = function(text) {
866 return '<del>' + text + '</del>';
867};
868
869Renderer.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 || prot.indexOf('data:') === 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
890Renderer.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
899Renderer.prototype.text = function(text) {
900 return text;
901};
902
903/**
904 * Parsing & Compiling
905 */
906
907function 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 Method
918 */
919
920Parser.parse = function(src, options, renderer) {
921 var parser = new Parser(options, renderer);
922 return parser.parse(src);
923};
924
925/**
926 * Parse Loop
927 */
928
929Parser.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 Token
943 */
944
945Parser.prototype.next = function() {
946 return this.token = this.tokens.pop();
947};
948
949/**
950 * Preview Next Token
951 */
952
953Parser.prototype.peek = function() {
954 return this.tokens[this.tokens.length - 1] || 0;
955};
956
957/**
958 * Parse Text Tokens
959 */
960
961Parser.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 Token
973 */
974
975Parser.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 , i
998 , row
999 , cell
1000 , flags
1001 , j;
1002
1003 // header
1004 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.pedantic
1070 ? 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 * Helpers
1085 */
1086
1087function escape(html, encode) {
1088 return html
1089 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
1090 .replace(/</g, '&lt;')
1091 .replace(/>/g, '&gt;')
1092 .replace(/"/g, '&quot;')
1093 .replace(/'/g, '&#39;');
1094}
1095
1096function unescape(html) {
1097 // explicitly match decimal, hex, and named HTML entities
1098 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
1099 n = n.toLowerCase();
1100 if (n === 'colon') return ':';
1101 if (n.charAt(0) === '#') {
1102 return n.charAt(1) === 'x'
1103 ? String.fromCharCode(parseInt(n.substring(2), 16))
1104 : String.fromCharCode(+n.substring(1));
1105 }
1106 return '';
1107 });
1108}
1109
1110function replace(regex, opt) {
1111 regex = regex.source;
1112 opt = opt || '';
1113 return function self(name, val) {
1114 if (!name) return new RegExp(regex, opt);
1115 val = val.source || val;
1116 val = val.replace(/(^|[^\[])\^/g, '$1');
1117 regex = regex.replace(name, val);
1118 return self;
1119 };
1120}
1121
1122function noop() {}
1123noop.exec = noop;
1124
1125function merge(obj) {
1126 var i = 1
1127 , target
1128 , key;
1129
1130 for (; i < arguments.length; i++) {
1131 target = arguments[i];
1132 for (key in target) {
1133 if (Object.prototype.hasOwnProperty.call(target, key)) {
1134 obj[key] = target[key];
1135 }
1136 }
1137 }
1138
1139 return obj;
1140}
1141
1142
1143/**
1144 * Marked
1145 */
1146
1147function marked(src, opt, callback) {
1148 if (callback || typeof opt === 'function') {
1149 if (!callback) {
1150 callback = opt;
1151 opt = null;
1152 }
1153
1154 opt = merge({}, marked.defaults, opt || {});
1155
1156 var highlight = opt.highlight
1157 , tokens
1158 , pending
1159 , i = 0;
1160
1161 try {
1162 tokens = Lexer.lex(src, opt)
1163 } catch (e) {
1164 return callback(e);
1165 }
1166
1167 pending = tokens.length;
1168
1169 var done = function(err) {
1170 if (err) {
1171 opt.highlight = highlight;
1172 return callback(err);
1173 }
1174
1175 var out;
1176
1177 try {
1178 out = Parser.parse(tokens, opt);
1179 } catch (e) {
1180 err = e;
1181 }
1182
1183 opt.highlight = highlight;
1184
1185 return err
1186 ? callback(err)
1187 : callback(null, out);
1188 };
1189
1190 if (!highlight || highlight.length < 3) {
1191 return done();
1192 }
1193
1194 delete opt.highlight;
1195
1196 if (!pending) return done();
1197
1198 for (; i < tokens.length; i++) {
1199 (function(token) {
1200 if (token.type !== 'code') {
1201 return --pending || done();
1202 }
1203 return highlight(token.text, token.lang, function(err, code) {
1204 if (err) return done(err);
1205 if (code == null || code === token.text) {
1206 return --pending || done();
1207 }
1208 token.text = code;
1209 token.escaped = true;
1210 --pending || done();
1211 });
1212 })(tokens[i]);
1213 }
1214
1215 return;
1216 }
1217 try {
1218 if (opt) opt = merge({}, marked.defaults, opt);
1219 return Parser.parse(Lexer.lex(src, opt), opt);
1220 } catch (e) {
1221 e.message += '\nPlease report this to https://github.com/chjj/marked.';
1222 if ((opt || marked.defaults).silent) {
1223 return '<p>An error occured:</p><pre>'
1224 + escape(e.message + '', true)
1225 + '</pre>';
1226 }
1227 throw e;
1228 }
1229}
1230
1231/**
1232 * Options
1233 */
1234
1235marked.options =
1236marked.setOptions = function(opt) {
1237 merge(marked.defaults, opt);
1238 return marked;
1239};
1240
1241marked.defaults = {
1242 gfm: true,
1243 tables: true,
1244 breaks: false,
1245 pedantic: false,
1246 sanitize: false,
1247 sanitizer: null,
1248 mangle: true,
1249 smartLists: false,
1250 silent: false,
1251 highlight: null,
1252 langPrefix: 'lang-',
1253 smartypants: false,
1254 headerPrefix: '',
1255 renderer: new Renderer,
1256 xhtml: false
1257};
1258
1259/**
1260 * Expose
1261 */
1262
1263marked.Parser = Parser;
1264marked.parser = Parser.parse;
1265
1266marked.Renderer = Renderer;
1267
1268marked.Lexer = Lexer;
1269marked.lexer = Lexer.lex;
1270
1271marked.InlineLexer = InlineLexer;
1272marked.inlineLexer = InlineLexer.output;
1273
1274marked.parse = marked;
1275
1276if (typeof module !== 'undefined' && typeof exports === 'object') {
1277 module.exports = marked;
1278} else if (typeof define === 'function' && define.amd) {
1279 define(function() { return marked; });
1280} else {
1281 this.marked = marked;
1282}
1283
1284}).call(function() {
1285 return this || (typeof window !== 'undefined' ? window : global);
1286}());
\No newline at end of file