UNPKG

39.9 kBJavaScriptView Raw
1/**
2 * marked - a markdown parser
3 * Copyright (c) 2011-2018, 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: '^ {0,3}(?:' // optional indentation
24 + '<(script|pre|style)[\\s>][\\s\\S]*?(?:</\\1>[^\\n]*\\n+|$)' // (1)
25 + '|comment[^\\n]*(\\n+|$)' // (2)
26 + '|<\\?[\\s\\S]*?\\?>\\n*' // (3)
27 + '|<![A-Z][\\s\\S]*?>\\n*' // (4)
28 + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>\\n*' // (5)
29 + '|</?(tag)(?: +|\\n|/?>)[\\s\\S]*?(?:\\n{2,}|$)' // (6)
30 + '|<(?!script|pre|style)([a-z][\\w-]*)(?:attribute)*? */?>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) open tag
31 + '|</(?!script|pre|style)[a-z][\\w-]*\\s*>(?=\\h*\\n)[\\s\\S]*?(?:\\n{2,}|$)' // (7) closing tag
32 + ')',
33 def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
34 table: noop,
35 lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
36 paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,
37 text: /^[^\n]+/
38};
39
40block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
41block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
42block.def = edit(block.def)
43 .replace('label', block._label)
44 .replace('title', block._title)
45 .getRegex();
46
47block.bullet = /(?:[*+-]|\d+\.)/;
48block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
49block.item = edit(block.item, 'gm')
50 .replace(/bull/g, block.bullet)
51 .getRegex();
52
53block.list = edit(block.list)
54 .replace(/bull/g, block.bullet)
55 .replace('hr', '\\n+(?=\\1?(?:(?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$))')
56 .replace('def', '\\n+(?=' + block.def.source + ')')
57 .getRegex();
58
59block._tag = 'address|article|aside|base|basefont|blockquote|body|caption'
60 + '|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption'
61 + '|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe'
62 + '|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option'
63 + '|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|title|tr'
64 + '|track|ul';
65block._comment = /<!--(?!-?>)[\s\S]*?-->/;
66block.html = edit(block.html, 'i')
67 .replace('comment', block._comment)
68 .replace('tag', block._tag)
69 .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/)
70 .getRegex();
71
72block.paragraph = edit(block.paragraph)
73 .replace('hr', block.hr)
74 .replace('heading', block.heading)
75 .replace('lheading', block.lheading)
76 .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
77 .getRegex();
78
79block.blockquote = edit(block.blockquote)
80 .replace('paragraph', block.paragraph)
81 .getRegex();
82
83/**
84 * Normal Block Grammar
85 */
86
87block.normal = merge({}, block);
88
89/**
90 * GFM Block Grammar
91 */
92
93block.gfm = merge({}, block.normal, {
94 fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\n? *\1 *(?:\n+|$)/,
95 paragraph: /^/,
96 heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
97});
98
99block.gfm.paragraph = edit(block.paragraph)
100 .replace('(?!', '(?!'
101 + block.gfm.fences.source.replace('\\1', '\\2') + '|'
102 + block.list.source.replace('\\1', '\\3') + '|')
103 .getRegex();
104
105/**
106 * GFM + Tables Block Grammar
107 */
108
109block.tables = merge({}, block.gfm, {
110 nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
111 table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
112});
113
114/**
115 * Pedantic grammar
116 */
117
118block.pedantic = merge({}, block.normal, {
119 html: edit(
120 '^ *(?:comment *(?:\\n|\\s*$)'
121 + '|<(tag)[\\s\\S]+?</\\1> *(?:\\n{2,}|\\s*$)' // closed tag
122 + '|<tag(?:"[^"]*"|\'[^\']*\'|\\s[^\'"/>\\s]*)*?/?> *(?:\\n{2,}|\\s*$))')
123 .replace('comment', block._comment)
124 .replace(/tag/g, '(?!(?:'
125 + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub'
126 + '|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)'
127 + '\\b)\\w+(?!:|[^\\w\\s@]*@)\\b')
128 .getRegex(),
129 def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/
130});
131
132/**
133 * Block Lexer
134 */
135
136function Lexer(options) {
137 this.tokens = [];
138 this.tokens.links = Object.create(null);
139 this.options = options || marked.defaults;
140 this.rules = block.normal;
141
142 if (this.options.pedantic) {
143 this.rules = block.pedantic;
144 } else if (this.options.gfm) {
145 if (this.options.tables) {
146 this.rules = block.tables;
147 } else {
148 this.rules = block.gfm;
149 }
150 }
151}
152
153/**
154 * Expose Block Rules
155 */
156
157Lexer.rules = block;
158
159/**
160 * Static Lex Method
161 */
162
163Lexer.lex = function(src, options) {
164 var lexer = new Lexer(options);
165 return lexer.lex(src);
166};
167
168/**
169 * Preprocessing
170 */
171
172Lexer.prototype.lex = function(src) {
173 src = src
174 .replace(/\r\n|\r/g, '\n')
175 .replace(/\t/g, ' ')
176 .replace(/\u00a0/g, ' ')
177 .replace(/\u2424/g, '\n');
178
179 return this.token(src, true);
180};
181
182/**
183 * Lexing
184 */
185
186Lexer.prototype.token = function(src, top) {
187 src = src.replace(/^ +$/gm, '');
188 var next,
189 loose,
190 cap,
191 bull,
192 b,
193 item,
194 listStart,
195 listItems,
196 t,
197 space,
198 i,
199 tag,
200 l,
201 isordered,
202 istask,
203 ischecked;
204
205 while (src) {
206 // newline
207 if (cap = this.rules.newline.exec(src)) {
208 src = src.substring(cap[0].length);
209 if (cap[0].length > 1) {
210 this.tokens.push({
211 type: 'space'
212 });
213 }
214 }
215
216 // code
217 if (cap = this.rules.code.exec(src)) {
218 src = src.substring(cap[0].length);
219 cap = cap[0].replace(/^ {4}/gm, '');
220 this.tokens.push({
221 type: 'code',
222 text: !this.options.pedantic
223 ? rtrim(cap, '\n')
224 : cap
225 });
226 continue;
227 }
228
229 // fences (gfm)
230 if (cap = this.rules.fences.exec(src)) {
231 src = src.substring(cap[0].length);
232 this.tokens.push({
233 type: 'code',
234 lang: cap[2],
235 text: cap[3] || ''
236 });
237 continue;
238 }
239
240 // heading
241 if (cap = this.rules.heading.exec(src)) {
242 src = src.substring(cap[0].length);
243 this.tokens.push({
244 type: 'heading',
245 depth: cap[1].length,
246 text: cap[2]
247 });
248 continue;
249 }
250
251 // table no leading pipe (gfm)
252 if (top && (cap = this.rules.nptable.exec(src))) {
253 item = {
254 type: 'table',
255 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
256 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
257 cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
258 };
259
260 if (item.header.length === item.align.length) {
261 src = src.substring(cap[0].length);
262
263 for (i = 0; i < item.align.length; i++) {
264 if (/^ *-+: *$/.test(item.align[i])) {
265 item.align[i] = 'right';
266 } else if (/^ *:-+: *$/.test(item.align[i])) {
267 item.align[i] = 'center';
268 } else if (/^ *:-+ *$/.test(item.align[i])) {
269 item.align[i] = 'left';
270 } else {
271 item.align[i] = null;
272 }
273 }
274
275 for (i = 0; i < item.cells.length; i++) {
276 item.cells[i] = splitCells(item.cells[i], item.header.length);
277 }
278
279 this.tokens.push(item);
280
281 continue;
282 }
283 }
284
285 // hr
286 if (cap = this.rules.hr.exec(src)) {
287 src = src.substring(cap[0].length);
288 this.tokens.push({
289 type: 'hr'
290 });
291 continue;
292 }
293
294 // blockquote
295 if (cap = this.rules.blockquote.exec(src)) {
296 src = src.substring(cap[0].length);
297
298 this.tokens.push({
299 type: 'blockquote_start'
300 });
301
302 cap = cap[0].replace(/^ *> ?/gm, '');
303
304 // Pass `top` to keep the current
305 // "toplevel" state. This is exactly
306 // how markdown.pl works.
307 this.token(cap, top);
308
309 this.tokens.push({
310 type: 'blockquote_end'
311 });
312
313 continue;
314 }
315
316 // list
317 if (cap = this.rules.list.exec(src)) {
318 src = src.substring(cap[0].length);
319 bull = cap[2];
320 isordered = bull.length > 1;
321
322 listStart = {
323 type: 'list_start',
324 ordered: isordered,
325 start: isordered ? +bull : '',
326 loose: false
327 };
328
329 this.tokens.push(listStart);
330
331 // Get each top-level item.
332 cap = cap[0].match(this.rules.item);
333
334 listItems = [];
335 next = false;
336 l = cap.length;
337 i = 0;
338
339 for (; i < l; i++) {
340 item = cap[i];
341
342 // Remove the list item's bullet
343 // so it is seen as the next token.
344 space = item.length;
345 item = item.replace(/^ *([*+-]|\d+\.) +/, '');
346
347 // Outdent whatever the
348 // list item contains. Hacky.
349 if (~item.indexOf('\n ')) {
350 space -= item.length;
351 item = !this.options.pedantic
352 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
353 : item.replace(/^ {1,4}/gm, '');
354 }
355
356 // Determine whether the next list item belongs here.
357 // Backpedal if it does not belong in this list.
358 if (this.options.smartLists && i !== l - 1) {
359 b = block.bullet.exec(cap[i + 1])[0];
360 if (bull !== b && !(bull.length > 1 && b.length > 1)) {
361 src = cap.slice(i + 1).join('\n') + src;
362 i = l - 1;
363 }
364 }
365
366 // Determine whether item is loose or not.
367 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
368 // for discount behavior.
369 loose = next || /\n\n(?!\s*$)/.test(item);
370 if (i !== l - 1) {
371 next = item.charAt(item.length - 1) === '\n';
372 if (!loose) loose = next;
373 }
374
375 if (loose) {
376 listStart.loose = true;
377 }
378
379 // Check for task list items
380 istask = /^\[[ xX]\] /.test(item);
381 ischecked = undefined;
382 if (istask) {
383 ischecked = item[1] !== ' ';
384 item = item.replace(/^\[[ xX]\] +/, '');
385 }
386
387 t = {
388 type: 'list_item_start',
389 task: istask,
390 checked: ischecked,
391 loose: loose
392 };
393
394 listItems.push(t);
395 this.tokens.push(t);
396
397 // Recurse.
398 this.token(item, false);
399
400 this.tokens.push({
401 type: 'list_item_end'
402 });
403 }
404
405 if (listStart.loose) {
406 l = listItems.length;
407 i = 0;
408 for (; i < l; i++) {
409 listItems[i].loose = true;
410 }
411 }
412
413 this.tokens.push({
414 type: 'list_end'
415 });
416
417 continue;
418 }
419
420 // html
421 if (cap = this.rules.html.exec(src)) {
422 src = src.substring(cap[0].length);
423 this.tokens.push({
424 type: this.options.sanitize
425 ? 'paragraph'
426 : 'html',
427 pre: !this.options.sanitizer
428 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
429 text: cap[0]
430 });
431 continue;
432 }
433
434 // def
435 if (top && (cap = this.rules.def.exec(src))) {
436 src = src.substring(cap[0].length);
437 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
438 tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
439 if (!this.tokens.links[tag]) {
440 this.tokens.links[tag] = {
441 href: cap[2],
442 title: cap[3]
443 };
444 }
445 continue;
446 }
447
448 // table (gfm)
449 if (top && (cap = this.rules.table.exec(src))) {
450 item = {
451 type: 'table',
452 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
453 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
454 cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
455 };
456
457 if (item.header.length === item.align.length) {
458 src = src.substring(cap[0].length);
459
460 for (i = 0; i < item.align.length; i++) {
461 if (/^ *-+: *$/.test(item.align[i])) {
462 item.align[i] = 'right';
463 } else if (/^ *:-+: *$/.test(item.align[i])) {
464 item.align[i] = 'center';
465 } else if (/^ *:-+ *$/.test(item.align[i])) {
466 item.align[i] = 'left';
467 } else {
468 item.align[i] = null;
469 }
470 }
471
472 for (i = 0; i < item.cells.length; i++) {
473 item.cells[i] = splitCells(
474 item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
475 item.header.length);
476 }
477
478 this.tokens.push(item);
479
480 continue;
481 }
482 }
483
484 // lheading
485 if (cap = this.rules.lheading.exec(src)) {
486 src = src.substring(cap[0].length);
487 this.tokens.push({
488 type: 'heading',
489 depth: cap[2] === '=' ? 1 : 2,
490 text: cap[1]
491 });
492 continue;
493 }
494
495 // top-level paragraph
496 if (top && (cap = this.rules.paragraph.exec(src))) {
497 src = src.substring(cap[0].length);
498 this.tokens.push({
499 type: 'paragraph',
500 text: cap[1].charAt(cap[1].length - 1) === '\n'
501 ? cap[1].slice(0, -1)
502 : cap[1]
503 });
504 continue;
505 }
506
507 // text
508 if (cap = this.rules.text.exec(src)) {
509 // Top-level should never reach here.
510 src = src.substring(cap[0].length);
511 this.tokens.push({
512 type: 'text',
513 text: cap[0]
514 });
515 continue;
516 }
517
518 if (src) {
519 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
520 }
521 }
522
523 return this.tokens;
524};
525
526/**
527 * Inline-Level Grammar
528 */
529
530var inline = {
531 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
532 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
533 url: noop,
534 tag: '^comment'
535 + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
536 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
537 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
538 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
539 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
540 link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
541 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
542 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
543 strong: /^__([^\s])__(?!_)|^\*\*([^\s])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
544 em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\s.])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\s.])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
545 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
546 br: /^( {2,}|\\)\n(?!\s*$)/,
547 del: noop,
548 text: /^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
549};
550
551inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
552
553inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
554inline._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])?)+(?![-_])/;
555inline.autolink = edit(inline.autolink)
556 .replace('scheme', inline._scheme)
557 .replace('email', inline._email)
558 .getRegex();
559
560inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
561
562inline.tag = edit(inline.tag)
563 .replace('comment', block._comment)
564 .replace('attribute', inline._attribute)
565 .getRegex();
566
567inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
568inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
569inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
570
571inline.link = edit(inline.link)
572 .replace('label', inline._label)
573 .replace('href', inline._href)
574 .replace('title', inline._title)
575 .getRegex();
576
577inline.reflink = edit(inline.reflink)
578 .replace('label', inline._label)
579 .getRegex();
580
581/**
582 * Normal Inline Grammar
583 */
584
585inline.normal = merge({}, inline);
586
587/**
588 * Pedantic Inline Grammar
589 */
590
591inline.pedantic = merge({}, inline.normal, {
592 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
593 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
594 link: edit(/^!?\[(label)\]\((.*?)\)/)
595 .replace('label', inline._label)
596 .getRegex(),
597 reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
598 .replace('label', inline._label)
599 .getRegex()
600});
601
602/**
603 * GFM Inline Grammar
604 */
605
606inline.gfm = merge({}, inline.normal, {
607 escape: edit(inline.escape).replace('])', '~|])').getRegex(),
608 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
609 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
610 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
611 del: /^~+(?=\S)([\s\S]*?\S)~+/,
612 text: edit(inline.text)
613 .replace(']|', '~]|')
614 .replace('|$', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|$')
615 .getRegex()
616});
617
618inline.gfm.url = edit(inline.gfm.url)
619 .replace('email', inline.gfm._extended_email)
620 .getRegex();
621/**
622 * GFM + Line Breaks Inline Grammar
623 */
624
625inline.breaks = merge({}, inline.gfm, {
626 br: edit(inline.br).replace('{2,}', '*').getRegex(),
627 text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
628});
629
630/**
631 * Inline Lexer & Compiler
632 */
633
634function InlineLexer(links, options) {
635 this.options = options || marked.defaults;
636 this.links = links;
637 this.rules = inline.normal;
638 this.renderer = this.options.renderer || new Renderer();
639 this.renderer.options = this.options;
640
641 if (!this.links) {
642 throw new Error('Tokens array requires a `links` property.');
643 }
644
645 if (this.options.pedantic) {
646 this.rules = inline.pedantic;
647 } else if (this.options.gfm) {
648 if (this.options.breaks) {
649 this.rules = inline.breaks;
650 } else {
651 this.rules = inline.gfm;
652 }
653 }
654}
655
656/**
657 * Expose Inline Rules
658 */
659
660InlineLexer.rules = inline;
661
662/**
663 * Static Lexing/Compiling Method
664 */
665
666InlineLexer.output = function(src, links, options) {
667 var inline = new InlineLexer(links, options);
668 return inline.output(src);
669};
670
671/**
672 * Lexing/Compiling
673 */
674
675InlineLexer.prototype.output = function(src) {
676 var out = '',
677 link,
678 text,
679 href,
680 title,
681 cap,
682 prevCapZero;
683
684 while (src) {
685 // escape
686 if (cap = this.rules.escape.exec(src)) {
687 src = src.substring(cap[0].length);
688 out += cap[1];
689 continue;
690 }
691
692 // autolink
693 if (cap = this.rules.autolink.exec(src)) {
694 src = src.substring(cap[0].length);
695 if (cap[2] === '@') {
696 text = escape(this.mangle(cap[1]));
697 href = 'mailto:' + text;
698 } else {
699 text = escape(cap[1]);
700 href = text;
701 }
702 out += this.renderer.link(href, null, text);
703 continue;
704 }
705
706 // url (gfm)
707 if (!this.inLink && (cap = this.rules.url.exec(src))) {
708 if (cap[2] === '@') {
709 text = escape(cap[0]);
710 href = 'mailto:' + text;
711 } else {
712 // do extended autolink path validation
713 do {
714 prevCapZero = cap[0];
715 cap[0] = this.rules._backpedal.exec(cap[0])[0];
716 } while (prevCapZero !== cap[0]);
717 text = escape(cap[0]);
718 if (cap[1] === 'www.') {
719 href = 'http://' + text;
720 } else {
721 href = text;
722 }
723 }
724 src = src.substring(cap[0].length);
725 out += this.renderer.link(href, null, text);
726 continue;
727 }
728
729 // tag
730 if (cap = this.rules.tag.exec(src)) {
731 if (!this.inLink && /^<a /i.test(cap[0])) {
732 this.inLink = true;
733 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
734 this.inLink = false;
735 }
736 if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
737 this.inRawBlock = true;
738 } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
739 this.inRawBlock = false;
740 }
741
742 src = src.substring(cap[0].length);
743 out += this.options.sanitize
744 ? this.options.sanitizer
745 ? this.options.sanitizer(cap[0])
746 : escape(cap[0])
747 : cap[0];
748 continue;
749 }
750
751 // link
752 if (cap = this.rules.link.exec(src)) {
753 src = src.substring(cap[0].length);
754 this.inLink = true;
755 href = cap[2];
756 if (this.options.pedantic) {
757 link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
758
759 if (link) {
760 href = link[1];
761 title = link[3];
762 } else {
763 title = '';
764 }
765 } else {
766 title = cap[3] ? cap[3].slice(1, -1) : '';
767 }
768 href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
769 out += this.outputLink(cap, {
770 href: InlineLexer.escapes(href),
771 title: InlineLexer.escapes(title)
772 });
773 this.inLink = false;
774 continue;
775 }
776
777 // reflink, nolink
778 if ((cap = this.rules.reflink.exec(src))
779 || (cap = this.rules.nolink.exec(src))) {
780 src = src.substring(cap[0].length);
781 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
782 link = this.links[link.toLowerCase()];
783 if (!link || !link.href) {
784 out += cap[0].charAt(0);
785 src = cap[0].substring(1) + src;
786 continue;
787 }
788 this.inLink = true;
789 out += this.outputLink(cap, link);
790 this.inLink = false;
791 continue;
792 }
793
794 // strong
795 if (cap = this.rules.strong.exec(src)) {
796 src = src.substring(cap[0].length);
797 out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
798 continue;
799 }
800
801 // em
802 if (cap = this.rules.em.exec(src)) {
803 src = src.substring(cap[0].length);
804 out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
805 continue;
806 }
807
808 // code
809 if (cap = this.rules.code.exec(src)) {
810 src = src.substring(cap[0].length);
811 out += this.renderer.codespan(escape(cap[2].trim(), true));
812 continue;
813 }
814
815 // br
816 if (cap = this.rules.br.exec(src)) {
817 src = src.substring(cap[0].length);
818 out += this.renderer.br();
819 continue;
820 }
821
822 // del (gfm)
823 if (cap = this.rules.del.exec(src)) {
824 src = src.substring(cap[0].length);
825 out += this.renderer.del(this.output(cap[1]));
826 continue;
827 }
828
829 // text
830 if (cap = this.rules.text.exec(src)) {
831 src = src.substring(cap[0].length);
832 if (this.inRawBlock) {
833 out += this.renderer.text(cap[0]);
834 } else {
835 out += this.renderer.text(escape(this.smartypants(cap[0])));
836 }
837 continue;
838 }
839
840 if (src) {
841 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
842 }
843 }
844
845 return out;
846};
847
848InlineLexer.escapes = function(text) {
849 return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
850};
851
852/**
853 * Compile Link
854 */
855
856InlineLexer.prototype.outputLink = function(cap, link) {
857 var href = link.href,
858 title = link.title ? escape(link.title) : null;
859
860 return cap[0].charAt(0) !== '!'
861 ? this.renderer.link(href, title, this.output(cap[1]))
862 : this.renderer.image(href, title, escape(cap[1]));
863};
864
865/**
866 * Smartypants Transformations
867 */
868
869InlineLexer.prototype.smartypants = function(text) {
870 if (!this.options.smartypants) return text;
871 return text
872 // em-dashes
873 .replace(/---/g, '\u2014')
874 // en-dashes
875 .replace(/--/g, '\u2013')
876 // opening singles
877 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
878 // closing singles & apostrophes
879 .replace(/'/g, '\u2019')
880 // opening doubles
881 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
882 // closing doubles
883 .replace(/"/g, '\u201d')
884 // ellipses
885 .replace(/\.{3}/g, '\u2026');
886};
887
888/**
889 * Mangle Links
890 */
891
892InlineLexer.prototype.mangle = function(text) {
893 if (!this.options.mangle) return text;
894 var out = '',
895 l = text.length,
896 i = 0,
897 ch;
898
899 for (; i < l; i++) {
900 ch = text.charCodeAt(i);
901 if (Math.random() > 0.5) {
902 ch = 'x' + ch.toString(16);
903 }
904 out += '&#' + ch + ';';
905 }
906
907 return out;
908};
909
910/**
911 * Renderer
912 */
913
914function Renderer(options) {
915 this.options = options || marked.defaults;
916}
917
918Renderer.prototype.code = function(code, lang, escaped) {
919 if (this.options.highlight) {
920 var out = this.options.highlight(code, lang);
921 if (out != null && out !== code) {
922 escaped = true;
923 code = out;
924 }
925 }
926
927 if (!lang) {
928 return '<pre><code>'
929 + (escaped ? code : escape(code, true))
930 + '</code></pre>';
931 }
932
933 return '<pre><code class="'
934 + this.options.langPrefix
935 + escape(lang, true)
936 + '">'
937 + (escaped ? code : escape(code, true))
938 + '</code></pre>\n';
939};
940
941Renderer.prototype.blockquote = function(quote) {
942 return '<blockquote>\n' + quote + '</blockquote>\n';
943};
944
945Renderer.prototype.html = function(html) {
946 return html;
947};
948
949Renderer.prototype.heading = function(text, level, raw) {
950 if (this.options.headerIds) {
951 return '<h'
952 + level
953 + ' id="'
954 + this.options.headerPrefix
955 + raw.toLowerCase().replace(/[^\w]+/g, '-')
956 + '">'
957 + text
958 + '</h'
959 + level
960 + '>\n';
961 }
962 // ignore IDs
963 return '<h' + level + '>' + text + '</h' + level + '>\n';
964};
965
966Renderer.prototype.hr = function() {
967 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
968};
969
970Renderer.prototype.list = function(body, ordered, start) {
971 var type = ordered ? 'ol' : 'ul',
972 startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
973 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
974};
975
976Renderer.prototype.listitem = function(text) {
977 return '<li>' + text + '</li>\n';
978};
979
980Renderer.prototype.checkbox = function(checked) {
981 return '<input '
982 + (checked ? 'checked="" ' : '')
983 + 'disabled="" type="checkbox"'
984 + (this.options.xhtml ? ' /' : '')
985 + '> ';
986};
987
988Renderer.prototype.paragraph = function(text) {
989 return '<p>' + text + '</p>\n';
990};
991
992Renderer.prototype.table = function(header, body) {
993 if (body) body = '<tbody>' + body + '</tbody>';
994
995 return '<table>\n'
996 + '<thead>\n'
997 + header
998 + '</thead>\n'
999 + body
1000 + '</table>\n';
1001};
1002
1003Renderer.prototype.tablerow = function(content) {
1004 return '<tr>\n' + content + '</tr>\n';
1005};
1006
1007Renderer.prototype.tablecell = function(content, flags) {
1008 var type = flags.header ? 'th' : 'td';
1009 var tag = flags.align
1010 ? '<' + type + ' align="' + flags.align + '">'
1011 : '<' + type + '>';
1012 return tag + content + '</' + type + '>\n';
1013};
1014
1015// span level renderer
1016Renderer.prototype.strong = function(text) {
1017 return '<strong>' + text + '</strong>';
1018};
1019
1020Renderer.prototype.em = function(text) {
1021 return '<em>' + text + '</em>';
1022};
1023
1024Renderer.prototype.codespan = function(text) {
1025 return '<code>' + text + '</code>';
1026};
1027
1028Renderer.prototype.br = function() {
1029 return this.options.xhtml ? '<br/>' : '<br>';
1030};
1031
1032Renderer.prototype.del = function(text) {
1033 return '<del>' + text + '</del>';
1034};
1035
1036Renderer.prototype.link = function(href, title, text) {
1037 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1038 if (href === null) {
1039 return text;
1040 }
1041 var out = '<a href="' + escape(href) + '"';
1042 if (title) {
1043 out += ' title="' + title + '"';
1044 }
1045 out += '>' + text + '</a>';
1046 return out;
1047};
1048
1049Renderer.prototype.image = function(href, title, text) {
1050 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1051 if (href === null) {
1052 return text;
1053 }
1054
1055 var out = '<img src="' + href + '" alt="' + text + '"';
1056 if (title) {
1057 out += ' title="' + title + '"';
1058 }
1059 out += this.options.xhtml ? '/>' : '>';
1060 return out;
1061};
1062
1063Renderer.prototype.text = function(text) {
1064 return text;
1065};
1066
1067/**
1068 * TextRenderer
1069 * returns only the textual part of the token
1070 */
1071
1072function TextRenderer() {}
1073
1074// no need for block level renderers
1075
1076TextRenderer.prototype.strong =
1077TextRenderer.prototype.em =
1078TextRenderer.prototype.codespan =
1079TextRenderer.prototype.del =
1080TextRenderer.prototype.text = function (text) {
1081 return text;
1082};
1083
1084TextRenderer.prototype.link =
1085TextRenderer.prototype.image = function(href, title, text) {
1086 return '' + text;
1087};
1088
1089TextRenderer.prototype.br = function() {
1090 return '';
1091};
1092
1093/**
1094 * Parsing & Compiling
1095 */
1096
1097function Parser(options) {
1098 this.tokens = [];
1099 this.token = null;
1100 this.options = options || marked.defaults;
1101 this.options.renderer = this.options.renderer || new Renderer();
1102 this.renderer = this.options.renderer;
1103 this.renderer.options = this.options;
1104}
1105
1106/**
1107 * Static Parse Method
1108 */
1109
1110Parser.parse = function(src, options) {
1111 var parser = new Parser(options);
1112 return parser.parse(src);
1113};
1114
1115/**
1116 * Parse Loop
1117 */
1118
1119Parser.prototype.parse = function(src) {
1120 this.inline = new InlineLexer(src.links, this.options);
1121 // use an InlineLexer with a TextRenderer to extract pure text
1122 this.inlineText = new InlineLexer(
1123 src.links,
1124 merge({}, this.options, {renderer: new TextRenderer()})
1125 );
1126 this.tokens = src.reverse();
1127
1128 var out = '';
1129 while (this.next()) {
1130 out += this.tok();
1131 }
1132
1133 return out;
1134};
1135
1136/**
1137 * Next Token
1138 */
1139
1140Parser.prototype.next = function() {
1141 return this.token = this.tokens.pop();
1142};
1143
1144/**
1145 * Preview Next Token
1146 */
1147
1148Parser.prototype.peek = function() {
1149 return this.tokens[this.tokens.length - 1] || 0;
1150};
1151
1152/**
1153 * Parse Text Tokens
1154 */
1155
1156Parser.prototype.parseText = function() {
1157 var body = this.token.text;
1158
1159 while (this.peek().type === 'text') {
1160 body += '\n' + this.next().text;
1161 }
1162
1163 return this.inline.output(body);
1164};
1165
1166/**
1167 * Parse Current Token
1168 */
1169
1170Parser.prototype.tok = function() {
1171 switch (this.token.type) {
1172 case 'space': {
1173 return '';
1174 }
1175 case 'hr': {
1176 return this.renderer.hr();
1177 }
1178 case 'heading': {
1179 return this.renderer.heading(
1180 this.inline.output(this.token.text),
1181 this.token.depth,
1182 unescape(this.inlineText.output(this.token.text)));
1183 }
1184 case 'code': {
1185 return this.renderer.code(this.token.text,
1186 this.token.lang,
1187 this.token.escaped);
1188 }
1189 case 'table': {
1190 var header = '',
1191 body = '',
1192 i,
1193 row,
1194 cell,
1195 j;
1196
1197 // header
1198 cell = '';
1199 for (i = 0; i < this.token.header.length; i++) {
1200 cell += this.renderer.tablecell(
1201 this.inline.output(this.token.header[i]),
1202 { header: true, align: this.token.align[i] }
1203 );
1204 }
1205 header += this.renderer.tablerow(cell);
1206
1207 for (i = 0; i < this.token.cells.length; i++) {
1208 row = this.token.cells[i];
1209
1210 cell = '';
1211 for (j = 0; j < row.length; j++) {
1212 cell += this.renderer.tablecell(
1213 this.inline.output(row[j]),
1214 { header: false, align: this.token.align[j] }
1215 );
1216 }
1217
1218 body += this.renderer.tablerow(cell);
1219 }
1220 return this.renderer.table(header, body);
1221 }
1222 case 'blockquote_start': {
1223 body = '';
1224
1225 while (this.next().type !== 'blockquote_end') {
1226 body += this.tok();
1227 }
1228
1229 return this.renderer.blockquote(body);
1230 }
1231 case 'list_start': {
1232 body = '';
1233 var ordered = this.token.ordered,
1234 start = this.token.start;
1235
1236 while (this.next().type !== 'list_end') {
1237 body += this.tok();
1238 }
1239
1240 return this.renderer.list(body, ordered, start);
1241 }
1242 case 'list_item_start': {
1243 body = '';
1244 var loose = this.token.loose;
1245
1246 if (this.token.task) {
1247 body += this.renderer.checkbox(this.token.checked);
1248 }
1249
1250 while (this.next().type !== 'list_item_end') {
1251 body += !loose && this.token.type === 'text'
1252 ? this.parseText()
1253 : this.tok();
1254 }
1255
1256 return this.renderer.listitem(body);
1257 }
1258 case 'html': {
1259 // TODO parse inline content if parameter markdown=1
1260 return this.renderer.html(this.token.text);
1261 }
1262 case 'paragraph': {
1263 return this.renderer.paragraph(this.inline.output(this.token.text));
1264 }
1265 case 'text': {
1266 return this.renderer.paragraph(this.parseText());
1267 }
1268 }
1269};
1270
1271/**
1272 * Helpers
1273 */
1274
1275function escape(html, encode) {
1276 if (encode) {
1277 if (escape.escapeTest.test(html)) {
1278 return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; });
1279 }
1280 } else {
1281 if (escape.escapeTestNoEncode.test(html)) {
1282 return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; });
1283 }
1284 }
1285
1286 return html;
1287}
1288
1289escape.escapeTest = /[&<>"']/;
1290escape.escapeReplace = /[&<>"']/g;
1291escape.replacements = {
1292 '&': '&amp;',
1293 '<': '&lt;',
1294 '>': '&gt;',
1295 '"': '&quot;',
1296 "'": '&#39;'
1297};
1298
1299escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
1300escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
1301
1302function unescape(html) {
1303 // explicitly match decimal, hex, and named HTML entities
1304 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
1305 n = n.toLowerCase();
1306 if (n === 'colon') return ':';
1307 if (n.charAt(0) === '#') {
1308 return n.charAt(1) === 'x'
1309 ? String.fromCharCode(parseInt(n.substring(2), 16))
1310 : String.fromCharCode(+n.substring(1));
1311 }
1312 return '';
1313 });
1314}
1315
1316function edit(regex, opt) {
1317 regex = regex.source || regex;
1318 opt = opt || '';
1319 return {
1320 replace: function(name, val) {
1321 val = val.source || val;
1322 val = val.replace(/(^|[^\[])\^/g, '$1');
1323 regex = regex.replace(name, val);
1324 return this;
1325 },
1326 getRegex: function() {
1327 return new RegExp(regex, opt);
1328 }
1329 };
1330}
1331
1332function cleanUrl(sanitize, base, href) {
1333 if (sanitize) {
1334 try {
1335 var prot = decodeURIComponent(unescape(href))
1336 .replace(/[^\w:]/g, '')
1337 .toLowerCase();
1338 } catch (e) {
1339 return null;
1340 }
1341 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
1342 return null;
1343 }
1344 }
1345 if (base && !originIndependentUrl.test(href)) {
1346 href = resolveUrl(base, href);
1347 }
1348 try {
1349 href = encodeURI(href).replace(/%25/g, '%');
1350 } catch (e) {
1351 return null;
1352 }
1353 return href;
1354}
1355
1356function resolveUrl(base, href) {
1357 if (!baseUrls[' ' + base]) {
1358 // we can ignore everything in base after the last slash of its path component,
1359 // but we might need to add _that_
1360 // https://tools.ietf.org/html/rfc3986#section-3
1361 if (/^[^:]+:\/*[^/]*$/.test(base)) {
1362 baseUrls[' ' + base] = base + '/';
1363 } else {
1364 baseUrls[' ' + base] = rtrim(base, '/', true);
1365 }
1366 }
1367 base = baseUrls[' ' + base];
1368
1369 if (href.slice(0, 2) === '//') {
1370 return base.replace(/:[\s\S]*/, ':') + href;
1371 } else if (href.charAt(0) === '/') {
1372 return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
1373 } else {
1374 return base + href;
1375 }
1376}
1377var baseUrls = {};
1378var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
1379
1380function noop() {}
1381noop.exec = noop;
1382
1383function merge(obj) {
1384 var i = 1,
1385 target,
1386 key;
1387
1388 for (; i < arguments.length; i++) {
1389 target = arguments[i];
1390 for (key in target) {
1391 if (Object.prototype.hasOwnProperty.call(target, key)) {
1392 obj[key] = target[key];
1393 }
1394 }
1395 }
1396
1397 return obj;
1398}
1399
1400function splitCells(tableRow, count) {
1401 // ensure that every cell-delimiting pipe has a space
1402 // before it to distinguish it from an escaped pipe
1403 var row = tableRow.replace(/\|/g, function (match, offset, str) {
1404 var escaped = false,
1405 curr = offset;
1406 while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
1407 if (escaped) {
1408 // odd number of slashes means | is escaped
1409 // so we leave it alone
1410 return '|';
1411 } else {
1412 // add space before unescaped |
1413 return ' |';
1414 }
1415 }),
1416 cells = row.split(/ \|/),
1417 i = 0;
1418
1419 if (cells.length > count) {
1420 cells.splice(count);
1421 } else {
1422 while (cells.length < count) cells.push('');
1423 }
1424
1425 for (; i < cells.length; i++) {
1426 // leading or trailing whitespace is ignored per the gfm spec
1427 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
1428 }
1429 return cells;
1430}
1431
1432// Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
1433// /c*$/ is vulnerable to REDOS.
1434// invert: Remove suffix of non-c chars instead. Default falsey.
1435function rtrim(str, c, invert) {
1436 if (str.length === 0) {
1437 return '';
1438 }
1439
1440 // Length of suffix matching the invert condition.
1441 var suffLen = 0;
1442
1443 // Step left until we fail to match the invert condition.
1444 while (suffLen < str.length) {
1445 var currChar = str.charAt(str.length - suffLen - 1);
1446 if (currChar === c && !invert) {
1447 suffLen++;
1448 } else if (currChar !== c && invert) {
1449 suffLen++;
1450 } else {
1451 break;
1452 }
1453 }
1454
1455 return str.substr(0, str.length - suffLen);
1456}
1457
1458/**
1459 * Marked
1460 */
1461
1462function marked(src, opt, callback) {
1463 // throw error in case of non string input
1464 if (typeof src === 'undefined' || src === null) {
1465 throw new Error('marked(): input parameter is undefined or null');
1466 }
1467 if (typeof src !== 'string') {
1468 throw new Error('marked(): input parameter is of type '
1469 + Object.prototype.toString.call(src) + ', string expected');
1470 }
1471
1472 if (callback || typeof opt === 'function') {
1473 if (!callback) {
1474 callback = opt;
1475 opt = null;
1476 }
1477
1478 opt = merge({}, marked.defaults, opt || {});
1479
1480 var highlight = opt.highlight,
1481 tokens,
1482 pending,
1483 i = 0;
1484
1485 try {
1486 tokens = Lexer.lex(src, opt);
1487 } catch (e) {
1488 return callback(e);
1489 }
1490
1491 pending = tokens.length;
1492
1493 var done = function(err) {
1494 if (err) {
1495 opt.highlight = highlight;
1496 return callback(err);
1497 }
1498
1499 var out;
1500
1501 try {
1502 out = Parser.parse(tokens, opt);
1503 } catch (e) {
1504 err = e;
1505 }
1506
1507 opt.highlight = highlight;
1508
1509 return err
1510 ? callback(err)
1511 : callback(null, out);
1512 };
1513
1514 if (!highlight || highlight.length < 3) {
1515 return done();
1516 }
1517
1518 delete opt.highlight;
1519
1520 if (!pending) return done();
1521
1522 for (; i < tokens.length; i++) {
1523 (function(token) {
1524 if (token.type !== 'code') {
1525 return --pending || done();
1526 }
1527 return highlight(token.text, token.lang, function(err, code) {
1528 if (err) return done(err);
1529 if (code == null || code === token.text) {
1530 return --pending || done();
1531 }
1532 token.text = code;
1533 token.escaped = true;
1534 --pending || done();
1535 });
1536 })(tokens[i]);
1537 }
1538
1539 return;
1540 }
1541 try {
1542 if (opt) opt = merge({}, marked.defaults, opt);
1543 return Parser.parse(Lexer.lex(src, opt), opt);
1544 } catch (e) {
1545 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
1546 if ((opt || marked.defaults).silent) {
1547 return '<p>An error occurred:</p><pre>'
1548 + escape(e.message + '', true)
1549 + '</pre>';
1550 }
1551 throw e;
1552 }
1553}
1554
1555/**
1556 * Options
1557 */
1558
1559marked.options =
1560marked.setOptions = function(opt) {
1561 merge(marked.defaults, opt);
1562 return marked;
1563};
1564
1565marked.getDefaults = function () {
1566 return {
1567 baseUrl: null,
1568 breaks: false,
1569 gfm: true,
1570 headerIds: true,
1571 headerPrefix: '',
1572 highlight: null,
1573 langPrefix: 'language-',
1574 mangle: true,
1575 pedantic: false,
1576 renderer: new Renderer(),
1577 sanitize: false,
1578 sanitizer: null,
1579 silent: false,
1580 smartLists: false,
1581 smartypants: false,
1582 tables: true,
1583 xhtml: false
1584 };
1585};
1586
1587marked.defaults = marked.getDefaults();
1588
1589/**
1590 * Expose
1591 */
1592
1593marked.Parser = Parser;
1594marked.parser = Parser.parse;
1595
1596marked.Renderer = Renderer;
1597marked.TextRenderer = TextRenderer;
1598
1599marked.Lexer = Lexer;
1600marked.lexer = Lexer.lex;
1601
1602marked.InlineLexer = InlineLexer;
1603marked.inlineLexer = InlineLexer.output;
1604
1605marked.parse = marked;
1606
1607if (typeof module !== 'undefined' && typeof exports === 'object') {
1608 module.exports = marked;
1609} else if (typeof define === 'function' && define.amd) {
1610 define(function() { return marked; });
1611} else {
1612 root.marked = marked;
1613}
1614})(this || (typeof window !== 'undefined' ? window : global));