blob: c2a678d5504aa3a1c204342fb832637ab2eeec51 [file] [log] [blame]
Thomas Vachuska0fa2aa12015-08-18 12:53:04 -07001/**
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});
81
82block.gfm.paragraph = replace(block.paragraph)
83 ('(?!', '(?!'
84 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
85 + block.list.source.replace('\\1', '\\3') + '|')
86 ();
87
88/**
89 * GFM + Tables Block Grammar
90 */
91
92block.tables = merge({}, block.gfm, {
93 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
94 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
95});
96
97/**
98 * Block Lexer
99 */
100
101function Lexer(options) {
102 this.tokens = [];
103 this.tokens.links = {};
104 this.options = options || marked.defaults;
105 this.rules = block.normal;
106
107 if (this.options.gfm) {
108 if (this.options.tables) {
109 this.rules = block.tables;
110 } else {
111 this.rules = block.gfm;
112 }
113 }
114}
115
116/**
117 * Expose Block Rules
118 */
119
120Lexer.rules = block;
121
122/**
123 * Static Lex Method
124 */
125
126Lexer.lex = function(src, options) {
127 var lexer = new Lexer(options);
128 return lexer.lex(src);
129};
130
131/**
132 * Preprocessing
133 */
134
135Lexer.prototype.lex = function(src) {
136 src = src
137 .replace(/\r\n|\r/g, '\n')
138 .replace(/\t/g, ' ')
139 .replace(/\u00a0/g, ' ')
140 .replace(/\u2424/g, '\n');
141
142 return this.token(src, true);
143};
144
145/**
146 * Lexing
147 */
148
149Lexer.prototype.token = function(src, top, bq) {
150 var src = src.replace(/^ +$/gm, '')
151 , next
152 , loose
153 , cap
154 , bull
155 , b
156 , item
157 , space
158 , i
159 , l;
160
161 while (src) {
162 // newline
163 if (cap = this.rules.newline.exec(src)) {
164 src = src.substring(cap[0].length);
165 if (cap[0].length > 1) {
166 this.tokens.push({
167 type: 'space'
168 });
169 }
170 }
171
172 // code
173 if (cap = this.rules.code.exec(src)) {
174 src = src.substring(cap[0].length);
175 cap = cap[0].replace(/^ {4}/gm, '');
176 this.tokens.push({
177 type: 'code',
178 text: !this.options.pedantic
179 ? cap.replace(/\n+$/, '')
180 : cap
181 });
182 continue;
183 }
184
185 // fences (gfm)
186 if (cap = this.rules.fences.exec(src)) {
187 src = src.substring(cap[0].length);
188 this.tokens.push({
189 type: 'code',
190 lang: cap[2],
191 text: cap[3]
192 });
193 continue;
194 }
195
196 // heading
197 if (cap = this.rules.heading.exec(src)) {
198 src = src.substring(cap[0].length);
199 this.tokens.push({
200 type: 'heading',
201 depth: cap[1].length,
202 text: cap[2]
203 });
204 continue;
205 }
206
207 // table no leading pipe (gfm)
208 if (top && (cap = this.rules.nptable.exec(src))) {
209 src = src.substring(cap[0].length);
210
211 item = {
212 type: 'table',
213 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
214 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
215 cells: cap[3].replace(/\n$/, '').split('\n')
216 };
217
218 for (i = 0; i < item.align.length; i++) {
219 if (/^ *-+: *$/.test(item.align[i])) {
220 item.align[i] = 'right';
221 } else if (/^ *:-+: *$/.test(item.align[i])) {
222 item.align[i] = 'center';
223 } else if (/^ *:-+ *$/.test(item.align[i])) {
224 item.align[i] = 'left';
225 } else {
226 item.align[i] = null;
227 }
228 }
229
230 for (i = 0; i < item.cells.length; i++) {
231 item.cells[i] = item.cells[i].split(/ *\| */);
232 }
233
234 this.tokens.push(item);
235
236 continue;
237 }
238
239 // lheading
240 if (cap = this.rules.lheading.exec(src)) {
241 src = src.substring(cap[0].length);
242 this.tokens.push({
243 type: 'heading',
244 depth: cap[2] === '=' ? 1 : 2,
245 text: cap[1]
246 });
247 continue;
248 }
249
250 // hr
251 if (cap = this.rules.hr.exec(src)) {
252 src = src.substring(cap[0].length);
253 this.tokens.push({
254 type: 'hr'
255 });
256 continue;
257 }
258
259 // blockquote
260 if (cap = this.rules.blockquote.exec(src)) {
261 src = src.substring(cap[0].length);
262
263 this.tokens.push({
264 type: 'blockquote_start'
265 });
266
267 cap = cap[0].replace(/^ *> ?/gm, '');
268
269 // Pass `top` to keep the current
270 // "toplevel" state. This is exactly
271 // how markdown.pl works.
272 this.token(cap, top, true);
273
274 this.tokens.push({
275 type: 'blockquote_end'
276 });
277
278 continue;
279 }
280
281 // list
282 if (cap = this.rules.list.exec(src)) {
283 src = src.substring(cap[0].length);
284 bull = cap[2];
285
286 this.tokens.push({
287 type: 'list_start',
288 ordered: bull.length > 1
289 });
290
291 // Get each top-level item.
292 cap = cap[0].match(this.rules.item);
293
294 next = false;
295 l = cap.length;
296 i = 0;
297
298 for (; i < l; i++) {
299 item = cap[i];
300
301 // Remove the list item's bullet
302 // so it is seen as the next token.
303 space = item.length;
304 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
305
306 // Outdent whatever the
307 // list item contains. Hacky.
308 if (~item.indexOf('\n ')) {
309 space -= item.length;
310 item = !this.options.pedantic
311 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
312 : item.replace(/^ {1,4}/gm, '');
313 }
314
315 // Determine whether the next list item belongs here.
316 // Backpedal if it does not belong in this list.
317 if (this.options.smartLists && i !== l - 1) {
318 b = block.bullet.exec(cap[i + 1])[0];
319 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
320 src = cap.slice(i + 1).join('\n') + src;
321 i = l - 1;
322 }
323 }
324
325 // Determine whether item is loose or not.
326 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
327 // for discount behavior.
328 loose = next || /\n\n(?!\s*$)/.test(item);
329 if (i !== l - 1) {
330 next = item.charAt(item.length - 1) === '\n';
331 if (!loose) loose = next;
332 }
333
334 this.tokens.push({
335 type: loose
336 ? 'loose_item_start'
337 : 'list_item_start'
338 });
339
340 // Recurse.
341 this.token(item, false, bq);
342
343 this.tokens.push({
344 type: 'list_item_end'
345 });
346 }
347
348 this.tokens.push({
349 type: 'list_end'
350 });
351
352 continue;
353 }
354
355 // html
356 if (cap = this.rules.html.exec(src)) {
357 src = src.substring(cap[0].length);
358 this.tokens.push({
359 type: this.options.sanitize
360 ? 'paragraph'
361 : 'html',
362 pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
363 text: cap[0]
364 });
365 continue;
366 }
367
368 // def
369 if ((!bq && top) && (cap = this.rules.def.exec(src))) {
370 src = src.substring(cap[0].length);
371 this.tokens.links[cap[1].toLowerCase()] = {
372 href: cap[2],
373 title: cap[3]
374 };
375 continue;
376 }
377
378 // table (gfm)
379 if (top && (cap = this.rules.table.exec(src))) {
380 src = src.substring(cap[0].length);
381
382 item = {
383 type: 'table',
384 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
385 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
386 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
387 };
388
389 for (i = 0; i < item.align.length; i++) {
390 if (/^ *-+: *$/.test(item.align[i])) {
391 item.align[i] = 'right';
392 } else if (/^ *:-+: *$/.test(item.align[i])) {
393 item.align[i] = 'center';
394 } else if (/^ *:-+ *$/.test(item.align[i])) {
395 item.align[i] = 'left';
396 } else {
397 item.align[i] = null;
398 }
399 }
400
401 for (i = 0; i < item.cells.length; i++) {
402 item.cells[i] = item.cells[i]
403 .replace(/^ *\| *| *\| *$/g, '')
404 .split(/ *\| */);
405 }
406
407 this.tokens.push(item);
408
409 continue;
410 }
411
412 // top-level paragraph
413 if (top && (cap = this.rules.paragraph.exec(src))) {
414 src = src.substring(cap[0].length);
415 this.tokens.push({
416 type: 'paragraph',
417 text: cap[1].charAt(cap[1].length - 1) === '\n'
418 ? cap[1].slice(0, -1)
419 : cap[1]
420 });
421 continue;
422 }
423
424 // text
425 if (cap = this.rules.text.exec(src)) {
426 // Top-level should never reach here.
427 src = src.substring(cap[0].length);
428 this.tokens.push({
429 type: 'text',
430 text: cap[0]
431 });
432 continue;
433 }
434
435 if (src) {
436 throw new
437 Error('Infinite loop on byte: ' + src.charCodeAt(0));
438 }
439 }
440
441 return this.tokens;
442};
443
444/**
445 * Inline-Level Grammar
446 */
447
448var inline = {
449 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
450 autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
451 url: noop,
452 tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
453 link: /^!?\[(inside)\]\(href\)/,
454 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
455 nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
456 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
457 em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
458 code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
459 br: /^ {2,}\n(?!\s*$)/,
460 del: noop,
461 text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
462};
463
464inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
465inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
466
467inline.link = replace(inline.link)
468 ('inside', inline._inside)
469 ('href', inline._href)
470 ();
471
472inline.reflink = replace(inline.reflink)
473 ('inside', inline._inside)
474 ();
475
476/**
477 * Normal Inline Grammar
478 */
479
480inline.normal = merge({}, inline);
481
482/**
483 * Pedantic Inline Grammar
484 */
485
486inline.pedantic = merge({}, inline.normal, {
487 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
488 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
489});
490
491/**
492 * GFM Inline Grammar
493 */
494
495inline.gfm = merge({}, inline.normal, {
496 escape: replace(inline.escape)('])', '~|])')(),
497 url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
498 del: /^~~(?=\S)([\s\S]*?\S)~~/,
499 text: replace(inline.text)
500 (']|', '~]|')
501 ('|', '|https?://|')
502 ()
503});
504
505/**
506 * GFM + Line Breaks Inline Grammar
507 */
508
509inline.breaks = merge({}, inline.gfm, {
510 br: replace(inline.br)('{2,}', '*')(),
511 text: replace(inline.gfm.text)('{2,}', '*')()
512});
513
514/**
515 * Inline Lexer & Compiler
516 */
517
518function InlineLexer(links, options) {
519 this.options = options || marked.defaults;
520 this.links = links;
521 this.rules = inline.normal;
522 this.renderer = this.options.renderer || new Renderer;
523 this.renderer.options = this.options;
524
525 if (!this.links) {
526 throw new
527 Error('Tokens array requires a `links` property.');
528 }
529
530 if (this.options.gfm) {
531 if (this.options.breaks) {
532 this.rules = inline.breaks;
533 } else {
534 this.rules = inline.gfm;
535 }
536 } else if (this.options.pedantic) {
537 this.rules = inline.pedantic;
538 }
539}
540
541/**
542 * Expose Inline Rules
543 */
544
545InlineLexer.rules = inline;
546
547/**
548 * Static Lexing/Compiling Method
549 */
550
551InlineLexer.output = function(src, links, options) {
552 var inline = new InlineLexer(links, options);
553 return inline.output(src);
554};
555
556/**
557 * Lexing/Compiling
558 */
559
560InlineLexer.prototype.output = function(src) {
561 var out = ''
562 , link
563 , text
564 , href
565 , cap;
566
567 while (src) {
568 // escape
569 if (cap = this.rules.escape.exec(src)) {
570 src = src.substring(cap[0].length);
571 out += cap[1];
572 continue;
573 }
574
575 // autolink
576 if (cap = this.rules.autolink.exec(src)) {
577 src = src.substring(cap[0].length);
578 if (cap[2] === '@') {
579 text = cap[1].charAt(6) === ':'
580 ? this.mangle(cap[1].substring(7))
581 : this.mangle(cap[1]);
582 href = this.mangle('mailto:') + text;
583 } else {
584 text = escape(cap[1]);
585 href = text;
586 }
587 out += this.renderer.link(href, null, text);
588 continue;
589 }
590
591 // url (gfm)
592 if (!this.inLink && (cap = this.rules.url.exec(src))) {
593 src = src.substring(cap[0].length);
594 text = escape(cap[1]);
595 href = text;
596 out += this.renderer.link(href, null, text);
597 continue;
598 }
599
600 // tag
601 if (cap = this.rules.tag.exec(src)) {
602 if (!this.inLink && /^<a /i.test(cap[0])) {
603 this.inLink = true;
604 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
605 this.inLink = false;
606 }
607 src = src.substring(cap[0].length);
608 out += this.options.sanitize
609 ? escape(cap[0])
610 : cap[0];
611 continue;
612 }
613
614 // link
615 if (cap = this.rules.link.exec(src)) {
616 src = src.substring(cap[0].length);
617 this.inLink = true;
618 out += this.outputLink(cap, {
619 href: cap[2],
620 title: cap[3]
621 });
622 this.inLink = false;
623 continue;
624 }
625
626 // reflink, nolink
627 if ((cap = this.rules.reflink.exec(src))
628 || (cap = this.rules.nolink.exec(src))) {
629 src = src.substring(cap[0].length);
630 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
631 link = this.links[link.toLowerCase()];
632 if (!link || !link.href) {
633 out += cap[0].charAt(0);
634 src = cap[0].substring(1) + src;
635 continue;
636 }
637 this.inLink = true;
638 out += this.outputLink(cap, link);
639 this.inLink = false;
640 continue;
641 }
642
643 // strong
644 if (cap = this.rules.strong.exec(src)) {
645 src = src.substring(cap[0].length);
646 out += this.renderer.strong(this.output(cap[2] || cap[1]));
647 continue;
648 }
649
650 // em
651 if (cap = this.rules.em.exec(src)) {
652 src = src.substring(cap[0].length);
653 out += this.renderer.em(this.output(cap[2] || cap[1]));
654 continue;
655 }
656
657 // code
658 if (cap = this.rules.code.exec(src)) {
659 src = src.substring(cap[0].length);
660 out += this.renderer.codespan(escape(cap[2], true));
661 continue;
662 }
663
664 // br
665 if (cap = this.rules.br.exec(src)) {
666 src = src.substring(cap[0].length);
667 out += this.renderer.br();
668 continue;
669 }
670
671 // del (gfm)
672 if (cap = this.rules.del.exec(src)) {
673 src = src.substring(cap[0].length);
674 out += this.renderer.del(this.output(cap[1]));
675 continue;
676 }
677
678 // text
679 if (cap = this.rules.text.exec(src)) {
680 src = src.substring(cap[0].length);
681 out += escape(this.smartypants(cap[0]));
682 continue;
683 }
684
685 if (src) {
686 throw new
687 Error('Infinite loop on byte: ' + src.charCodeAt(0));
688 }
689 }
690
691 return out;
692};
693
694/**
695 * Compile Link
696 */
697
698InlineLexer.prototype.outputLink = function(cap, link) {
699 var href = escape(link.href)
700 , title = link.title ? escape(link.title) : null;
701
702 return cap[0].charAt(0) !== '!'
703 ? this.renderer.link(href, title, this.output(cap[1]))
704 : this.renderer.image(href, title, escape(cap[1]));
705};
706
707/**
708 * Smartypants Transformations
709 */
710
711InlineLexer.prototype.smartypants = function(text) {
712 if (!this.options.smartypants) return text;
713 return text
714 // em-dashes
715 .replace(/--/g, '\u2014')
716 // opening singles
717 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
718 // closing singles & apostrophes
719 .replace(/'/g, '\u2019')
720 // opening doubles
721 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
722 // closing doubles
723 .replace(/"/g, '\u201d')
724 // ellipses
725 .replace(/\.{3}/g, '\u2026');
726};
727
728/**
729 * Mangle Links
730 */
731
732InlineLexer.prototype.mangle = function(text) {
733 var out = ''
734 , l = text.length
735 , i = 0
736 , ch;
737
738 for (; i < l; i++) {
739 ch = text.charCodeAt(i);
740 if (Math.random() > 0.5) {
741 ch = 'x' + ch.toString(16);
742 }
743 out += '&#' + ch + ';';
744 }
745
746 return out;
747};
748
749/**
750 * Renderer
751 */
752
753function Renderer(options) {
754 this.options = options || {};
755}
756
757Renderer.prototype.code = function(code, lang, escaped) {
758 if (this.options.highlight) {
759 var out = this.options.highlight(code, lang);
760 if (out != null && out !== code) {
761 escaped = true;
762 code = out;
763 }
764 }
765
766 if (!lang) {
767 return '<pre><code>'
768 + (escaped ? code : escape(code, true))
769 + '\n</code></pre>';
770 }
771
772 return '<pre><code class="'
773 + this.options.langPrefix
774 + escape(lang, true)
775 + '">'
776 + (escaped ? code : escape(code, true))
777 + '\n</code></pre>\n';
778};
779
780Renderer.prototype.blockquote = function(quote) {
781 return '<blockquote>\n' + quote + '</blockquote>\n';
782};
783
784Renderer.prototype.html = function(html) {
785 return html;
786};
787
788Renderer.prototype.heading = function(text, level, raw) {
789 return '<h'
790 + level
791 + ' id="'
792 + this.options.headerPrefix
793 + raw.toLowerCase().replace(/[^\w]+/g, '-')
794 + '">'
795 + text
796 + '</h'
797 + level
798 + '>\n';
799};
800
801Renderer.prototype.hr = function() {
802 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
803};
804
805Renderer.prototype.list = function(body, ordered) {
806 var type = ordered ? 'ol' : 'ul';
807 return '<' + type + '>\n' + body + '</' + type + '>\n';
808};
809
810Renderer.prototype.listitem = function(text) {
811 return '<li>' + text + '</li>\n';
812};
813
814Renderer.prototype.paragraph = function(text) {
815 return '<p>' + text + '</p>\n';
816};
817
818Renderer.prototype.table = function(header, body) {
819 return '<table>\n'
820 + '<thead>\n'
821 + header
822 + '</thead>\n'
823 + '<tbody>\n'
824 + body
825 + '</tbody>\n'
826 + '</table>\n';
827};
828
829Renderer.prototype.tablerow = function(content) {
830 return '<tr>\n' + content + '</tr>\n';
831};
832
833Renderer.prototype.tablecell = function(content, flags) {
834 var type = flags.header ? 'th' : 'td';
835 var tag = flags.align
836 ? '<' + type + ' style="text-align:' + flags.align + '">'
837 : '<' + type + '>';
838 return tag + content + '</' + type + '>\n';
839};
840
841// span level renderer
842Renderer.prototype.strong = function(text) {
843 return '<strong>' + text + '</strong>';
844};
845
846Renderer.prototype.em = function(text) {
847 return '<em>' + text + '</em>';
848};
849
850Renderer.prototype.codespan = function(text) {
851 return '<code>' + text + '</code>';
852};
853
854Renderer.prototype.br = function() {
855 return this.options.xhtml ? '<br/>' : '<br>';
856};
857
858Renderer.prototype.del = function(text) {
859 return '<del>' + text + '</del>';
860};
861
862Renderer.prototype.link = function(href, title, text) {
863 if (this.options.sanitize) {
864 try {
865 var prot = decodeURIComponent(unescape(href))
866 .replace(/[^\w:]/g, '')
867 .toLowerCase();
868 } catch (e) {
869 return '';
870 }
871 if (prot.indexOf('javascript:') === 0) {
872 return '';
873 }
874 }
875 var out = '<a href="' + href + '"';
876 if (title) {
877 out += ' title="' + title + '"';
878 }
879 out += '>' + text + '</a>';
880 return out;
881};
882
883Renderer.prototype.image = function(href, title, text) {
884 var out = '<img src="' + href + '" alt="' + text + '"';
885 if (title) {
886 out += ' title="' + title + '"';
887 }
888 out += this.options.xhtml ? '/>' : '>';
889 return out;
890};
891
892/**
893 * Parsing & Compiling
894 */
895
896function Parser(options) {
897 this.tokens = [];
898 this.token = null;
899 this.options = options || marked.defaults;
900 this.options.renderer = this.options.renderer || new Renderer;
901 this.renderer = this.options.renderer;
902 this.renderer.options = this.options;
903}
904
905/**
906 * Static Parse Method
907 */
908
909Parser.parse = function(src, options, renderer) {
910 var parser = new Parser(options, renderer);
911 return parser.parse(src);
912};
913
914/**
915 * Parse Loop
916 */
917
918Parser.prototype.parse = function(src) {
919 this.inline = new InlineLexer(src.links, this.options, this.renderer);
920 this.tokens = src.reverse();
921
922 var out = '';
923 while (this.next()) {
924 out += this.tok();
925 }
926
927 return out;
928};
929
930/**
931 * Next Token
932 */
933
934Parser.prototype.next = function() {
935 return this.token = this.tokens.pop();
936};
937
938/**
939 * Preview Next Token
940 */
941
942Parser.prototype.peek = function() {
943 return this.tokens[this.tokens.length - 1] || 0;
944};
945
946/**
947 * Parse Text Tokens
948 */
949
950Parser.prototype.parseText = function() {
951 var body = this.token.text;
952
953 while (this.peek().type === 'text') {
954 body += '\n' + this.next().text;
955 }
956
957 return this.inline.output(body);
958};
959
960/**
961 * Parse Current Token
962 */
963
964Parser.prototype.tok = function() {
965 switch (this.token.type) {
966 case 'space': {
967 return '';
968 }
969 case 'hr': {
970 return this.renderer.hr();
971 }
972 case 'heading': {
973 return this.renderer.heading(
974 this.inline.output(this.token.text),
975 this.token.depth,
976 this.token.text);
977 }
978 case 'code': {
979 return this.renderer.code(this.token.text,
980 this.token.lang,
981 this.token.escaped);
982 }
983 case 'table': {
984 var header = ''
985 , body = ''
986 , i
987 , row
988 , cell
989 , flags
990 , j;
991
992 // header
993 cell = '';
994 for (i = 0; i < this.token.header.length; i++) {
995 flags = { header: true, align: this.token.align[i] };
996 cell += this.renderer.tablecell(
997 this.inline.output(this.token.header[i]),
998 { header: true, align: this.token.align[i] }
999 );
1000 }
1001 header += this.renderer.tablerow(cell);
1002
1003 for (i = 0; i < this.token.cells.length; i++) {
1004 row = this.token.cells[i];
1005
1006 cell = '';
1007 for (j = 0; j < row.length; j++) {
1008 cell += this.renderer.tablecell(
1009 this.inline.output(row[j]),
1010 { header: false, align: this.token.align[j] }
1011 );
1012 }
1013
1014 body += this.renderer.tablerow(cell);
1015 }
1016 return this.renderer.table(header, body);
1017 }
1018 case 'blockquote_start': {
1019 var body = '';
1020
1021 while (this.next().type !== 'blockquote_end') {
1022 body += this.tok();
1023 }
1024
1025 return this.renderer.blockquote(body);
1026 }
1027 case 'list_start': {
1028 var body = ''
1029 , ordered = this.token.ordered;
1030
1031 while (this.next().type !== 'list_end') {
1032 body += this.tok();
1033 }
1034
1035 return this.renderer.list(body, ordered);
1036 }
1037 case 'list_item_start': {
1038 var body = '';
1039
1040 while (this.next().type !== 'list_item_end') {
1041 body += this.token.type === 'text'
1042 ? this.parseText()
1043 : this.tok();
1044 }
1045
1046 return this.renderer.listitem(body);
1047 }
1048 case 'loose_item_start': {
1049 var body = '';
1050
1051 while (this.next().type !== 'list_item_end') {
1052 body += this.tok();
1053 }
1054
1055 return this.renderer.listitem(body);
1056 }
1057 case 'html': {
1058 var html = !this.token.pre && !this.options.pedantic
1059 ? this.inline.output(this.token.text)
1060 : this.token.text;
1061 return this.renderer.html(html);
1062 }
1063 case 'paragraph': {
1064 return this.renderer.paragraph(this.inline.output(this.token.text));
1065 }
1066 case 'text': {
1067 return this.renderer.paragraph(this.parseText());
1068 }
1069 }
1070};
1071
1072/**
1073 * Helpers
1074 */
1075
1076function escape(html, encode) {
1077 return html
1078 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
1079 .replace(/</g, '&lt;')
1080 .replace(/>/g, '&gt;')
1081 .replace(/"/g, '&quot;')
1082 .replace(/'/g, '&#39;');
1083}
1084
1085function unescape(html) {
1086 return html.replace(/&([#\w]+);/g, function(_, n) {
1087 n = n.toLowerCase();
1088 if (n === 'colon') return ':';
1089 if (n.charAt(0) === '#') {
1090 return n.charAt(1) === 'x'
1091 ? String.fromCharCode(parseInt(n.substring(2), 16))
1092 : String.fromCharCode(+n.substring(1));
1093 }
1094 return '';
1095 });
1096}
1097
1098function replace(regex, opt) {
1099 regex = regex.source;
1100 opt = opt || '';
1101 return function self(name, val) {
1102 if (!name) return new RegExp(regex, opt);
1103 val = val.source || val;
1104 val = val.replace(/(^|[^\[])\^/g, '$1');
1105 regex = regex.replace(name, val);
1106 return self;
1107 };
1108}
1109
1110function noop() {}
1111noop.exec = noop;
1112
1113function merge(obj) {
1114 var i = 1
1115 , target
1116 , key;
1117
1118 for (; i < arguments.length; i++) {
1119 target = arguments[i];
1120 for (key in target) {
1121 if (Object.prototype.hasOwnProperty.call(target, key)) {
1122 obj[key] = target[key];
1123 }
1124 }
1125 }
1126
1127 return obj;
1128}
1129
1130
1131/**
1132 * Marked
1133 */
1134
1135function marked(src, opt, callback) {
1136 if (callback || typeof opt === 'function') {
1137 if (!callback) {
1138 callback = opt;
1139 opt = null;
1140 }
1141
1142 opt = merge({}, marked.defaults, opt || {});
1143
1144 var highlight = opt.highlight
1145 , tokens
1146 , pending
1147 , i = 0;
1148
1149 try {
1150 tokens = Lexer.lex(src, opt)
1151 } catch (e) {
1152 return callback(e);
1153 }
1154
1155 pending = tokens.length;
1156
1157 var done = function(err) {
1158 if (err) {
1159 opt.highlight = highlight;
1160 return callback(err);
1161 }
1162
1163 var out;
1164
1165 try {
1166 out = Parser.parse(tokens, opt);
1167 } catch (e) {
1168 err = e;
1169 }
1170
1171 opt.highlight = highlight;
1172
1173 return err
1174 ? callback(err)
1175 : callback(null, out);
1176 };
1177
1178 if (!highlight || highlight.length < 3) {
1179 return done();
1180 }
1181
1182 delete opt.highlight;
1183
1184 if (!pending) return done();
1185
1186 for (; i < tokens.length; i++) {
1187 (function(token) {
1188 if (token.type !== 'code') {
1189 return --pending || done();
1190 }
1191 return highlight(token.text, token.lang, function(err, code) {
1192 if (err) return done(err);
1193 if (code == null || code === token.text) {
1194 return --pending || done();
1195 }
1196 token.text = code;
1197 token.escaped = true;
1198 --pending || done();
1199 });
1200 })(tokens[i]);
1201 }
1202
1203 return;
1204 }
1205 try {
1206 if (opt) opt = merge({}, marked.defaults, opt);
1207 return Parser.parse(Lexer.lex(src, opt), opt);
1208 } catch (e) {
1209 e.message += '\nPlease report this to https://github.com/chjj/marked.';
1210 if ((opt || marked.defaults).silent) {
1211 return '<p>An error occured:</p><pre>'
1212 + escape(e.message + '', true)
1213 + '</pre>';
1214 }
1215 throw e;
1216 }
1217}
1218
1219/**
1220 * Options
1221 */
1222
1223marked.options =
1224marked.setOptions = function(opt) {
1225 merge(marked.defaults, opt);
1226 return marked;
1227};
1228
1229marked.defaults = {
1230 gfm: true,
1231 tables: true,
1232 breaks: false,
1233 pedantic: false,
1234 sanitize: false,
1235 smartLists: false,
1236 silent: false,
1237 highlight: null,
1238 langPrefix: 'lang-',
1239 smartypants: false,
1240 headerPrefix: '',
1241 renderer: new Renderer,
1242 xhtml: false
1243};
1244
1245/**
1246 * Expose
1247 */
1248
1249marked.Parser = Parser;
1250marked.parser = Parser.parse;
1251
1252marked.Renderer = Renderer;
1253
1254marked.Lexer = Lexer;
1255marked.lexer = Lexer.lex;
1256
1257marked.InlineLexer = InlineLexer;
1258marked.inlineLexer = InlineLexer.output;
1259
1260marked.parse = marked;
1261
1262if (typeof module !== 'undefined' && typeof exports === 'object') {
1263 module.exports = marked;
1264} else if (typeof define === 'function' && define.amd) {
1265 define(function() { return marked; });
1266} else {
1267 this.marked = marked;
1268}
1269
1270}).call(function() {
1271 return this || (typeof window !== 'undefined' ? window : global);
1272}());