UNPKG

41.2 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: /^( {0,3})(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{1,9}\.)/;
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: /^ {0,3}(`{3,}|~{3,})([^`\n]*)\n(?:|([\s\S]*?)\n)(?: {0,3}\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] ? cap[2].trim() : 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 (i !== l - 1) {
359 b = block.bullet.exec(cap[i + 1])[0];
360 if (bull.length > 1 ? b.length === 1
361 : (b.length > 1 || (this.options.smartLists && b !== bull))) {
362 src = cap.slice(i + 1).join('\n') + src;
363 i = l - 1;
364 }
365 }
366
367 // Determine whether item is loose or not.
368 // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
369 // for discount behavior.
370 loose = next || /\n\n(?!\s*$)/.test(item);
371 if (i !== l - 1) {
372 next = item.charAt(item.length - 1) === '\n';
373 if (!loose) loose = next;
374 }
375
376 if (loose) {
377 listStart.loose = true;
378 }
379
380 // Check for task list items
381 istask = /^\[[ xX]\] /.test(item);
382 ischecked = undefined;
383 if (istask) {
384 ischecked = item[1] !== ' ';
385 item = item.replace(/^\[[ xX]\] +/, '');
386 }
387
388 t = {
389 type: 'list_item_start',
390 task: istask,
391 checked: ischecked,
392 loose: loose
393 };
394
395 listItems.push(t);
396 this.tokens.push(t);
397
398 // Recurse.
399 this.token(item, false);
400
401 this.tokens.push({
402 type: 'list_item_end'
403 });
404 }
405
406 if (listStart.loose) {
407 l = listItems.length;
408 i = 0;
409 for (; i < l; i++) {
410 listItems[i].loose = true;
411 }
412 }
413
414 this.tokens.push({
415 type: 'list_end'
416 });
417
418 continue;
419 }
420
421 // html
422 if (cap = this.rules.html.exec(src)) {
423 src = src.substring(cap[0].length);
424 this.tokens.push({
425 type: this.options.sanitize
426 ? 'paragraph'
427 : 'html',
428 pre: !this.options.sanitizer
429 && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
430 text: cap[0]
431 });
432 continue;
433 }
434
435 // def
436 if (top && (cap = this.rules.def.exec(src))) {
437 src = src.substring(cap[0].length);
438 if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
439 tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
440 if (!this.tokens.links[tag]) {
441 this.tokens.links[tag] = {
442 href: cap[2],
443 title: cap[3]
444 };
445 }
446 continue;
447 }
448
449 // table (gfm)
450 if (top && (cap = this.rules.table.exec(src))) {
451 item = {
452 type: 'table',
453 header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
454 align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
455 cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
456 };
457
458 if (item.header.length === item.align.length) {
459 src = src.substring(cap[0].length);
460
461 for (i = 0; i < item.align.length; i++) {
462 if (/^ *-+: *$/.test(item.align[i])) {
463 item.align[i] = 'right';
464 } else if (/^ *:-+: *$/.test(item.align[i])) {
465 item.align[i] = 'center';
466 } else if (/^ *:-+ *$/.test(item.align[i])) {
467 item.align[i] = 'left';
468 } else {
469 item.align[i] = null;
470 }
471 }
472
473 for (i = 0; i < item.cells.length; i++) {
474 item.cells[i] = splitCells(
475 item.cells[i].replace(/^ *\| *| *\| *$/g, ''),
476 item.header.length);
477 }
478
479 this.tokens.push(item);
480
481 continue;
482 }
483 }
484
485 // lheading
486 if (cap = this.rules.lheading.exec(src)) {
487 src = src.substring(cap[0].length);
488 this.tokens.push({
489 type: 'heading',
490 depth: cap[2] === '=' ? 1 : 2,
491 text: cap[1]
492 });
493 continue;
494 }
495
496 // top-level paragraph
497 if (top && (cap = this.rules.paragraph.exec(src))) {
498 src = src.substring(cap[0].length);
499 this.tokens.push({
500 type: 'paragraph',
501 text: cap[1].charAt(cap[1].length - 1) === '\n'
502 ? cap[1].slice(0, -1)
503 : cap[1]
504 });
505 continue;
506 }
507
508 // text
509 if (cap = this.rules.text.exec(src)) {
510 // Top-level should never reach here.
511 src = src.substring(cap[0].length);
512 this.tokens.push({
513 type: 'text',
514 text: cap[0]
515 });
516 continue;
517 }
518
519 if (src) {
520 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
521 }
522 }
523
524 return this.tokens;
525};
526
527/**
528 * Inline-Level Grammar
529 */
530
531var inline = {
532 escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
533 autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
534 url: noop,
535 tag: '^comment'
536 + '|^</[a-zA-Z][\\w:-]*\\s*>' // self-closing tag
537 + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag
538 + '|^<\\?[\\s\\S]*?\\?>' // processing instruction, e.g. <?php ?>
539 + '|^<![a-zA-Z]+\\s[\\s\\S]*?>' // declaration, e.g. <!DOCTYPE html>
540 + '|^<!\\[CDATA\\[[\\s\\S]*?\\]\\]>', // CDATA section
541 link: /^!?\[(label)\]\(href(?:\s+(title))?\s*\)/,
542 reflink: /^!?\[(label)\]\[(?!\s*\])((?:\\[\[\]]?|[^\[\]\\])+)\]/,
543 nolink: /^!?\[(?!\s*\])((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\](?:\[\])?/,
544 strong: /^__([^\s_])__(?!_)|^\*\*([^\s*])\*\*(?!\*)|^__([^\s][\s\S]*?[^\s])__(?!_)|^\*\*([^\s][\s\S]*?[^\s])\*\*(?!\*)/,
545 em: /^_([^\s_])_(?!_)|^\*([^\s*"<\[])\*(?!\*)|^_([^\s][\s\S]*?[^\s_])_(?!_|[^\spunctuation])|^_([^\s_][\s\S]*?[^\s])_(?!_|[^\spunctuation])|^\*([^\s"<\[][\s\S]*?[^\s*])\*(?!\*)|^\*([^\s*"<\[][\s\S]*?[^\s])\*(?!\*)/,
546 code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
547 br: /^( {2,}|\\)\n(?!\s*$)/,
548 del: noop,
549 text: /^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
550};
551
552// list of punctuation marks from common mark spec
553// without ` and ] to workaround Rule 17 (inline code blocks/links)
554inline._punctuation = '!"#$%&\'()*+,\\-./:;<=>?@\\[^_{|}~';
555inline.em = edit(inline.em).replace(/punctuation/g, inline._punctuation).getRegex();
556
557inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
558
559inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
560inline._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])?)+(?![-_])/;
561inline.autolink = edit(inline.autolink)
562 .replace('scheme', inline._scheme)
563 .replace('email', inline._email)
564 .getRegex();
565
566inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
567
568inline.tag = edit(inline.tag)
569 .replace('comment', block._comment)
570 .replace('attribute', inline._attribute)
571 .getRegex();
572
573inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
574inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
575inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
576
577inline.link = edit(inline.link)
578 .replace('label', inline._label)
579 .replace('href', inline._href)
580 .replace('title', inline._title)
581 .getRegex();
582
583inline.reflink = edit(inline.reflink)
584 .replace('label', inline._label)
585 .getRegex();
586
587/**
588 * Normal Inline Grammar
589 */
590
591inline.normal = merge({}, inline);
592
593/**
594 * Pedantic Inline Grammar
595 */
596
597inline.pedantic = merge({}, inline.normal, {
598 strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
599 em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/,
600 link: edit(/^!?\[(label)\]\((.*?)\)/)
601 .replace('label', inline._label)
602 .getRegex(),
603 reflink: edit(/^!?\[(label)\]\s*\[([^\]]*)\]/)
604 .replace('label', inline._label)
605 .getRegex()
606});
607
608/**
609 * GFM Inline Grammar
610 */
611
612inline.gfm = merge({}, inline.normal, {
613 escape: edit(inline.escape).replace('])', '~|])').getRegex(),
614 _extended_email: /[A-Za-z0-9._+-]+(@)[a-zA-Z0-9-_]+(?:\.[a-zA-Z0-9-_]*[a-zA-Z0-9])+(?![-_])/,
615 url: /^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/,
616 _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
617 del: /^~+(?=\S)([\s\S]*?\S)~+/,
618 text: edit(inline.text)
619 .replace(']|', '~]|')
620 .replace('|$', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|$')
621 .getRegex()
622});
623
624inline.gfm.url = edit(inline.gfm.url, 'i')
625 .replace('email', inline.gfm._extended_email)
626 .getRegex();
627/**
628 * GFM + Line Breaks Inline Grammar
629 */
630
631inline.breaks = merge({}, inline.gfm, {
632 br: edit(inline.br).replace('{2,}', '*').getRegex(),
633 text: edit(inline.gfm.text).replace('{2,}', '*').getRegex()
634});
635
636/**
637 * Inline Lexer & Compiler
638 */
639
640function InlineLexer(links, options) {
641 this.options = options || marked.defaults;
642 this.links = links;
643 this.rules = inline.normal;
644 this.renderer = this.options.renderer || new Renderer();
645 this.renderer.options = this.options;
646
647 if (!this.links) {
648 throw new Error('Tokens array requires a `links` property.');
649 }
650
651 if (this.options.pedantic) {
652 this.rules = inline.pedantic;
653 } else if (this.options.gfm) {
654 if (this.options.breaks) {
655 this.rules = inline.breaks;
656 } else {
657 this.rules = inline.gfm;
658 }
659 }
660}
661
662/**
663 * Expose Inline Rules
664 */
665
666InlineLexer.rules = inline;
667
668/**
669 * Static Lexing/Compiling Method
670 */
671
672InlineLexer.output = function(src, links, options) {
673 var inline = new InlineLexer(links, options);
674 return inline.output(src);
675};
676
677/**
678 * Lexing/Compiling
679 */
680
681InlineLexer.prototype.output = function(src) {
682 var out = '',
683 link,
684 text,
685 href,
686 title,
687 cap,
688 prevCapZero;
689
690 while (src) {
691 // escape
692 if (cap = this.rules.escape.exec(src)) {
693 src = src.substring(cap[0].length);
694 out += escape(cap[1]);
695 continue;
696 }
697
698 // tag
699 if (cap = this.rules.tag.exec(src)) {
700 if (!this.inLink && /^<a /i.test(cap[0])) {
701 this.inLink = true;
702 } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
703 this.inLink = false;
704 }
705 if (!this.inRawBlock && /^<(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
706 this.inRawBlock = true;
707 } else if (this.inRawBlock && /^<\/(pre|code|kbd|script)(\s|>)/i.test(cap[0])) {
708 this.inRawBlock = false;
709 }
710
711 src = src.substring(cap[0].length);
712 out += this.options.sanitize
713 ? this.options.sanitizer
714 ? this.options.sanitizer(cap[0])
715 : escape(cap[0])
716 : cap[0];
717 continue;
718 }
719
720 // link
721 if (cap = this.rules.link.exec(src)) {
722 src = src.substring(cap[0].length);
723 this.inLink = true;
724 href = cap[2];
725 if (this.options.pedantic) {
726 link = /^([^'"]*[^\s])\s+(['"])(.*)\2/.exec(href);
727
728 if (link) {
729 href = link[1];
730 title = link[3];
731 } else {
732 title = '';
733 }
734 } else {
735 title = cap[3] ? cap[3].slice(1, -1) : '';
736 }
737 href = href.trim().replace(/^<([\s\S]*)>$/, '$1');
738 out += this.outputLink(cap, {
739 href: InlineLexer.escapes(href),
740 title: InlineLexer.escapes(title)
741 });
742 this.inLink = false;
743 continue;
744 }
745
746 // reflink, nolink
747 if ((cap = this.rules.reflink.exec(src))
748 || (cap = this.rules.nolink.exec(src))) {
749 src = src.substring(cap[0].length);
750 link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
751 link = this.links[link.toLowerCase()];
752 if (!link || !link.href) {
753 out += cap[0].charAt(0);
754 src = cap[0].substring(1) + src;
755 continue;
756 }
757 this.inLink = true;
758 out += this.outputLink(cap, link);
759 this.inLink = false;
760 continue;
761 }
762
763 // strong
764 if (cap = this.rules.strong.exec(src)) {
765 src = src.substring(cap[0].length);
766 out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
767 continue;
768 }
769
770 // em
771 if (cap = this.rules.em.exec(src)) {
772 src = src.substring(cap[0].length);
773 out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
774 continue;
775 }
776
777 // code
778 if (cap = this.rules.code.exec(src)) {
779 src = src.substring(cap[0].length);
780 out += this.renderer.codespan(escape(cap[2].trim(), true));
781 continue;
782 }
783
784 // br
785 if (cap = this.rules.br.exec(src)) {
786 src = src.substring(cap[0].length);
787 out += this.renderer.br();
788 continue;
789 }
790
791 // del (gfm)
792 if (cap = this.rules.del.exec(src)) {
793 src = src.substring(cap[0].length);
794 out += this.renderer.del(this.output(cap[1]));
795 continue;
796 }
797
798 // autolink
799 if (cap = this.rules.autolink.exec(src)) {
800 src = src.substring(cap[0].length);
801 if (cap[2] === '@') {
802 text = escape(this.mangle(cap[1]));
803 href = 'mailto:' + text;
804 } else {
805 text = escape(cap[1]);
806 href = text;
807 }
808 out += this.renderer.link(href, null, text);
809 continue;
810 }
811
812 // url (gfm)
813 if (!this.inLink && (cap = this.rules.url.exec(src))) {
814 if (cap[2] === '@') {
815 text = escape(cap[0]);
816 href = 'mailto:' + text;
817 } else {
818 // do extended autolink path validation
819 do {
820 prevCapZero = cap[0];
821 cap[0] = this.rules._backpedal.exec(cap[0])[0];
822 } while (prevCapZero !== cap[0]);
823 text = escape(cap[0]);
824 if (cap[1] === 'www.') {
825 href = 'http://' + text;
826 } else {
827 href = text;
828 }
829 }
830 src = src.substring(cap[0].length);
831 out += this.renderer.link(href, null, text);
832 continue;
833 }
834
835 // text
836 if (cap = this.rules.text.exec(src)) {
837 src = src.substring(cap[0].length);
838 if (this.inRawBlock) {
839 out += this.renderer.text(cap[0]);
840 } else {
841 out += this.renderer.text(escape(this.smartypants(cap[0])));
842 }
843 continue;
844 }
845
846 if (src) {
847 throw new Error('Infinite loop on byte: ' + src.charCodeAt(0));
848 }
849 }
850
851 return out;
852};
853
854InlineLexer.escapes = function(text) {
855 return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
856};
857
858/**
859 * Compile Link
860 */
861
862InlineLexer.prototype.outputLink = function(cap, link) {
863 var href = link.href,
864 title = link.title ? escape(link.title) : null;
865
866 return cap[0].charAt(0) !== '!'
867 ? this.renderer.link(href, title, this.output(cap[1]))
868 : this.renderer.image(href, title, escape(cap[1]));
869};
870
871/**
872 * Smartypants Transformations
873 */
874
875InlineLexer.prototype.smartypants = function(text) {
876 if (!this.options.smartypants) return text;
877 return text
878 // em-dashes
879 .replace(/---/g, '\u2014')
880 // en-dashes
881 .replace(/--/g, '\u2013')
882 // opening singles
883 .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
884 // closing singles & apostrophes
885 .replace(/'/g, '\u2019')
886 // opening doubles
887 .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
888 // closing doubles
889 .replace(/"/g, '\u201d')
890 // ellipses
891 .replace(/\.{3}/g, '\u2026');
892};
893
894/**
895 * Mangle Links
896 */
897
898InlineLexer.prototype.mangle = function(text) {
899 if (!this.options.mangle) return text;
900 var out = '',
901 l = text.length,
902 i = 0,
903 ch;
904
905 for (; i < l; i++) {
906 ch = text.charCodeAt(i);
907 if (Math.random() > 0.5) {
908 ch = 'x' + ch.toString(16);
909 }
910 out += '&#' + ch + ';';
911 }
912
913 return out;
914};
915
916/**
917 * Renderer
918 */
919
920function Renderer(options) {
921 this.options = options || marked.defaults;
922}
923
924Renderer.prototype.code = function(code, infostring, escaped) {
925 var lang = (infostring || '').match(/\S*/)[0];
926 if (this.options.highlight) {
927 var out = this.options.highlight(code, lang);
928 if (out != null && out !== code) {
929 escaped = true;
930 code = out;
931 }
932 }
933
934 if (!lang) {
935 return '<pre><code>'
936 + (escaped ? code : escape(code, true))
937 + '</code></pre>';
938 }
939
940 return '<pre><code class="'
941 + this.options.langPrefix
942 + escape(lang, true)
943 + '">'
944 + (escaped ? code : escape(code, true))
945 + '</code></pre>\n';
946};
947
948Renderer.prototype.blockquote = function(quote) {
949 return '<blockquote>\n' + quote + '</blockquote>\n';
950};
951
952Renderer.prototype.html = function(html) {
953 return html;
954};
955
956Renderer.prototype.heading = function(text, level, raw, slugger) {
957 if (this.options.headerIds) {
958 return '<h'
959 + level
960 + ' id="'
961 + this.options.headerPrefix
962 + slugger.slug(raw)
963 + '">'
964 + text
965 + '</h'
966 + level
967 + '>\n';
968 }
969 // ignore IDs
970 return '<h' + level + '>' + text + '</h' + level + '>\n';
971};
972
973Renderer.prototype.hr = function() {
974 return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
975};
976
977Renderer.prototype.list = function(body, ordered, start) {
978 var type = ordered ? 'ol' : 'ul',
979 startatt = (ordered && start !== 1) ? (' start="' + start + '"') : '';
980 return '<' + type + startatt + '>\n' + body + '</' + type + '>\n';
981};
982
983Renderer.prototype.listitem = function(text) {
984 return '<li>' + text + '</li>\n';
985};
986
987Renderer.prototype.checkbox = function(checked) {
988 return '<input '
989 + (checked ? 'checked="" ' : '')
990 + 'disabled="" type="checkbox"'
991 + (this.options.xhtml ? ' /' : '')
992 + '> ';
993};
994
995Renderer.prototype.paragraph = function(text) {
996 return '<p>' + text + '</p>\n';
997};
998
999Renderer.prototype.table = function(header, body) {
1000 if (body) body = '<tbody>' + body + '</tbody>';
1001
1002 return '<table>\n'
1003 + '<thead>\n'
1004 + header
1005 + '</thead>\n'
1006 + body
1007 + '</table>\n';
1008};
1009
1010Renderer.prototype.tablerow = function(content) {
1011 return '<tr>\n' + content + '</tr>\n';
1012};
1013
1014Renderer.prototype.tablecell = function(content, flags) {
1015 var type = flags.header ? 'th' : 'td';
1016 var tag = flags.align
1017 ? '<' + type + ' align="' + flags.align + '">'
1018 : '<' + type + '>';
1019 return tag + content + '</' + type + '>\n';
1020};
1021
1022// span level renderer
1023Renderer.prototype.strong = function(text) {
1024 return '<strong>' + text + '</strong>';
1025};
1026
1027Renderer.prototype.em = function(text) {
1028 return '<em>' + text + '</em>';
1029};
1030
1031Renderer.prototype.codespan = function(text) {
1032 return '<code>' + text + '</code>';
1033};
1034
1035Renderer.prototype.br = function() {
1036 return this.options.xhtml ? '<br/>' : '<br>';
1037};
1038
1039Renderer.prototype.del = function(text) {
1040 return '<del>' + text + '</del>';
1041};
1042
1043Renderer.prototype.link = function(href, title, text) {
1044 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1045 if (href === null) {
1046 return text;
1047 }
1048 var out = '<a href="' + escape(href) + '"';
1049 if (title) {
1050 out += ' title="' + title + '"';
1051 }
1052 out += '>' + text + '</a>';
1053 return out;
1054};
1055
1056Renderer.prototype.image = function(href, title, text) {
1057 href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1058 if (href === null) {
1059 return text;
1060 }
1061
1062 var out = '<img src="' + href + '" alt="' + text + '"';
1063 if (title) {
1064 out += ' title="' + title + '"';
1065 }
1066 out += this.options.xhtml ? '/>' : '>';
1067 return out;
1068};
1069
1070Renderer.prototype.text = function(text) {
1071 return text;
1072};
1073
1074/**
1075 * TextRenderer
1076 * returns only the textual part of the token
1077 */
1078
1079function TextRenderer() {}
1080
1081// no need for block level renderers
1082
1083TextRenderer.prototype.strong =
1084TextRenderer.prototype.em =
1085TextRenderer.prototype.codespan =
1086TextRenderer.prototype.del =
1087TextRenderer.prototype.text = function (text) {
1088 return text;
1089};
1090
1091TextRenderer.prototype.link =
1092TextRenderer.prototype.image = function(href, title, text) {
1093 return '' + text;
1094};
1095
1096TextRenderer.prototype.br = function() {
1097 return '';
1098};
1099
1100/**
1101 * Parsing & Compiling
1102 */
1103
1104function Parser(options) {
1105 this.tokens = [];
1106 this.token = null;
1107 this.options = options || marked.defaults;
1108 this.options.renderer = this.options.renderer || new Renderer();
1109 this.renderer = this.options.renderer;
1110 this.renderer.options = this.options;
1111 this.slugger = new Slugger();
1112}
1113
1114/**
1115 * Static Parse Method
1116 */
1117
1118Parser.parse = function(src, options) {
1119 var parser = new Parser(options);
1120 return parser.parse(src);
1121};
1122
1123/**
1124 * Parse Loop
1125 */
1126
1127Parser.prototype.parse = function(src) {
1128 this.inline = new InlineLexer(src.links, this.options);
1129 // use an InlineLexer with a TextRenderer to extract pure text
1130 this.inlineText = new InlineLexer(
1131 src.links,
1132 merge({}, this.options, {renderer: new TextRenderer()})
1133 );
1134 this.tokens = src.reverse();
1135
1136 var out = '';
1137 while (this.next()) {
1138 out += this.tok();
1139 }
1140
1141 return out;
1142};
1143
1144/**
1145 * Next Token
1146 */
1147
1148Parser.prototype.next = function() {
1149 return this.token = this.tokens.pop();
1150};
1151
1152/**
1153 * Preview Next Token
1154 */
1155
1156Parser.prototype.peek = function() {
1157 return this.tokens[this.tokens.length - 1] || 0;
1158};
1159
1160/**
1161 * Parse Text Tokens
1162 */
1163
1164Parser.prototype.parseText = function() {
1165 var body = this.token.text;
1166
1167 while (this.peek().type === 'text') {
1168 body += '\n' + this.next().text;
1169 }
1170
1171 return this.inline.output(body);
1172};
1173
1174/**
1175 * Parse Current Token
1176 */
1177
1178Parser.prototype.tok = function() {
1179 switch (this.token.type) {
1180 case 'space': {
1181 return '';
1182 }
1183 case 'hr': {
1184 return this.renderer.hr();
1185 }
1186 case 'heading': {
1187 return this.renderer.heading(
1188 this.inline.output(this.token.text),
1189 this.token.depth,
1190 unescape(this.inlineText.output(this.token.text)),
1191 this.slugger);
1192 }
1193 case 'code': {
1194 return this.renderer.code(this.token.text,
1195 this.token.lang,
1196 this.token.escaped);
1197 }
1198 case 'table': {
1199 var header = '',
1200 body = '',
1201 i,
1202 row,
1203 cell,
1204 j;
1205
1206 // header
1207 cell = '';
1208 for (i = 0; i < this.token.header.length; i++) {
1209 cell += this.renderer.tablecell(
1210 this.inline.output(this.token.header[i]),
1211 { header: true, align: this.token.align[i] }
1212 );
1213 }
1214 header += this.renderer.tablerow(cell);
1215
1216 for (i = 0; i < this.token.cells.length; i++) {
1217 row = this.token.cells[i];
1218
1219 cell = '';
1220 for (j = 0; j < row.length; j++) {
1221 cell += this.renderer.tablecell(
1222 this.inline.output(row[j]),
1223 { header: false, align: this.token.align[j] }
1224 );
1225 }
1226
1227 body += this.renderer.tablerow(cell);
1228 }
1229 return this.renderer.table(header, body);
1230 }
1231 case 'blockquote_start': {
1232 body = '';
1233
1234 while (this.next().type !== 'blockquote_end') {
1235 body += this.tok();
1236 }
1237
1238 return this.renderer.blockquote(body);
1239 }
1240 case 'list_start': {
1241 body = '';
1242 var ordered = this.token.ordered,
1243 start = this.token.start;
1244
1245 while (this.next().type !== 'list_end') {
1246 body += this.tok();
1247 }
1248
1249 return this.renderer.list(body, ordered, start);
1250 }
1251 case 'list_item_start': {
1252 body = '';
1253 var loose = this.token.loose;
1254
1255 if (this.token.task) {
1256 body += this.renderer.checkbox(this.token.checked);
1257 }
1258
1259 while (this.next().type !== 'list_item_end') {
1260 body += !loose && this.token.type === 'text'
1261 ? this.parseText()
1262 : this.tok();
1263 }
1264
1265 return this.renderer.listitem(body);
1266 }
1267 case 'html': {
1268 // TODO parse inline content if parameter markdown=1
1269 return this.renderer.html(this.token.text);
1270 }
1271 case 'paragraph': {
1272 return this.renderer.paragraph(this.inline.output(this.token.text));
1273 }
1274 case 'text': {
1275 return this.renderer.paragraph(this.parseText());
1276 }
1277 default: {
1278 var errMsg = 'Token with "' + this.token.type + '" type was not found.';
1279 if (this.options.silent) {
1280 console.log(errMsg);
1281 } else {
1282 throw new Error(errMsg);
1283 }
1284 }
1285 }
1286};
1287
1288/**
1289 * Slugger generates header id
1290 */
1291
1292function Slugger () {
1293 this.seen = {};
1294}
1295
1296/**
1297 * Convert string to unique id
1298 */
1299
1300Slugger.prototype.slug = function (value) {
1301 var slug = value
1302 .toLowerCase()
1303 .trim()
1304 .replace(/[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,./:;<=>?@[\]^`{|}~]/g, '')
1305 .replace(/\s/g, '-');
1306
1307 if (this.seen.hasOwnProperty(slug)) {
1308 var originalSlug = slug;
1309 do {
1310 this.seen[originalSlug]++;
1311 slug = originalSlug + '-' + this.seen[originalSlug];
1312 } while (this.seen.hasOwnProperty(slug));
1313 }
1314 this.seen[slug] = 0;
1315
1316 return slug;
1317};
1318
1319/**
1320 * Helpers
1321 */
1322
1323function escape(html, encode) {
1324 if (encode) {
1325 if (escape.escapeTest.test(html)) {
1326 return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch]; });
1327 }
1328 } else {
1329 if (escape.escapeTestNoEncode.test(html)) {
1330 return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch]; });
1331 }
1332 }
1333
1334 return html;
1335}
1336
1337escape.escapeTest = /[&<>"']/;
1338escape.escapeReplace = /[&<>"']/g;
1339escape.replacements = {
1340 '&': '&amp;',
1341 '<': '&lt;',
1342 '>': '&gt;',
1343 '"': '&quot;',
1344 "'": '&#39;'
1345};
1346
1347escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
1348escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
1349
1350function unescape(html) {
1351 // explicitly match decimal, hex, and named HTML entities
1352 return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
1353 n = n.toLowerCase();
1354 if (n === 'colon') return ':';
1355 if (n.charAt(0) === '#') {
1356 return n.charAt(1) === 'x'
1357 ? String.fromCharCode(parseInt(n.substring(2), 16))
1358 : String.fromCharCode(+n.substring(1));
1359 }
1360 return '';
1361 });
1362}
1363
1364function edit(regex, opt) {
1365 regex = regex.source || regex;
1366 opt = opt || '';
1367 return {
1368 replace: function(name, val) {
1369 val = val.source || val;
1370 val = val.replace(/(^|[^\[])\^/g, '$1');
1371 regex = regex.replace(name, val);
1372 return this;
1373 },
1374 getRegex: function() {
1375 return new RegExp(regex, opt);
1376 }
1377 };
1378}
1379
1380function cleanUrl(sanitize, base, href) {
1381 if (sanitize) {
1382 try {
1383 var prot = decodeURIComponent(unescape(href))
1384 .replace(/[^\w:]/g, '')
1385 .toLowerCase();
1386 } catch (e) {
1387 return null;
1388 }
1389 if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0 || prot.indexOf('data:') === 0) {
1390 return null;
1391 }
1392 }
1393 if (base && !originIndependentUrl.test(href)) {
1394 href = resolveUrl(base, href);
1395 }
1396 try {
1397 href = encodeURI(href).replace(/%25/g, '%');
1398 } catch (e) {
1399 return null;
1400 }
1401 return href;
1402}
1403
1404function resolveUrl(base, href) {
1405 if (!baseUrls[' ' + base]) {
1406 // we can ignore everything in base after the last slash of its path component,
1407 // but we might need to add _that_
1408 // https://tools.ietf.org/html/rfc3986#section-3
1409 if (/^[^:]+:\/*[^/]*$/.test(base)) {
1410 baseUrls[' ' + base] = base + '/';
1411 } else {
1412 baseUrls[' ' + base] = rtrim(base, '/', true);
1413 }
1414 }
1415 base = baseUrls[' ' + base];
1416
1417 if (href.slice(0, 2) === '//') {
1418 return base.replace(/:[\s\S]*/, ':') + href;
1419 } else if (href.charAt(0) === '/') {
1420 return base.replace(/(:\/*[^/]*)[\s\S]*/, '$1') + href;
1421 } else {
1422 return base + href;
1423 }
1424}
1425var baseUrls = {};
1426var originIndependentUrl = /^$|^[a-z][a-z0-9+.-]*:|^[?#]/i;
1427
1428function noop() {}
1429noop.exec = noop;
1430
1431function merge(obj) {
1432 var i = 1,
1433 target,
1434 key;
1435
1436 for (; i < arguments.length; i++) {
1437 target = arguments[i];
1438 for (key in target) {
1439 if (Object.prototype.hasOwnProperty.call(target, key)) {
1440 obj[key] = target[key];
1441 }
1442 }
1443 }
1444
1445 return obj;
1446}
1447
1448function splitCells(tableRow, count) {
1449 // ensure that every cell-delimiting pipe has a space
1450 // before it to distinguish it from an escaped pipe
1451 var row = tableRow.replace(/\|/g, function (match, offset, str) {
1452 var escaped = false,
1453 curr = offset;
1454 while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
1455 if (escaped) {
1456 // odd number of slashes means | is escaped
1457 // so we leave it alone
1458 return '|';
1459 } else {
1460 // add space before unescaped |
1461 return ' |';
1462 }
1463 }),
1464 cells = row.split(/ \|/),
1465 i = 0;
1466
1467 if (cells.length > count) {
1468 cells.splice(count);
1469 } else {
1470 while (cells.length < count) cells.push('');
1471 }
1472
1473 for (; i < cells.length; i++) {
1474 // leading or trailing whitespace is ignored per the gfm spec
1475 cells[i] = cells[i].trim().replace(/\\\|/g, '|');
1476 }
1477 return cells;
1478}
1479
1480// Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
1481// /c*$/ is vulnerable to REDOS.
1482// invert: Remove suffix of non-c chars instead. Default falsey.
1483function rtrim(str, c, invert) {
1484 if (str.length === 0) {
1485 return '';
1486 }
1487
1488 // Length of suffix matching the invert condition.
1489 var suffLen = 0;
1490
1491 // Step left until we fail to match the invert condition.
1492 while (suffLen < str.length) {
1493 var currChar = str.charAt(str.length - suffLen - 1);
1494 if (currChar === c && !invert) {
1495 suffLen++;
1496 } else if (currChar !== c && invert) {
1497 suffLen++;
1498 } else {
1499 break;
1500 }
1501 }
1502
1503 return str.substr(0, str.length - suffLen);
1504}
1505
1506/**
1507 * Marked
1508 */
1509
1510function marked(src, opt, callback) {
1511 // throw error in case of non string input
1512 if (typeof src === 'undefined' || src === null) {
1513 throw new Error('marked(): input parameter is undefined or null');
1514 }
1515 if (typeof src !== 'string') {
1516 throw new Error('marked(): input parameter is of type '
1517 + Object.prototype.toString.call(src) + ', string expected');
1518 }
1519
1520 if (callback || typeof opt === 'function') {
1521 if (!callback) {
1522 callback = opt;
1523 opt = null;
1524 }
1525
1526 opt = merge({}, marked.defaults, opt || {});
1527
1528 var highlight = opt.highlight,
1529 tokens,
1530 pending,
1531 i = 0;
1532
1533 try {
1534 tokens = Lexer.lex(src, opt);
1535 } catch (e) {
1536 return callback(e);
1537 }
1538
1539 pending = tokens.length;
1540
1541 var done = function(err) {
1542 if (err) {
1543 opt.highlight = highlight;
1544 return callback(err);
1545 }
1546
1547 var out;
1548
1549 try {
1550 out = Parser.parse(tokens, opt);
1551 } catch (e) {
1552 err = e;
1553 }
1554
1555 opt.highlight = highlight;
1556
1557 return err
1558 ? callback(err)
1559 : callback(null, out);
1560 };
1561
1562 if (!highlight || highlight.length < 3) {
1563 return done();
1564 }
1565
1566 delete opt.highlight;
1567
1568 if (!pending) return done();
1569
1570 for (; i < tokens.length; i++) {
1571 (function(token) {
1572 if (token.type !== 'code') {
1573 return --pending || done();
1574 }
1575 return highlight(token.text, token.lang, function(err, code) {
1576 if (err) return done(err);
1577 if (code == null || code === token.text) {
1578 return --pending || done();
1579 }
1580 token.text = code;
1581 token.escaped = true;
1582 --pending || done();
1583 });
1584 })(tokens[i]);
1585 }
1586
1587 return;
1588 }
1589 try {
1590 if (opt) opt = merge({}, marked.defaults, opt);
1591 return Parser.parse(Lexer.lex(src, opt), opt);
1592 } catch (e) {
1593 e.message += '\nPlease report this to https://github.com/markedjs/marked.';
1594 if ((opt || marked.defaults).silent) {
1595 return '<p>An error occurred:</p><pre>'
1596 + escape(e.message + '', true)
1597 + '</pre>';
1598 }
1599 throw e;
1600 }
1601}
1602
1603/**
1604 * Options
1605 */
1606
1607marked.options =
1608marked.setOptions = function(opt) {
1609 merge(marked.defaults, opt);
1610 return marked;
1611};
1612
1613marked.getDefaults = function () {
1614 return {
1615 baseUrl: null,
1616 breaks: false,
1617 gfm: true,
1618 headerIds: true,
1619 headerPrefix: '',
1620 highlight: null,
1621 langPrefix: 'language-',
1622 mangle: true,
1623 pedantic: false,
1624 renderer: new Renderer(),
1625 sanitize: false,
1626 sanitizer: null,
1627 silent: false,
1628 smartLists: false,
1629 smartypants: false,
1630 tables: true,
1631 xhtml: false
1632 };
1633};
1634
1635marked.defaults = marked.getDefaults();
1636
1637/**
1638 * Expose
1639 */
1640
1641marked.Parser = Parser;
1642marked.parser = Parser.parse;
1643
1644marked.Renderer = Renderer;
1645marked.TextRenderer = TextRenderer;
1646
1647marked.Lexer = Lexer;
1648marked.lexer = Lexer.lex;
1649
1650marked.InlineLexer = InlineLexer;
1651marked.inlineLexer = InlineLexer.output;
1652
1653marked.Slugger = Slugger;
1654
1655marked.parse = marked;
1656
1657if (typeof module !== 'undefined' && typeof exports === 'object') {
1658 module.exports = marked;
1659} else if (typeof define === 'function' && define.amd) {
1660 define(function() { return marked; });
1661} else {
1662 root.marked = marked;
1663}
1664})(this || (typeof window !== 'undefined' ? window : global));