marked 0.3.19 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/marked.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * marked - a markdown parser
3
- * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
3
+ * Copyright (c) 2011-2018, Christopher Jeffrey. (MIT Licensed)
4
4
  * https://github.com/markedjs/marked
5
5
  */
6
6
 
@@ -16,20 +16,29 @@ var block = {
16
16
  code: /^( {4}[^\n]+\n*)+/,
17
17
  fences: noop,
18
18
  hr: /^ {0,3}((?:- *){3,}|(?:_ *){3,}|(?:\* *){3,})(?:\n+|$)/,
19
- heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19
+ heading: /^ *(#{1,6}) *([^\n]+?) *(?:#+ *)?(?:\n+|$)/,
20
20
  nptable: noop,
21
21
  blockquote: /^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/,
22
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*$))/,
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
+ + ')',
24
33
  def: /^ {0,3}\[(label)\]: *\n? *<?([^\s>]+)>?(?:(?: +\n? *| *\n *)(title))? *(?:\n+|$)/,
25
34
  table: noop,
26
35
  lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
27
- paragraph: /^([^\n]+(?:\n?(?!hr|heading|lheading| {0,3}>|tag)[^\n]+)+)/,
36
+ paragraph: /^([^\n]+(?:\n(?!hr|heading|lheading| {0,3}>|<\/?(?:tag)(?: +|\n|\/?>)|<(?:script|pre|style|!--))[^\n]+)*)/,
28
37
  text: /^[^\n]+/
29
38
  };
30
39
 
31
- block._label = /(?:\\[\[\]]|[^\[\]])+/;
32
- block._title = /(?:"(?:\\"|[^"]|"[^"\n]*")*"|'\n?(?:[^'\n]+\n?)*'|\([^()]*\))/;
40
+ block._label = /(?!\s*\])(?:\\[\[\]]|[^\[\]])+/;
41
+ block._title = /(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/;
33
42
  block.def = edit(block.def)
34
43
  .replace('label', block._label)
35
44
  .replace('title', block._title)
@@ -47,23 +56,24 @@ block.list = edit(block.list)
47
56
  .replace('def', '\\n+(?=' + block.def.source + ')')
48
57
  .getRegex();
49
58
 
50
- block._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
-
55
- block.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)
59
+ block._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';
65
+ block._comment = /<!--(?!-?>)[\s\S]*?-->/;
66
+ block.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"'=<>`]+)?/)
60
70
  .getRegex();
61
71
 
62
72
  block.paragraph = edit(block.paragraph)
63
73
  .replace('hr', block.hr)
64
74
  .replace('heading', block.heading)
65
75
  .replace('lheading', block.lheading)
66
- .replace('tag', '<' + block._tag)
76
+ .replace('tag', block._tag) // pars can be interrupted by type (6) html blocks
67
77
  .getRegex();
68
78
 
69
79
  block.blockquote = edit(block.blockquote)
@@ -97,8 +107,26 @@ block.gfm.paragraph = edit(block.paragraph)
97
107
  */
98
108
 
99
109
  block.tables = merge({}, block.gfm, {
100
- nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
101
- table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
110
+ nptable: /^ *([^|\n ].*\|.*)\n *([-:]+ *\|[-| :]*)(?:\n((?:.*[^>\n ].*(?:\n|$))*)\n*|$)/,
111
+ table: /^ *\|(.+)\n *\|?( *[-:]+[-| :]*)(?:\n((?: *[^>\n ].*(?:\n|$))*)\n*|$)/
112
+ });
113
+
114
+ /**
115
+ * Pedantic grammar
116
+ */
117
+
118
+ block.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+|$)/
102
130
  });
103
131
 
104
132
  /**
@@ -107,11 +135,13 @@ block.tables = merge({}, block.gfm, {
107
135
 
108
136
  function Lexer(options) {
109
137
  this.tokens = [];
110
- this.tokens.links = {};
138
+ this.tokens.links = Object.create(null);
111
139
  this.options = options || marked.defaults;
112
140
  this.rules = block.normal;
113
141
 
114
- if (this.options.gfm) {
142
+ if (this.options.pedantic) {
143
+ this.rules = block.pedantic;
144
+ } else if (this.options.gfm) {
115
145
  if (this.options.tables) {
116
146
  this.rules = block.tables;
117
147
  } else {
@@ -161,11 +191,16 @@ Lexer.prototype.token = function(src, top) {
161
191
  bull,
162
192
  b,
163
193
  item,
194
+ listStart,
195
+ listItems,
196
+ t,
164
197
  space,
165
198
  i,
166
199
  tag,
167
200
  l,
168
- isordered;
201
+ isordered,
202
+ istask,
203
+ ischecked;
169
204
 
170
205
  while (src) {
171
206
  // newline
@@ -185,7 +220,7 @@ Lexer.prototype.token = function(src, top) {
185
220
  this.tokens.push({
186
221
  type: 'code',
187
222
  text: !this.options.pedantic
188
- ? cap.replace(/\n+$/, '')
223
+ ? rtrim(cap, '\n')
189
224
  : cap
190
225
  });
191
226
  continue;
@@ -215,34 +250,36 @@ Lexer.prototype.token = function(src, top) {
215
250
 
216
251
  // table no leading pipe (gfm)
217
252
  if (top && (cap = this.rules.nptable.exec(src))) {
218
- src = src.substring(cap[0].length);
219
-
220
253
  item = {
221
254
  type: 'table',
222
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
255
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
223
256
  align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
224
- cells: cap[3].replace(/\n$/, '').split('\n')
257
+ cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
225
258
  };
226
259
 
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;
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
+ }
236
273
  }
237
- }
238
274
 
239
- for (i = 0; i < item.cells.length; i++) {
240
- item.cells[i] = item.cells[i].split(/ *\| */);
241
- }
275
+ for (i = 0; i < item.cells.length; i++) {
276
+ item.cells[i] = splitCells(item.cells[i], item.header.length);
277
+ }
242
278
 
243
- this.tokens.push(item);
279
+ this.tokens.push(item);
244
280
 
245
- continue;
281
+ continue;
282
+ }
246
283
  }
247
284
 
248
285
  // hr
@@ -282,15 +319,19 @@ Lexer.prototype.token = function(src, top) {
282
319
  bull = cap[2];
283
320
  isordered = bull.length > 1;
284
321
 
285
- this.tokens.push({
322
+ listStart = {
286
323
  type: 'list_start',
287
324
  ordered: isordered,
288
- start: isordered ? +bull : ''
289
- });
325
+ start: isordered ? +bull : '',
326
+ loose: false
327
+ };
328
+
329
+ this.tokens.push(listStart);
290
330
 
291
331
  // Get each top-level item.
292
332
  cap = cap[0].match(this.rules.item);
293
333
 
334
+ listItems = [];
294
335
  next = false;
295
336
  l = cap.length;
296
337
  i = 0;
@@ -331,11 +372,27 @@ Lexer.prototype.token = function(src, top) {
331
372
  if (!loose) loose = next;
332
373
  }
333
374
 
334
- this.tokens.push({
335
- type: loose
336
- ? 'loose_item_start'
337
- : 'list_item_start'
338
- });
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);
339
396
 
340
397
  // Recurse.
341
398
  this.token(item, false);
@@ -345,6 +402,14 @@ Lexer.prototype.token = function(src, top) {
345
402
  });
346
403
  }
347
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
+
348
413
  this.tokens.push({
349
414
  type: 'list_end'
350
415
  });
@@ -370,7 +435,7 @@ Lexer.prototype.token = function(src, top) {
370
435
  if (top && (cap = this.rules.def.exec(src))) {
371
436
  src = src.substring(cap[0].length);
372
437
  if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
373
- tag = cap[1].toLowerCase();
438
+ tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
374
439
  if (!this.tokens.links[tag]) {
375
440
  this.tokens.links[tag] = {
376
441
  href: cap[2],
@@ -382,36 +447,38 @@ Lexer.prototype.token = function(src, top) {
382
447
 
383
448
  // table (gfm)
384
449
  if (top && (cap = this.rules.table.exec(src))) {
385
- src = src.substring(cap[0].length);
386
-
387
450
  item = {
388
451
  type: 'table',
389
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
452
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
390
453
  align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
391
- cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
454
+ cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
392
455
  };
393
456
 
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;
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
+ }
403
470
  }
404
- }
405
471
 
406
- for (i = 0; i < item.cells.length; i++) {
407
- item.cells[i] = item.cells[i]
408
- .replace(/^ *\| *| *\| *$/g, '')
409
- .split(/ *\| */);
410
- }
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
+ }
411
477
 
412
- this.tokens.push(item);
478
+ this.tokens.push(item);
413
479
 
414
- continue;
480
+ continue;
481
+ }
415
482
  }
416
483
 
417
484
  // lheading
@@ -461,39 +528,54 @@ Lexer.prototype.token = function(src, top) {
461
528
  */
462
529
 
463
530
  var inline = {
464
- escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
531
+ escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
465
532
  autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
466
533
  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*$)/,
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*$)/,
475
547
  del: noop,
476
- text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
548
+ text: /^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
477
549
  };
478
550
 
551
+ inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
552
+
479
553
  inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
480
554
  inline._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
-
482
555
  inline.autolink = edit(inline.autolink)
483
556
  .replace('scheme', inline._scheme)
484
557
  .replace('email', inline._email)
485
- .getRegex()
558
+ .getRegex();
486
559
 
487
- inline._inside = /(?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/;
488
- inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
560
+ inline._attribute = /\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/;
561
+
562
+ inline.tag = edit(inline.tag)
563
+ .replace('comment', block._comment)
564
+ .replace('attribute', inline._attribute)
565
+ .getRegex();
566
+
567
+ inline._label = /(?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?/;
568
+ inline._href = /\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f\\]*\)|[^\s\x00-\x1f()\\])*?)/;
569
+ inline._title = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
489
570
 
490
571
  inline.link = edit(inline.link)
491
- .replace('inside', inline._inside)
572
+ .replace('label', inline._label)
492
573
  .replace('href', inline._href)
574
+ .replace('title', inline._title)
493
575
  .getRegex();
494
576
 
495
577
  inline.reflink = edit(inline.reflink)
496
- .replace('inside', inline._inside)
578
+ .replace('label', inline._label)
497
579
  .getRegex();
498
580
 
499
581
  /**
@@ -508,7 +590,13 @@ inline.normal = merge({}, inline);
508
590
 
509
591
  inline.pedantic = merge({}, inline.normal, {
510
592
  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
511
- em: /^_(?=\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()
512
600
  });
513
601
 
514
602
  /**
@@ -517,17 +605,19 @@ inline.pedantic = merge({}, inline.normal, {
517
605
 
518
606
  inline.gfm = merge({}, inline.normal, {
519
607
  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(),
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/,
523
610
  _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
524
- del: /^~~(?=\S)([\s\S]*?\S)~~/,
611
+ del: /^~+(?=\S)([\s\S]*?\S)~+/,
525
612
  text: edit(inline.text)
526
613
  .replace(']|', '~]|')
527
- .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
614
+ .replace('|$', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|$')
528
615
  .getRegex()
529
616
  });
530
617
 
618
+ inline.gfm.url = edit(inline.gfm.url)
619
+ .replace('email', inline.gfm._extended_email)
620
+ .getRegex();
531
621
  /**
532
622
  * GFM + Line Breaks Inline Grammar
533
623
  */
@@ -552,14 +642,14 @@ function InlineLexer(links, options) {
552
642
  throw new Error('Tokens array requires a `links` property.');
553
643
  }
554
644
 
555
- if (this.options.gfm) {
645
+ if (this.options.pedantic) {
646
+ this.rules = inline.pedantic;
647
+ } else if (this.options.gfm) {
556
648
  if (this.options.breaks) {
557
649
  this.rules = inline.breaks;
558
650
  } else {
559
651
  this.rules = inline.gfm;
560
652
  }
561
- } else if (this.options.pedantic) {
562
- this.rules = inline.pedantic;
563
653
  }
564
654
  }
565
655
 
@@ -587,7 +677,9 @@ InlineLexer.prototype.output = function(src) {
587
677
  link,
588
678
  text,
589
679
  href,
590
- cap;
680
+ title,
681
+ cap,
682
+ prevCapZero;
591
683
 
592
684
  while (src) {
593
685
  // escape
@@ -613,12 +705,15 @@ InlineLexer.prototype.output = function(src) {
613
705
 
614
706
  // url (gfm)
615
707
  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
708
  if (cap[2] === '@') {
619
709
  text = escape(cap[0]);
620
710
  href = 'mailto:' + text;
621
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]);
622
717
  text = escape(cap[0]);
623
718
  if (cap[1] === 'www.') {
624
719
  href = 'http://' + text;
@@ -626,6 +721,7 @@ InlineLexer.prototype.output = function(src) {
626
721
  href = text;
627
722
  }
628
723
  }
724
+ src = src.substring(cap[0].length);
629
725
  out += this.renderer.link(href, null, text);
630
726
  continue;
631
727
  }
@@ -637,12 +733,18 @@ InlineLexer.prototype.output = function(src) {
637
733
  } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
638
734
  this.inLink = false;
639
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
+
640
742
  src = src.substring(cap[0].length);
641
743
  out += this.options.sanitize
642
744
  ? this.options.sanitizer
643
745
  ? this.options.sanitizer(cap[0])
644
746
  : escape(cap[0])
645
- : cap[0]
747
+ : cap[0];
646
748
  continue;
647
749
  }
648
750
 
@@ -650,9 +752,23 @@ InlineLexer.prototype.output = function(src) {
650
752
  if (cap = this.rules.link.exec(src)) {
651
753
  src = src.substring(cap[0].length);
652
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');
653
769
  out += this.outputLink(cap, {
654
- href: cap[2],
655
- title: cap[3]
770
+ href: InlineLexer.escapes(href),
771
+ title: InlineLexer.escapes(title)
656
772
  });
657
773
  this.inLink = false;
658
774
  continue;
@@ -678,14 +794,14 @@ InlineLexer.prototype.output = function(src) {
678
794
  // strong
679
795
  if (cap = this.rules.strong.exec(src)) {
680
796
  src = src.substring(cap[0].length);
681
- out += this.renderer.strong(this.output(cap[2] || cap[1]));
797
+ out += this.renderer.strong(this.output(cap[4] || cap[3] || cap[2] || cap[1]));
682
798
  continue;
683
799
  }
684
800
 
685
801
  // em
686
802
  if (cap = this.rules.em.exec(src)) {
687
803
  src = src.substring(cap[0].length);
688
- out += this.renderer.em(this.output(cap[2] || cap[1]));
804
+ out += this.renderer.em(this.output(cap[6] || cap[5] || cap[4] || cap[3] || cap[2] || cap[1]));
689
805
  continue;
690
806
  }
691
807
 
@@ -713,7 +829,11 @@ InlineLexer.prototype.output = function(src) {
713
829
  // text
714
830
  if (cap = this.rules.text.exec(src)) {
715
831
  src = src.substring(cap[0].length);
716
- out += this.renderer.text(escape(this.smartypants(cap[0])));
832
+ if (this.inRawBlock) {
833
+ out += this.renderer.text(cap[0]);
834
+ } else {
835
+ out += this.renderer.text(escape(this.smartypants(cap[0])));
836
+ }
717
837
  continue;
718
838
  }
719
839
 
@@ -725,12 +845,16 @@ InlineLexer.prototype.output = function(src) {
725
845
  return out;
726
846
  };
727
847
 
848
+ InlineLexer.escapes = function(text) {
849
+ return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
850
+ };
851
+
728
852
  /**
729
853
  * Compile Link
730
854
  */
731
855
 
732
856
  InlineLexer.prototype.outputLink = function(cap, link) {
733
- var href = escape(link.href),
857
+ var href = link.href,
734
858
  title = link.title ? escape(link.title) : null;
735
859
 
736
860
  return cap[0].charAt(0) !== '!'
@@ -788,7 +912,7 @@ InlineLexer.prototype.mangle = function(text) {
788
912
  */
789
913
 
790
914
  function Renderer(options) {
791
- this.options = options || {};
915
+ this.options = options || marked.defaults;
792
916
  }
793
917
 
794
918
  Renderer.prototype.code = function(code, lang, escaped) {
@@ -803,7 +927,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
803
927
  if (!lang) {
804
928
  return '<pre><code>'
805
929
  + (escaped ? code : escape(code, true))
806
- + '\n</code></pre>';
930
+ + '</code></pre>';
807
931
  }
808
932
 
809
933
  return '<pre><code class="'
@@ -811,7 +935,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
811
935
  + escape(lang, true)
812
936
  + '">'
813
937
  + (escaped ? code : escape(code, true))
814
- + '\n</code></pre>\n';
938
+ + '</code></pre>\n';
815
939
  };
816
940
 
817
941
  Renderer.prototype.blockquote = function(quote) {
@@ -823,16 +947,20 @@ Renderer.prototype.html = function(html) {
823
947
  };
824
948
 
825
949
  Renderer.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';
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';
836
964
  };
837
965
 
838
966
  Renderer.prototype.hr = function() {
@@ -849,18 +977,26 @@ Renderer.prototype.listitem = function(text) {
849
977
  return '<li>' + text + '</li>\n';
850
978
  };
851
979
 
980
+ Renderer.prototype.checkbox = function(checked) {
981
+ return '<input '
982
+ + (checked ? 'checked="" ' : '')
983
+ + 'disabled="" type="checkbox"'
984
+ + (this.options.xhtml ? ' /' : '')
985
+ + '> ';
986
+ };
987
+
852
988
  Renderer.prototype.paragraph = function(text) {
853
989
  return '<p>' + text + '</p>\n';
854
990
  };
855
991
 
856
992
  Renderer.prototype.table = function(header, body) {
993
+ if (body) body = '<tbody>' + body + '</tbody>';
994
+
857
995
  return '<table>\n'
858
996
  + '<thead>\n'
859
997
  + header
860
998
  + '</thead>\n'
861
- + '<tbody>\n'
862
999
  + body
863
- + '</tbody>\n'
864
1000
  + '</table>\n';
865
1001
  };
866
1002
 
@@ -871,7 +1007,7 @@ Renderer.prototype.tablerow = function(content) {
871
1007
  Renderer.prototype.tablecell = function(content, flags) {
872
1008
  var type = flags.header ? 'th' : 'td';
873
1009
  var tag = flags.align
874
- ? '<' + type + ' style="text-align:' + flags.align + '">'
1010
+ ? '<' + type + ' align="' + flags.align + '">'
875
1011
  : '<' + type + '>';
876
1012
  return tag + content + '</' + type + '>\n';
877
1013
  };
@@ -898,22 +1034,11 @@ Renderer.prototype.del = function(text) {
898
1034
  };
899
1035
 
900
1036
  Renderer.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);
1037
+ href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1038
+ if (href === null) {
1039
+ return text;
915
1040
  }
916
- var out = '<a href="' + href + '"';
1041
+ var out = '<a href="' + escape(href) + '"';
917
1042
  if (title) {
918
1043
  out += ' title="' + title + '"';
919
1044
  }
@@ -922,9 +1047,11 @@ Renderer.prototype.link = function(href, title, text) {
922
1047
  };
923
1048
 
924
1049
  Renderer.prototype.image = function(href, title, text) {
925
- if (this.options.baseUrl && !originIndependentUrl.test(href)) {
926
- href = resolveUrl(this.options.baseUrl, href);
1050
+ href = cleanUrl(this.options.sanitize, this.options.baseUrl, href);
1051
+ if (href === null) {
1052
+ return text;
927
1053
  }
1054
+
928
1055
  var out = '<img src="' + href + '" alt="' + text + '"';
929
1056
  if (title) {
930
1057
  out += ' title="' + title + '"';
@@ -952,16 +1079,16 @@ TextRenderer.prototype.codespan =
952
1079
  TextRenderer.prototype.del =
953
1080
  TextRenderer.prototype.text = function (text) {
954
1081
  return text;
955
- }
1082
+ };
956
1083
 
957
1084
  TextRenderer.prototype.link =
958
1085
  TextRenderer.prototype.image = function(href, title, text) {
959
1086
  return '' + text;
960
- }
1087
+ };
961
1088
 
962
1089
  TextRenderer.prototype.br = function() {
963
1090
  return '';
964
- }
1091
+ };
965
1092
 
966
1093
  /**
967
1094
  * Parsing & Compiling
@@ -1114,29 +1241,23 @@ Parser.prototype.tok = function() {
1114
1241
  }
1115
1242
  case 'list_item_start': {
1116
1243
  body = '';
1244
+ var loose = this.token.loose;
1117
1245
 
1118
- while (this.next().type !== 'list_item_end') {
1119
- body += this.token.type === 'text'
1120
- ? this.parseText()
1121
- : this.tok();
1246
+ if (this.token.task) {
1247
+ body += this.renderer.checkbox(this.token.checked);
1122
1248
  }
1123
1249
 
1124
- return this.renderer.listitem(body);
1125
- }
1126
- case 'loose_item_start': {
1127
- body = '';
1128
-
1129
1250
  while (this.next().type !== 'list_item_end') {
1130
- body += this.tok();
1251
+ body += !loose && this.token.type === 'text'
1252
+ ? this.parseText()
1253
+ : this.tok();
1131
1254
  }
1132
1255
 
1133
1256
  return this.renderer.listitem(body);
1134
1257
  }
1135
1258
  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);
1259
+ // TODO parse inline content if parameter markdown=1
1260
+ return this.renderer.html(this.token.text);
1140
1261
  }
1141
1262
  case 'paragraph': {
1142
1263
  return this.renderer.paragraph(this.inline.output(this.token.text));
@@ -1152,14 +1273,32 @@ Parser.prototype.tok = function() {
1152
1273
  */
1153
1274
 
1154
1275
  function 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;');
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;
1161
1287
  }
1162
1288
 
1289
+ escape.escapeTest = /[&<>"']/;
1290
+ escape.escapeReplace = /[&<>"']/g;
1291
+ escape.replacements = {
1292
+ '&': '&amp;',
1293
+ '<': '&lt;',
1294
+ '>': '&gt;',
1295
+ '"': '&quot;',
1296
+ "'": '&#39;'
1297
+ };
1298
+
1299
+ escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
1300
+ escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
1301
+
1163
1302
  function unescape(html) {
1164
1303
  // explicitly match decimal, hex, and named HTML entities
1165
1304
  return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
@@ -1175,7 +1314,7 @@ function unescape(html) {
1175
1314
  }
1176
1315
 
1177
1316
  function edit(regex, opt) {
1178
- regex = regex.source;
1317
+ regex = regex.source || regex;
1179
1318
  opt = opt || '';
1180
1319
  return {
1181
1320
  replace: function(name, val) {
@@ -1190,6 +1329,30 @@ function edit(regex, opt) {
1190
1329
  };
1191
1330
  }
1192
1331
 
1332
+ function 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
+
1193
1356
  function resolveUrl(base, href) {
1194
1357
  if (!baseUrls[' ' + base]) {
1195
1358
  // we can ignore everything in base after the last slash of its path component,
@@ -1198,7 +1361,7 @@ function resolveUrl(base, href) {
1198
1361
  if (/^[^:]+:\/*[^/]*$/.test(base)) {
1199
1362
  baseUrls[' ' + base] = base + '/';
1200
1363
  } else {
1201
- baseUrls[' ' + base] = base.replace(/[^/]*$/, '');
1364
+ baseUrls[' ' + base] = rtrim(base, '/', true);
1202
1365
  }
1203
1366
  }
1204
1367
  base = baseUrls[' ' + base];
@@ -1234,6 +1397,64 @@ function merge(obj) {
1234
1397
  return obj;
1235
1398
  }
1236
1399
 
1400
+ function 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.
1435
+ function 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
+
1237
1458
  /**
1238
1459
  * Marked
1239
1460
  */
@@ -1262,7 +1483,7 @@ function marked(src, opt, callback) {
1262
1483
  i = 0;
1263
1484
 
1264
1485
  try {
1265
- tokens = Lexer.lex(src, opt)
1486
+ tokens = Lexer.lex(src, opt);
1266
1487
  } catch (e) {
1267
1488
  return callback(e);
1268
1489
  }
@@ -1341,25 +1562,30 @@ marked.setOptions = function(opt) {
1341
1562
  return marked;
1342
1563
  };
1343
1564
 
1344
- marked.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
1565
+ marked.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
+ };
1361
1585
  };
1362
1586
 
1587
+ marked.defaults = marked.getDefaults();
1588
+
1363
1589
  /**
1364
1590
  * Expose
1365
1591
  */