UNPKG

32.4 kBJavaScriptView Raw
1/**
2 * marked - a markdown parser
3 * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
4 * https://github.com/markedjs/marked
5 */
6
7;(function(root) {
8'use strict';
9
10/**
11 * Block-Level Grammar
12 */
13
14var block = {
15 newline: /^\n+/,
16 code: /^( {4}[^\n]+\n*)+/,
17 fences: noop,
18 hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
19 heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
20 nptable: noop,
21 blockquote: /^( {0,3}> ?(paragraph|[^\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: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
25 table: noop,
26 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
27 paragraph: /^([^\n]+(?:\n?(?!hr|heading|lheading| {0,3}>|tag)[^\n]+)+)/,
28 text: /^[^\n]+/
29};
30
31block._label = /(?:\\[\[\]]|[^\[\]])+/;
32block._title = /(?:"(?:\\"|[^"]|"[^"\n]*")*"|'\n?(?:[^'\n]+\n?)*'|\([^()]*\))/;
33block.def = edit(block.def)
34 .replace('label', block._label)
35 .replace('title', block._title)
36 .getRegex();
37
38block.bullet = /(?:[*+-]|\d+\.)/;
39block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
40block.item = edit(block.item, 'gm')
41 .replace(/bull/g, block.bullet)
42 .getRegex();
43
44block.list = edit(block.list)
45 .replace(/bull/g, block.bullet)
46 .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
47 .replace('def', '\\n+(?=' + block.def.source + ')')
48 .getRegex();
49
50block._tag = '(?!(?:'
51 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
52 + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
53 + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b';
54
55block.html = edit(block.html)
56 .replace('comment', /<!--[\s\S]*?-->/)
57 .replace('closed', /<(tag)[\s\S]+?<\/\1>/)
58 .replace('closing', /<tag(?:"[^"]*"|'[^']*'|\s[^'"\/>\s]*)*?\/?>/)
59 .replace(/tag/g, block._tag)
60 .getRegex();
61
62block.paragraph = edit(block.paragraph)
63 .replace('hr', block.hr)
64 .replace('heading', block.heading)
65 .replace('lheading', block.lheading)
66 .replace('tag', '<' + block._tag)
67 .getRegex();
68
69block.blockquote = edit(block.blockquote)
70 .replace('paragraph', block.paragraph)
71 .getRegex();
72
73/**
74 * Normal Block Grammar
75 */
76
77block.normal = merge({}, block);
78
79/**
80 * GFM Block Grammar
81 */
82
83block.gfm = merge({}, block.normal, {
84 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,
85 paragraph: /^/,
86 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
87});
88
89block.gfm.paragraph = edit(block.paragraph)
90 .replace('(?!', '(?!'
91 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
92 + block.list.source.replace('\\1', '\\3') + '|')
93 .getRegex();
94
95/**
96 * GFM + Tables Block Grammar
97 */
98
99block.tables = merge({}, block.gfm, {
100 nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
101 table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
102});
103
104/**
105 * Block Lexer
106 */
107
108function Lexer(options) {
109 this.tokens = [];
110 this.tokens.links = {};
111 this.options = options || marked.defaults;
112 this.rules = block.normal;
113
114 if (this.options.gfm) {
115 if (this.options.tables) {
116 this.rules = block.tables;
117 } else {
118 this.rules = block.gfm;
119 }
120 }
121}
122
123/**
124 * Expose Block Rules
125 */
126
127Lexer.rules = block;
128
129/**
130 * Static Lex Method
131 */
132
133Lexer.lex = function(src, options) {
134 var lexer = new Lexer(options);
135 return lexer.lex(src);
136};
137
138/**
139 * Preprocessing
140 */
141
142Lexer.prototype.lex = function(src) {
143 src = src
144 .replace(/\r\n|\r/g, '\n')
145 .replace(/\t/g, ' ')
146 .replace(/\u00a0/g, ' ')
147 .replace(/\u2424/g, '\n');
148
149 return this.token(src, true);
150};
151
152/**
153 * Lexing
154 */
155
156Lexer.prototype.token = function(src, top) {
157 src = src.replace(/^ +$/gm, '');
158 var next,
159 loose,
160 cap,
161 bull,
162 b,
163 item,
164 space,
165 i,
166 tag,
167 l,
168 isordered;
169
170 while (src) {
171 // newline
172 if (cap = this.rules.newline.exec(src)) {
173 src = src.substring(cap[0].length);
174 if (cap[0].length > 1) {
175 this.tokens.push({
176 type: 'space'
177 });
178 }
179 }
180
181 // code
182 if (cap = this.rules.code.exec(src)) {
183 src = src.substring(cap[0].length);
184 cap = cap[0].replace(/^ {4}/gm, '');
185 this.tokens.push({
186 type: 'code',
187 text: !this.options.pedantic
188 ? cap.replace(/\n+$/, '')
189 : cap
190 });
191 continue;
192 }
193
194 // fences (gfm)
195 if (cap = this.rules.fences.exec(src)) {
196 src = src.substring(cap[0].length);
197 this.tokens.push({
198 type: 'code',
199 lang: cap[2],
200 text: cap[3] || ''
201 });
202 continue;
203 }
204
205 // heading
206 if (cap = this.rules.heading.exec(src)) {
207 src = src.substring(cap[0].length);
208 this.tokens.push({
209 type: 'heading',
210 depth: cap[1].length,
211 text: cap[2]
212 });
213 continue;
214 }
215
216 // table no leading pipe (gfm)
217 if (top && (cap = this.rules.nptable.exec(src))) {
218 src = src.substring(cap[0].length);
219
220 item = {
221 type: 'table',
222 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
223 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
224 cells: cap[3].replace(/\n$/, '').split('\n')
225 };
226
227 for (i = 0; i < item.align.length; i++) {
228 if (/^ *-+: *$/.test(item.align[i])) {
229 item.align[i] = 'right';
230 } else if (/^ *:-+: *$/.test(item.align[i])) {
231 item.align[i] = 'center';
232 } else if (/^ *:-+ *$/.test(item.align[i])) {
233 item.align[i] = 'left';
234 } else {
235 item.align[i] = null;
236 }
237 }
238
239 for (i = 0; i < item.cells.length; i++) {
240 item.cells[i] = item.cells[i].split(/ *\| */);
241 }
242
243 this.tokens.push(item);
244
245 continue;
246 }
247
248 // hr
249 if (cap = this.rules.hr.exec(src)) {
250 src = src.substring(cap[0].length);
251 this.tokens.push({
252 type: 'hr'
253 });
254 continue;
255 }
256
257 // blockquote
258 if (cap = this.rules.blockquote.exec(src)) {
259 src = src.substring(cap[0].length);
260
261 this.tokens.push({
262 type: 'blockquote_start'
263 });
264
265 cap = cap[0].replace(/^ *> ?/gm, '');
266
267 // Pass `top` to keep the current
268 // "toplevel" state. This is exactly
269 // how markdown.pl works.
270 this.token(cap, top);
271
272 this.tokens.push({
273 type: 'blockquote_end'
274 });
275
276 continue;
277 }
278
279 // list
280 if (cap = this.rules.list.exec(src)) {
281 src = src.substring(cap[0].length);
282 bull = cap[2];
283 isordered = bull.length > 1;
284
285 this.tokens.push({
286 type: 'list_start',
287 ordered: isordered,
288 start: isordered ? +bull : ''
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);
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: !this.options.sanitizer
363 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
364 text: cap[0]
365 });
366 continue;
367 }
368
369 // def
370 if (top && (cap = this.rules.def.exec(src))) {
371 src = src.substring(cap[0].length);
372 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
373 tag = cap[1].toLowerCase();
374 if (!this.tokens.links[tag]) {
375 this.tokens.links[tag] = {
376 href: cap[2],
377 title: cap[3]
378 };
379 }
380 continue;
381 }
382
383 // table (gfm)
384 if (top && (cap = this.rules.table.exec(src))) {
385 src = src.substring(cap[0].length);
386
387 item = {
388 type: 'table',
389 header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
390 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
391 cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
392 };
393
394 for (i = 0; i < item.align.length; i++) {
395 if (/^ *-+: *$/.test(item.align[i])) {
396 item.align[i] = 'right';
397 } else if (/^ *:-+: *$/.test(item.align[i])) {
398 item.align[i] = 'center';
399 } else if (/^ *:-+ *$/.test(item.align[i])) {
400 item.align[i] = 'left';
401 } else {
402 item.align[i] = null;
403 }
404 }
405
406 for (i = 0; i < item.cells.length; i++) {
407 item.cells[i] = item.cells[i]
408 .replace(/^ *\| *| *\| *$/g, '')
409 .split(/ *\| */);
410 }
411
412 this.tokens.push(item);
413
414 continue;
415 }
416
417 // lheading
418 if (cap = this.rules.lheading.exec(src)) {
419 src = src.substring(cap[0].length);
420 this.tokens.push({
421 type: 'heading',
422 depth: cap[2] === '=' ? 1 : 2,
423 text: cap[1]
424 });
425 continue;
426 }
427
428 // top-level paragraph
429 if (top && (cap = this.rules.paragraph.exec(src))) {
430 src = src.substring(cap[0].length);
431 this.tokens.push({
432 type: 'paragraph',
433 text: cap[1].charAt(cap[1].length - 1) === '\n'
434 ? cap[1].slice(0, -1)
435 : cap[1]
436 });
437 continue;
438 }
439
440 // text
441 if (cap = this.rules.text.exec(src)) {
442 // Top-level should never reach here.
443 src = src.substring(cap[0].length);
444 this.tokens.push({
445 type: 'text',
446 text: cap[0]
447 });
448 continue;
449 }
450
451 if (src) {
452 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
453 }
454 }
455
456 return this.tokens;
457};
458
459/**
460 * Inline-Level Grammar
461 */
462
463var inline = {
464 escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
465 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
466 url: noop,
467 tag: /^<!--[\s\S]*?-->|^<\/?[a-zA-Z0-9\-]+(?:"[^"]*"|'[^']*'|\s[^<'">\/\s]*)*?\/?>/,
468 link: /^!?\[(inside)\]\(href\)/,
469 reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
470 nolink: /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\]/,
471 strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
472 em: /^_([^\s_](?:[^_]|__)+?[^\s_])_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)/,
473 code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
474 br: /^ {2,}\n(?!\s*$)/,
475 del: noop,
476 text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
477};
478
479inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
480inline._email = /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/;
481
482inline.autolink = edit(inline.autolink)
483 .replace('scheme', inline._scheme)
484 .replace('email', inline._email)
485 .getRegex()
486
487inline._inside = /(?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/;
488inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
489
490inline.link = edit(inline.link)
491 .replace('inside', inline._inside)
492 .replace('href', inline._href)
493 .getRegex();
494
495inline.reflink = edit(inline.reflink)
496 .replace('inside', inline._inside)
497 .getRegex();
498
499/**
500 * Normal Inline Grammar
501 */
502
503inline.normal = merge({}, inline);
504
505/**
506 * Pedantic Inline Grammar
507 */
508
509inline.pedantic = merge({}, inline.normal, {
510 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
511 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
512});
513
514/**
515 * GFM Inline Grammar
516 */
517
518inline.gfm = merge({}, inline.normal, {
519 escape: edit(inline.escape).replace('])', '~|])').getRegex(),
520 url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
521 .replace('email', inline._email)
522 .getRegex(),
523 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
524 del: /^~~(?=\S)([\s\S]*?\S)~~/,
525 text: edit(inline.text)
526 .replace(']|', '~]|')
527 .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
528 .getRegex()
529});
530
531/**
532 * GFM + Line Breaks Inline Grammar
533 */
534
535inline.breaks = merge({}, inline.gfm, {
536 br: edit(inline.br).replace('{2,}', '*').getRegex(),
537 text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
538});
539
540/**
541 * Inline Lexer & Compiler
542 */
543
544function InlineLexer(links, options) {
545 this.options = options || marked.defaults;
546 this.links = links;
547 this.rules = inline.normal;
548 this.renderer = this.options.renderer || new Renderer();
549 this.renderer.options = this.options;
550
551 if (!this.links) {
552 throw new Error('Tokens array requires a `links` property.');
553 }
554
555 if (this.options.gfm) {
556 if (this.options.breaks) {
557 this.rules = inline.breaks;
558 } else {
559 this.rules = inline.gfm;
560 }
561 } else if (this.options.pedantic) {
562 this.rules = inline.pedantic;
563 }
564}
565
566/**
567 * Expose Inline Rules
568 */
569
570InlineLexer.rules = inline;
571
572/**
573 * Static Lexing/Compiling Method
574 */
575
576InlineLexer.output = function(src, links, options) {
577 var inline = new InlineLexer(links, options);
578 return inline.output(src);
579};
580
581/**
582 * Lexing/Compiling
583 */
584
585InlineLexer.prototype.output = function(src) {
586 var out = '',
587 link,
588 text,
589 href,
590 cap;
591
592 while (src) {
593 // escape
594 if (cap = this.rules.escape.exec(src)) {
595 src = src.substring(cap[0].length);
596 out += cap[1];
597 continue;
598 }
599
600 // autolink
601 if (cap = this.rules.autolink.exec(src)) {
602 src = src.substring(cap[0].length);
603 if (cap[2] === '@') {
604 text = escape(this.mangle(cap[1]));
605 href = 'mailto:' + text;
606 } else {
607 text = escape(cap[1]);
608 href = text;
609 }
610 out += this.renderer.link(href, null, text);
611 continue;
612 }
613
614 // url (gfm)
615 if (!this.inLink && (cap = this.rules.url.exec(src))) {
616 cap[0] = this.rules._backpedal.exec(cap[0])[0];
617 src = src.substring(cap[0].length);
618 if (cap[2] === '@') {
619 text = escape(cap[0]);
620 href = 'mailto:' + text;
621 } else {
622 text = escape(cap[0]);
623 if (cap[1] === 'www.') {
624 href = 'http://' + text;
625 } else {
626 href = text;
627 }
628 }
629 out += this.renderer.link(href, null, text);
630 continue;
631 }
632
633 // tag
634 if (cap = this.rules.tag.exec(src)) {
635 if (!this.inLink && /^<a /i.test(cap[0])) {
636 this.inLink = true;
637 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
638 this.inLink = false;
639 }
640 src = src.substring(cap[0].length);
641 out += this.options.sanitize
642 ? this.options.sanitizer
643 ? this.options.sanitizer(cap[0])
644 : escape(cap[0])
645 : cap[0]
646 continue;
647 }
648
649 // link
650 if (cap = this.rules.link.exec(src)) {
651 src = src.substring(cap[0].length);
652 this.inLink = true;
653 out += this.outputLink(cap, {
654 href: cap[2],
655 title: cap[3]
656 });
657 this.inLink = false;
658 continue;
659 }
660
661 // reflink, nolink
662 if ((cap = this.rules.reflink.exec(src))
663 || (cap = this.rules.nolink.exec(src))) {
664 src = src.substring(cap[0].length);
665 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
666 link = this.links[link.toLowerCase()];
667 if (!link || !link.href) {
668 out += cap[0].charAt(0);
669 src = cap[0].substring(1) + src;
670 continue;
671 }
672 this.inLink = true;
673 out += this.outputLink(cap, link);
674 this.inLink = false;
675 continue;
676 }
677
678 // strong
679 if (cap = this.rules.strong.exec(src)) {
680 src = src.substring(cap[0].length);
681 out += this.renderer.strong(this.output(cap[2] || cap[1]));
682 continue;
683 }
684
685 // em
686 if (cap = this.rules.em.exec(src)) {
687 src = src.substring(cap[0].length);
688 out += this.renderer.em(this.output(cap[2] || cap[1]));
689 continue;
690 }
691
692 // code
693 if (cap = this.rules.code.exec(src)) {
694 src = src.substring(cap[0].length);
695 out += this.renderer.codespan(escape(cap[2].trim(), true));
696 continue;
697 }
698
699 // br
700 if (cap = this.rules.br.exec(src)) {
701 src = src.substring(cap[0].length);
702 out += this.renderer.br();
703 continue;
704 }
705
706 // del (gfm)
707 if (cap = this.rules.del.exec(src)) {
708 src = src.substring(cap[0].length);
709 out += this.renderer.del(this.output(cap[1]));
710 continue;
711 }
712
713 // text
714 if (cap = this.rules.text.exec(src)) {
715 src = src.substring(cap[0].length);
716 out += this.renderer.text(escape(this.smartypants(cap[0])));
717 continue;
718 }
719
720 if (src) {
721 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
722 }
723 }
724
725 return out;
726};
727
728/**
729 * Compile Link
730 */
731
732InlineLexer.prototype.outputLink = function(cap, link) {
733 var href = escape(link.href),
734 title = link.title ? escape(link.title) : null;
735
736 return cap[0].charAt(0) !== '!'
737 ? this.renderer.link(href, title, this.output(cap[1]))
738 : this.renderer.image(href, title, escape(cap[1]));
739};
740
741/**
742 * Smartypants Transformations
743 */
744
745InlineLexer.prototype.smartypants = function(text) {
746 if (!this.options.smartypants) return text;
747 return text
748 // em-dashes
749 .replace(/---/g, '\u2014')
750 // en-dashes
751 .replace(/--/g, '\u2013')
752 // opening singles
753 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
754 // closing singles & apostrophes
755 .replace(/'/g, '\u2019')
756 // opening doubles
757 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
758 // closing doubles
759 .replace(/"/g, '\u201d')
760 // ellipses
761 .replace(/\.{3}/g, '\u2026');
762};
763
764/**
765 * Mangle Links
766 */
767
768InlineLexer.prototype.mangle = function(text) {
769 if (!this.options.mangle) return text;
770 var out = '',
771 l = text.length,
772 i = 0,
773 ch;
774
775 for (; i < l; i++) {
776 ch = text.charCodeAt(i);
777 if (Math.random() > 0.5) {
778 ch = 'x' + ch.toString(16);
779 }
780 out += '&#' + ch + ';';
781 }
782
783 return out;
784};
785
786/**
787 * Renderer
788 */
789
790function Renderer(options) {
791 this.options = options || {};
792}
793
794Renderer.prototype.code = function(code, lang, escaped) {
795 if (this.options.highlight) {
796 var out = this.options.highlight(code, lang);
797 if (out != null && out !== code) {
798 escaped = true;
799 code = out;
800 }
801 }
802
803 if (!lang) {
804 return '<pre><code>'
805 + (escaped ? code : escape(code, true))
806 + '\n</code></pre>';
807 }
808
809 return '<pre><code class="'
810 + this.options.langPrefix
811 + escape(lang, true)
812 + '">'
813 + (escaped ? code : escape(code, true))
814 + '\n</code></pre>\n';
815};
816
817Renderer.prototype.blockquote = function(quote) {
818 return '<blockquote>\n' + quote + '</blockquote>\n';
819};
820
821Renderer.prototype.html = function(html) {
822 return html;
823};
824
825Renderer.prototype.heading = function(text, level, raw) {
826 return '<h'
827 + level
828 + ' id="'
829 + this.options.headerPrefix
830 + raw.toLowerCase().replace(/[^\w]+/g, '-')
831 + '">'
832 + text
833 + '</h'
834 + level
835 + '>\n';
836};
837
838Renderer.prototype.hr = function() {
839 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
840};
841
842Renderer.prototype.list = function(body, ordered, start) {
843 var type = ordered ? 'ol' : 'ul',
844 startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
845 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
846};
847
848Renderer.prototype.listitem = function(text) {
849 return '<li>' + text + '</li>\n';
850};
851
852Renderer.prototype.paragraph = function(text) {
853 return '<p>' + text + '</p>\n';
854};
855
856Renderer.prototype.table = function(header, body) {
857 return '<table>\n'
858 + '<thead>\n'
859 + header
860 + '</thead>\n'
861 + '<tbody>\n'
862 + body
863 + '</tbody>\n'
864 + '</table>\n';
865};
866
867Renderer.prototype.tablerow = function(content) {
868 return '<tr>\n' + content + '</tr>\n';
869};
870
871Renderer.prototype.tablecell = function(content, flags) {
872 var type = flags.header ? 'th' : 'td';
873 var tag = flags.align
874 ? '<' + type + ' style="text-align:' + flags.align + '">'
875 : '<' + type + '>';
876 return tag + content + '</' + type + '>\n';
877};
878
879// span level renderer
880Renderer.prototype.strong = function(text) {
881 return '<strong>' + text + '</strong>';
882};
883
884Renderer.prototype.em = function(text) {
885 return '<em>' + text + '</em>';
886};
887
888Renderer.prototype.codespan = function(text) {
889 return '<code>' + text + '</code>';
890};
891
892Renderer.prototype.br = function() {
893 return this.options.xhtml ? '<br/>' : '<br>';
894};
895
896Renderer.prototype.del = function(text) {
897 return '<del>' + text + '</del>';
898};
899
900Renderer.prototype.link = function(href, title, text) {
901 if (this.options.sanitize) {
902 try {
903 var prot = decodeURIComponent(unescape(href))
904 .replace(/[^\w:]/g, '')
905 .toLowerCase();
906 } catch (e) {
907 return text;
908 }
909 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
910 return text;
911 }
912 }
913 if (this.options.baseUrl && !originIndependentUrl.test(href)) {
914 href = resolveUrl(this.options.baseUrl, href);
915 }
916 var out = '<a href="' + href + '"';
917 if (title) {
918 out += ' title="' + title + '"';
919 }
920 out += '>' + text + '</a>';
921 return out;
922};
923
924Renderer.prototype.image = function(href, title, text) {
925 if (this.options.baseUrl && !originIndependentUrl.test(href)) {
926 href = resolveUrl(this.options.baseUrl, href);
927 }
928 var out = '<img src="' + href + '" alt="' + text + '"';
929 if (title) {
930 out += ' title="' + title + '"';
931 }
932 out += this.options.xhtml ? '/>' : '>';
933 return out;
934};
935
936Renderer.prototype.text = function(text) {
937 return text;
938};
939
940/**
941 * TextRenderer
942 * returns only the textual part of the token
943 */
944
945function TextRenderer() {}
946
947// no need for block level renderers
948
949TextRenderer.prototype.strong =
950TextRenderer.prototype.em =
951TextRenderer.prototype.codespan =
952TextRenderer.prototype.del =
953TextRenderer.prototype.text = function (text) {
954 return text;
955}
956
957TextRenderer.prototype.link =
958TextRenderer.prototype.image = function(href, title, text) {
959 return '' + text;
960}
961
962TextRenderer.prototype.br = function() {
963 return '';
964}
965
966/**
967 * Parsing & Compiling
968 */
969
970function Parser(options) {
971 this.tokens = [];
972 this.token = null;
973 this.options = options || marked.defaults;
974 this.options.renderer = this.options.renderer || new Renderer();
975 this.renderer = this.options.renderer;
976 this.renderer.options = this.options;
977}
978
979/**
980 * Static Parse Method
981 */
982
983Parser.parse = function(src, options) {
984 var parser = new Parser(options);
985 return parser.parse(src);
986};
987
988/**
989 * Parse Loop
990 */
991
992Parser.prototype.parse = function(src) {
993 this.inline = new InlineLexer(src.links, this.options);
994 // use an InlineLexer with a TextRenderer to extract pure text
995 this.inlineText = new InlineLexer(
996 src.links,
997 merge({}, this.options, {renderer: new TextRenderer()})
998 );
999 this.tokens = src.reverse();
1000
1001 var out = '';
1002 while (this.next()) {
1003 out += this.tok();
1004 }
1005
1006 return out;
1007};
1008
1009/**
1010 * Next Token
1011 */
1012
1013Parser.prototype.next = function() {
1014 return this.token = this.tokens.pop();
1015};
1016
1017/**
1018 * Preview Next Token
1019 */
1020
1021Parser.prototype.peek = function() {
1022 return this.tokens[this.tokens.length - 1] || 0;
1023};
1024
1025/**
1026 * Parse Text Tokens
1027 */
1028
1029Parser.prototype.parseText = function() {
1030 var body = this.token.text;
1031
1032 while (this.peek().type === 'text') {
1033 body += '\n' + this.next().text;
1034 }
1035
1036 return this.inline.output(body);
1037};
1038
1039/**
1040 * Parse Current Token
1041 */
1042
1043Parser.prototype.tok = function() {
1044 switch (this.token.type) {
1045 case 'space': {
1046 return '';
1047 }
1048 case 'hr': {
1049 return this.renderer.hr();
1050 }
1051 case 'heading': {
1052 return this.renderer.heading(
1053 this.inline.output(this.token.text),
1054 this.token.depth,
1055 unescape(this.inlineText.output(this.token.text)));
1056 }
1057 case 'code': {
1058 return this.renderer.code(this.token.text,
1059 this.token.lang,
1060 this.token.escaped);
1061 }
1062 case 'table': {
1063 var header = '',
1064 body = '',
1065 i,
1066 row,
1067 cell,
1068 j;
1069
1070 // header
1071 cell = '';
1072 for (i = 0; i < this.token.header.length; i++) {
1073 cell += this.renderer.tablecell(
1074 this.inline.output(this.token.header[i]),
1075 { header: true, align: this.token.align[i] }
1076 );
1077 }
1078 header += this.renderer.tablerow(cell);
1079
1080 for (i = 0; i < this.token.cells.length; i++) {
1081 row = this.token.cells[i];
1082
1083 cell = '';
1084 for (j = 0; j < row.length; j++) {
1085 cell += this.renderer.tablecell(
1086 this.inline.output(row[j]),
1087 { header: false, align: this.token.align[j] }
1088 );
1089 }
1090
1091 body += this.renderer.tablerow(cell);
1092 }
1093 return this.renderer.table(header, body);
1094 }
1095 case 'blockquote_start': {
1096 body = '';
1097
1098 while (this.next().type !== 'blockquote_end') {
1099 body += this.tok();
1100 }
1101
1102 return this.renderer.blockquote(body);
1103 }
1104 case 'list_start': {
1105 body = '';
1106 var ordered = this.token.ordered,
1107 start = this.token.start;
1108
1109 while (this.next().type !== 'list_end') {
1110 body += this.tok();
1111 }
1112
1113 return this.renderer.list(body, ordered, start);
1114 }
1115 case 'list_item_start': {
1116 body = '';
1117
1118 while (this.next().type !== 'list_item_end') {
1119 body += this.token.type === 'text'
1120 ? this.parseText()
1121 : this.tok();
1122 }
1123
1124 return this.renderer.listitem(body);
1125 }
1126 case 'loose_item_start': {
1127 body = '';
1128
1129 while (this.next().type !== 'list_item_end') {
1130 body += this.tok();
1131 }
1132
1133 return this.renderer.listitem(body);
1134 }
1135 case 'html': {
1136 var html = !this.token.pre && !this.options.pedantic
1137 ? this.inline.output(this.token.text)
1138 : this.token.text;
1139 return this.renderer.html(html);
1140 }
1141 case 'paragraph': {
1142 return this.renderer.paragraph(this.inline.output(this.token.text));
1143 }
1144 case 'text': {
1145 return this.renderer.paragraph(this.parseText());
1146 }
1147 }
1148};
1149
1150/**
1151 * Helpers
1152 */
1153
1154function escape(html, encode) {
1155 return html
1156 .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
1157 .replace(/</g, '&lt;')
1158 .replace(/>/g, '&gt;')
1159 .replace(/"/g, '&quot;')
1160 .replace(/'/g, '&#39;');
1161}
1162
1163function unescape(html) {
1164 // explicitly match decimal, hex, and named HTML entities
1165 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
1166 n = n.toLowerCase();
1167 if (n === 'colon') return ':';
1168 if (n.charAt(0) === '#') {
1169 return n.charAt(1) === 'x'
1170 ? String.fromCharCode(parseInt(n.substring(2), 16))
1171 : String.fromCharCode(+n.substring(1));
1172 }
1173 return '';
1174 });
1175}
1176
1177function edit(regex, opt) {
1178 regex = regex.source;
1179 opt = opt || '';
1180 return {
1181 replace: function(name, val) {
1182 val = val.source || val;
1183 val = val.replace(/(^|[^\[])\^/g, '$1');
1184 regex = regex.replace(name, val);
1185 return this;
1186 },
1187 getRegex: function() {
1188 return new RegExp(regex, opt);
1189 }
1190 };
1191}
1192
1193function resolveUrl(base, href) {
1194 if (!baseUrls[' ' + base]) {
1195 // we can ignore everything in base after the last slash of its path component,
1196 // but we might need to add _that_
1197 // https://tools.ietf.org/html/rfc3986#section-3
1198 if (/^[^:]+:\/*[^/]*$/.test(base)) {
1199 baseUrls[' ' + base] = base + '/';
1200 } else {
1201 baseUrls[' ' + base] = base.replace(/[^/]*$/, '');
1202 }
1203 }
1204 base = baseUrls[' ' + base];
1205
1206 if (href.slice(0, 2) === '//') {
1207 return base.replace(/:[\s\S]*/, ':') + href;
1208 } else if (href.charAt(0) === '/') {
1209 return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
1210 } else {
1211 return base + href;
1212 }
1213}
1214var baseUrls = {};
1215var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
1216
1217function noop() {}
1218noop.exec = noop;
1219
1220function merge(obj) {
1221 var i = 1,
1222 target,
1223 key;
1224
1225 for (; i < arguments.length; i++) {
1226 target = arguments[i];
1227 for (key in target) {
1228 if (Object.prototype.hasOwnProperty.call(target, key)) {
1229 obj[key] = target[key];
1230 }
1231 }
1232 }
1233
1234 return obj;
1235}
1236
1237/**
1238 * Marked
1239 */
1240
1241function marked(src, opt, callback) {
1242 // throw error in case of non string input
1243 if (typeof src === 'undefined' || src === null) {
1244 throw new Error('marked(): input parameter is undefined or null');
1245 }
1246 if (typeof src !== 'string') {
1247 throw new Error('marked(): input parameter is of type '
1248 + Object.prototype.toString.call(src) + ', string expected');
1249 }
1250
1251 if (callback || typeof opt === 'function') {
1252 if (!callback) {
1253 callback = opt;
1254 opt = null;
1255 }
1256
1257 opt = merge({}, marked.defaults, opt || {});
1258
1259 var highlight = opt.highlight,
1260 tokens,
1261 pending,
1262 i = 0;
1263
1264 try {
1265 tokens = Lexer.lex(src, opt)
1266 } catch (e) {
1267 return callback(e);
1268 }
1269
1270 pending = tokens.length;
1271
1272 var done = function(err) {
1273 if (err) {
1274 opt.highlight = highlight;
1275 return callback(err);
1276 }
1277
1278 var out;
1279
1280 try {
1281 out = Parser.parse(tokens, opt);
1282 } catch (e) {
1283 err = e;
1284 }
1285
1286 opt.highlight = highlight;
1287
1288 return err
1289 ? callback(err)
1290 : callback(null, out);
1291 };
1292
1293 if (!highlight || highlight.length < 3) {
1294 return done();
1295 }
1296
1297 delete opt.highlight;
1298
1299 if (!pending) return done();
1300
1301 for (; i < tokens.length; i++) {
1302 (function(token) {
1303 if (token.type !== 'code') {
1304 return --pending || done();
1305 }
1306 return highlight(token.text, token.lang, function(err, code) {
1307 if (err) return done(err);
1308 if (code == null || code === token.text) {
1309 return --pending || done();
1310 }
1311 token.text = code;
1312 token.escaped = true;
1313 --pending || done();
1314 });
1315 })(tokens[i]);
1316 }
1317
1318 return;
1319 }
1320 try {
1321 if (opt) opt = merge({}, marked.defaults, opt);
1322 return Parser.parse(Lexer.lex(src, opt), opt);
1323 } catch (e) {
1324 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
1325 if ((opt || marked.defaults).silent) {
1326 return '<p>An error occurred:</p><pre>'
1327 + escape(e.message + '', true)
1328 + '</pre>';
1329 }
1330 throw e;
1331 }
1332}
1333
1334/**
1335 * Options
1336 */
1337
1338marked.options =
1339marked.setOptions = function(opt) {
1340 merge(marked.defaults, opt);
1341 return marked;
1342};
1343
1344marked.defaults = {
1345 gfm: true,
1346 tables: true,
1347 breaks: false,
1348 pedantic: false,
1349 sanitize: false,
1350 sanitizer: null,
1351 mangle: true,
1352 smartLists: false,
1353 silent: false,
1354 highlight: null,
1355 langPrefix: 'lang-',
1356 smartypants: false,
1357 headerPrefix: '',
1358 renderer: new Renderer(),
1359 xhtml: false,
1360 baseUrl: null
1361};
1362
1363/**
1364 * Expose
1365 */
1366
1367marked.Parser = Parser;
1368marked.parser = Parser.parse;
1369
1370marked.Renderer = Renderer;
1371marked.TextRenderer = TextRenderer;
1372
1373marked.Lexer = Lexer;
1374marked.lexer = Lexer.lex;
1375
1376marked.InlineLexer = InlineLexer;
1377marked.inlineLexer = InlineLexer.output;
1378
1379marked.parse = marked;
1380
1381if (typeof module !== 'undefined' && typeof exports === 'object') {
1382 module.exports = marked;
1383} else if (typeof define === 'function' && define.amd) {
1384 define(function() { return marked; });
1385} else {
1386 root.marked = marked;
1387}
1388})(this || (typeof window !== 'undefined' ? window : global));