marked 0.3.18 → 0.5.1

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,10 +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
- l;
200
+ l,
201
+ isordered,
202
+ istask,
203
+ ischecked;
168
204
 
169
205
  while (src) {
170
206
  // newline
@@ -184,7 +220,7 @@ Lexer.prototype.token = function(src, top) {
184
220
  this.tokens.push({
185
221
  type: 'code',
186
222
  text: !this.options.pedantic
187
- ? cap.replace(/\n+$/, '')
223
+ ? rtrim(cap, '\n')
188
224
  : cap
189
225
  });
190
226
  continue;
@@ -214,34 +250,36 @@ Lexer.prototype.token = function(src, top) {
214
250
 
215
251
  // table no leading pipe (gfm)
216
252
  if (top && (cap = this.rules.nptable.exec(src))) {
217
- src = src.substring(cap[0].length);
218
-
219
253
  item = {
220
254
  type: 'table',
221
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
255
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
222
256
  align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
223
- cells: cap[3].replace(/\n$/, '').split('\n')
257
+ cells: cap[3] ? cap[3].replace(/\n$/, '').split('\n') : []
224
258
  };
225
259
 
226
- for (i = 0; i < item.align.length; i++) {
227
- if (/^ *-+: *$/.test(item.align[i])) {
228
- item.align[i] = 'right';
229
- } else if (/^ *:-+: *$/.test(item.align[i])) {
230
- item.align[i] = 'center';
231
- } else if (/^ *:-+ *$/.test(item.align[i])) {
232
- item.align[i] = 'left';
233
- } else {
234
- 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
+ }
235
273
  }
236
- }
237
274
 
238
- for (i = 0; i < item.cells.length; i++) {
239
- item.cells[i] = item.cells[i].split(/ *\| */);
240
- }
275
+ for (i = 0; i < item.cells.length; i++) {
276
+ item.cells[i] = splitCells(item.cells[i], item.header.length);
277
+ }
241
278
 
242
- this.tokens.push(item);
279
+ this.tokens.push(item);
243
280
 
244
- continue;
281
+ continue;
282
+ }
245
283
  }
246
284
 
247
285
  // hr
@@ -279,15 +317,21 @@ Lexer.prototype.token = function(src, top) {
279
317
  if (cap = this.rules.list.exec(src)) {
280
318
  src = src.substring(cap[0].length);
281
319
  bull = cap[2];
320
+ isordered = bull.length > 1;
282
321
 
283
- this.tokens.push({
322
+ listStart = {
284
323
  type: 'list_start',
285
- ordered: bull.length > 1
286
- });
324
+ ordered: isordered,
325
+ start: isordered ? +bull : '',
326
+ loose: false
327
+ };
328
+
329
+ this.tokens.push(listStart);
287
330
 
288
331
  // Get each top-level item.
289
332
  cap = cap[0].match(this.rules.item);
290
333
 
334
+ listItems = [];
291
335
  next = false;
292
336
  l = cap.length;
293
337
  i = 0;
@@ -328,11 +372,27 @@ Lexer.prototype.token = function(src, top) {
328
372
  if (!loose) loose = next;
329
373
  }
330
374
 
331
- this.tokens.push({
332
- type: loose
333
- ? 'loose_item_start'
334
- : 'list_item_start'
335
- });
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);
336
396
 
337
397
  // Recurse.
338
398
  this.token(item, false);
@@ -342,6 +402,14 @@ Lexer.prototype.token = function(src, top) {
342
402
  });
343
403
  }
344
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
+
345
413
  this.tokens.push({
346
414
  type: 'list_end'
347
415
  });
@@ -367,7 +435,7 @@ Lexer.prototype.token = function(src, top) {
367
435
  if (top && (cap = this.rules.def.exec(src))) {
368
436
  src = src.substring(cap[0].length);
369
437
  if (cap[3]) cap[3] = cap[3].substring(1, cap[3].length - 1);
370
- tag = cap[1].toLowerCase();
438
+ tag = cap[1].toLowerCase().replace(/\s+/g, ' ');
371
439
  if (!this.tokens.links[tag]) {
372
440
  this.tokens.links[tag] = {
373
441
  href: cap[2],
@@ -379,36 +447,38 @@ Lexer.prototype.token = function(src, top) {
379
447
 
380
448
  // table (gfm)
381
449
  if (top && (cap = this.rules.table.exec(src))) {
382
- src = src.substring(cap[0].length);
383
-
384
450
  item = {
385
451
  type: 'table',
386
- header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
452
+ header: splitCells(cap[1].replace(/^ *| *\| *$/g, '')),
387
453
  align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
388
- cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
454
+ cells: cap[3] ? cap[3].replace(/(?: *\| *)?\n$/, '').split('\n') : []
389
455
  };
390
456
 
391
- for (i = 0; i < item.align.length; i++) {
392
- if (/^ *-+: *$/.test(item.align[i])) {
393
- item.align[i] = 'right';
394
- } else if (/^ *:-+: *$/.test(item.align[i])) {
395
- item.align[i] = 'center';
396
- } else if (/^ *:-+ *$/.test(item.align[i])) {
397
- item.align[i] = 'left';
398
- } else {
399
- 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
+ }
400
470
  }
401
- }
402
471
 
403
- for (i = 0; i < item.cells.length; i++) {
404
- item.cells[i] = item.cells[i]
405
- .replace(/^ *\| *| *\| *$/g, '')
406
- .split(/ *\| */);
407
- }
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
+ }
408
477
 
409
- this.tokens.push(item);
478
+ this.tokens.push(item);
410
479
 
411
- continue;
480
+ continue;
481
+ }
412
482
  }
413
483
 
414
484
  // lheading
@@ -458,39 +528,54 @@ Lexer.prototype.token = function(src, top) {
458
528
  */
459
529
 
460
530
  var inline = {
461
- escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
531
+ escape: /^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,
462
532
  autolink: /^<(scheme:[^\s\x00-\x1f<>]*|email)>/,
463
533
  url: noop,
464
- tag: /^<!--[\s\S]*?-->|^<\/?[a-zA-Z0-9\-]+(?:"[^"]*"|'[^']*'|\s[^<'">\/\s]*)*?\/?>/,
465
- link: /^!?\[(inside)\]\(href\)/,
466
- reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
467
- nolink: /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]])*)\]/,
468
- strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
469
- em: /^_([^\s_](?:[^_]|__)+?[^\s_])_\b|^\*((?:\*\*|[^*])+?)\*(?!\*)/,
470
- code: /^(`+)\s*([\s\S]*?[^`]?)\s*\1(?!`)/,
471
- 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])\*(?!\*)/,
545
+ code: /^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,
546
+ br: /^( {2,}|\\)\n(?!\s*$)/,
472
547
  del: noop,
473
- text: /^[\s\S]+?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
548
+ text: /^(`+|[^`])[\s\S]*?(?=[\\<!\[`*]|\b_| {2,}\n|$)/
474
549
  };
475
550
 
551
+ inline._escapes = /\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/g;
552
+
476
553
  inline._scheme = /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/;
477
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])?)+(?![-_])/;
478
-
479
555
  inline.autolink = edit(inline.autolink)
480
556
  .replace('scheme', inline._scheme)
481
557
  .replace('email', inline._email)
482
- .getRegex()
558
+ .getRegex();
483
559
 
484
- inline._inside = /(?:\[[^\[\]]*\]|\\[\[\]]|[^\[\]]|\](?=[^\[]*\]))*/;
485
- 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 = /"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/;
486
570
 
487
571
  inline.link = edit(inline.link)
488
- .replace('inside', inline._inside)
572
+ .replace('label', inline._label)
489
573
  .replace('href', inline._href)
574
+ .replace('title', inline._title)
490
575
  .getRegex();
491
576
 
492
577
  inline.reflink = edit(inline.reflink)
493
- .replace('inside', inline._inside)
578
+ .replace('label', inline._label)
494
579
  .getRegex();
495
580
 
496
581
  /**
@@ -505,7 +590,13 @@ inline.normal = merge({}, inline);
505
590
 
506
591
  inline.pedantic = merge({}, inline.normal, {
507
592
  strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
508
- 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()
509
600
  });
510
601
 
511
602
  /**
@@ -514,17 +605,19 @@ inline.pedantic = merge({}, inline.normal, {
514
605
 
515
606
  inline.gfm = merge({}, inline.normal, {
516
607
  escape: edit(inline.escape).replace('])', '~|])').getRegex(),
517
- url: edit(/^((?:ftp|https?):\/\/|www\.)(?:[a-zA-Z0-9\-]+\.?)+[^\s<]*|^email/)
518
- .replace('email', inline._email)
519
- .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/,
520
610
  _backpedal: /(?:[^?!.,:;*_~()&]+|\([^)]*\)|&(?![a-zA-Z0-9]+;$)|[?!.,:;*_~)]+(?!$))+/,
521
- del: /^~~(?=\S)([\s\S]*?\S)~~/,
611
+ del: /^~+(?=\S)([\s\S]*?\S)~+/,
522
612
  text: edit(inline.text)
523
613
  .replace(']|', '~]|')
524
- .replace('|', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|')
614
+ .replace('|$', '|https?://|ftp://|www\\.|[a-zA-Z0-9.!#$%&\'*+/=?^_`{\\|}~-]+@|$')
525
615
  .getRegex()
526
616
  });
527
617
 
618
+ inline.gfm.url = edit(inline.gfm.url)
619
+ .replace('email', inline.gfm._extended_email)
620
+ .getRegex();
528
621
  /**
529
622
  * GFM + Line Breaks Inline Grammar
530
623
  */
@@ -549,14 +642,14 @@ function InlineLexer(links, options) {
549
642
  throw new Error('Tokens array requires a `links` property.');
550
643
  }
551
644
 
552
- if (this.options.gfm) {
645
+ if (this.options.pedantic) {
646
+ this.rules = inline.pedantic;
647
+ } else if (this.options.gfm) {
553
648
  if (this.options.breaks) {
554
649
  this.rules = inline.breaks;
555
650
  } else {
556
651
  this.rules = inline.gfm;
557
652
  }
558
- } else if (this.options.pedantic) {
559
- this.rules = inline.pedantic;
560
653
  }
561
654
  }
562
655
 
@@ -584,7 +677,9 @@ InlineLexer.prototype.output = function(src) {
584
677
  link,
585
678
  text,
586
679
  href,
587
- cap;
680
+ title,
681
+ cap,
682
+ prevCapZero;
588
683
 
589
684
  while (src) {
590
685
  // escape
@@ -610,12 +705,15 @@ InlineLexer.prototype.output = function(src) {
610
705
 
611
706
  // url (gfm)
612
707
  if (!this.inLink && (cap = this.rules.url.exec(src))) {
613
- cap[0] = this.rules._backpedal.exec(cap[0])[0];
614
- src = src.substring(cap[0].length);
615
708
  if (cap[2] === '@') {
616
709
  text = escape(cap[0]);
617
710
  href = 'mailto:' + text;
618
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]);
619
717
  text = escape(cap[0]);
620
718
  if (cap[1] === 'www.') {
621
719
  href = 'http://' + text;
@@ -623,6 +721,7 @@ InlineLexer.prototype.output = function(src) {
623
721
  href = text;
624
722
  }
625
723
  }
724
+ src = src.substring(cap[0].length);
626
725
  out += this.renderer.link(href, null, text);
627
726
  continue;
628
727
  }
@@ -634,6 +733,12 @@ InlineLexer.prototype.output = function(src) {
634
733
  } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
635
734
  this.inLink = false;
636
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
+
637
742
  src = src.substring(cap[0].length);
638
743
  out += this.options.sanitize
639
744
  ? this.options.sanitizer
@@ -647,9 +752,23 @@ InlineLexer.prototype.output = function(src) {
647
752
  if (cap = this.rules.link.exec(src)) {
648
753
  src = src.substring(cap[0].length);
649
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');
650
769
  out += this.outputLink(cap, {
651
- href: cap[2],
652
- title: cap[3]
770
+ href: InlineLexer.escapes(href),
771
+ title: InlineLexer.escapes(title)
653
772
  });
654
773
  this.inLink = false;
655
774
  continue;
@@ -675,14 +794,14 @@ InlineLexer.prototype.output = function(src) {
675
794
  // strong
676
795
  if (cap = this.rules.strong.exec(src)) {
677
796
  src = src.substring(cap[0].length);
678
- 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]));
679
798
  continue;
680
799
  }
681
800
 
682
801
  // em
683
802
  if (cap = this.rules.em.exec(src)) {
684
803
  src = src.substring(cap[0].length);
685
- 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]));
686
805
  continue;
687
806
  }
688
807
 
@@ -710,7 +829,11 @@ InlineLexer.prototype.output = function(src) {
710
829
  // text
711
830
  if (cap = this.rules.text.exec(src)) {
712
831
  src = src.substring(cap[0].length);
713
- 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
+ }
714
837
  continue;
715
838
  }
716
839
 
@@ -722,12 +845,16 @@ InlineLexer.prototype.output = function(src) {
722
845
  return out;
723
846
  };
724
847
 
848
+ InlineLexer.escapes = function(text) {
849
+ return text ? text.replace(InlineLexer.rules._escapes, '$1') : text;
850
+ }
851
+
725
852
  /**
726
853
  * Compile Link
727
854
  */
728
855
 
729
856
  InlineLexer.prototype.outputLink = function(cap, link) {
730
- var href = escape(link.href),
857
+ var href = link.href,
731
858
  title = link.title ? escape(link.title) : null;
732
859
 
733
860
  return cap[0].charAt(0) !== '!'
@@ -785,7 +912,7 @@ InlineLexer.prototype.mangle = function(text) {
785
912
  */
786
913
 
787
914
  function Renderer(options) {
788
- this.options = options || {};
915
+ this.options = options || marked.defaults;
789
916
  }
790
917
 
791
918
  Renderer.prototype.code = function(code, lang, escaped) {
@@ -800,7 +927,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
800
927
  if (!lang) {
801
928
  return '<pre><code>'
802
929
  + (escaped ? code : escape(code, true))
803
- + '\n</code></pre>';
930
+ + '</code></pre>';
804
931
  }
805
932
 
806
933
  return '<pre><code class="'
@@ -808,7 +935,7 @@ Renderer.prototype.code = function(code, lang, escaped) {
808
935
  + escape(lang, true)
809
936
  + '">'
810
937
  + (escaped ? code : escape(code, true))
811
- + '\n</code></pre>\n';
938
+ + '</code></pre>\n';
812
939
  };
813
940
 
814
941
  Renderer.prototype.blockquote = function(quote) {
@@ -820,43 +947,56 @@ Renderer.prototype.html = function(html) {
820
947
  };
821
948
 
822
949
  Renderer.prototype.heading = function(text, level, raw) {
823
- return '<h'
824
- + level
825
- + ' id="'
826
- + this.options.headerPrefix
827
- + raw.toLowerCase().replace(/[^\w]+/g, '-')
828
- + '">'
829
- + text
830
- + '</h'
831
- + level
832
- + '>\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';
833
964
  };
834
965
 
835
966
  Renderer.prototype.hr = function() {
836
967
  return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
837
968
  };
838
969
 
839
- Renderer.prototype.list = function(body, ordered) {
840
- var type = ordered ? 'ol' : 'ul';
841
- return '<' + type + '>\n' + body + '</' + type + '>\n';
970
+ Renderer.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';
842
974
  };
843
975
 
844
976
  Renderer.prototype.listitem = function(text) {
845
977
  return '<li>' + text + '</li>\n';
846
978
  };
847
979
 
980
+ Renderer.prototype.checkbox = function(checked) {
981
+ return '<input '
982
+ + (checked ? 'checked="" ' : '')
983
+ + 'disabled="" type="checkbox"'
984
+ + (this.options.xhtml ? ' /' : '')
985
+ + '> ';
986
+ }
987
+
848
988
  Renderer.prototype.paragraph = function(text) {
849
989
  return '<p>' + text + '</p>\n';
850
990
  };
851
991
 
852
992
  Renderer.prototype.table = function(header, body) {
993
+ if (body) body = '<tbody>' + body + '</tbody>';
994
+
853
995
  return '<table>\n'
854
996
  + '<thead>\n'
855
997
  + header
856
998
  + '</thead>\n'
857
- + '<tbody>\n'
858
999
  + body
859
- + '</tbody>\n'
860
1000
  + '</table>\n';
861
1001
  };
862
1002
 
@@ -867,7 +1007,7 @@ Renderer.prototype.tablerow = function(content) {
867
1007
  Renderer.prototype.tablecell = function(content, flags) {
868
1008
  var type = flags.header ? 'th' : 'td';
869
1009
  var tag = flags.align
870
- ? '<' + type + ' style="text-align:' + flags.align + '">'
1010
+ ? '<' + type + ' align="' + flags.align + '">'
871
1011
  : '<' + type + '>';
872
1012
  return tag + content + '</' + type + '>\n';
873
1013
  };
@@ -909,7 +1049,12 @@ Renderer.prototype.link = function(href, title, text) {
909
1049
  if (this.options.baseUrl && !originIndependentUrl.test(href)) {
910
1050
  href = resolveUrl(this.options.baseUrl, href);
911
1051
  }
912
- var out = '<a href="' + href + '"';
1052
+ try {
1053
+ href = encodeURI(href).replace(/%25/g, '%');
1054
+ } catch (e) {
1055
+ return text;
1056
+ }
1057
+ var out = '<a href="' + escape(href) + '"';
913
1058
  if (title) {
914
1059
  out += ' title="' + title + '"';
915
1060
  }
@@ -1099,39 +1244,34 @@ Parser.prototype.tok = function() {
1099
1244
  }
1100
1245
  case 'list_start': {
1101
1246
  body = '';
1102
- var ordered = this.token.ordered;
1247
+ var ordered = this.token.ordered,
1248
+ start = this.token.start;
1103
1249
 
1104
1250
  while (this.next().type !== 'list_end') {
1105
1251
  body += this.tok();
1106
1252
  }
1107
1253
 
1108
- return this.renderer.list(body, ordered);
1254
+ return this.renderer.list(body, ordered, start);
1109
1255
  }
1110
1256
  case 'list_item_start': {
1111
1257
  body = '';
1258
+ var loose = this.token.loose;
1112
1259
 
1113
- while (this.next().type !== 'list_item_end') {
1114
- body += this.token.type === 'text'
1115
- ? this.parseText()
1116
- : this.tok();
1260
+ if (this.token.task) {
1261
+ body += this.renderer.checkbox(this.token.checked);
1117
1262
  }
1118
1263
 
1119
- return this.renderer.listitem(body);
1120
- }
1121
- case 'loose_item_start': {
1122
- body = '';
1123
-
1124
1264
  while (this.next().type !== 'list_item_end') {
1125
- body += this.tok();
1265
+ body += !loose && this.token.type === 'text'
1266
+ ? this.parseText()
1267
+ : this.tok();
1126
1268
  }
1127
1269
 
1128
1270
  return this.renderer.listitem(body);
1129
1271
  }
1130
1272
  case 'html': {
1131
- var html = !this.token.pre && !this.options.pedantic
1132
- ? this.inline.output(this.token.text)
1133
- : this.token.text;
1134
- return this.renderer.html(html);
1273
+ // TODO parse inline content if parameter markdown=1
1274
+ return this.renderer.html(this.token.text);
1135
1275
  }
1136
1276
  case 'paragraph': {
1137
1277
  return this.renderer.paragraph(this.inline.output(this.token.text));
@@ -1147,14 +1287,32 @@ Parser.prototype.tok = function() {
1147
1287
  */
1148
1288
 
1149
1289
  function escape(html, encode) {
1150
- return html
1151
- .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
1152
- .replace(/</g, '&lt;')
1153
- .replace(/>/g, '&gt;')
1154
- .replace(/"/g, '&quot;')
1155
- .replace(/'/g, '&#39;');
1290
+ if (encode) {
1291
+ if (escape.escapeTest.test(html)) {
1292
+ return html.replace(escape.escapeReplace, function (ch) { return escape.replacements[ch] });
1293
+ }
1294
+ } else {
1295
+ if (escape.escapeTestNoEncode.test(html)) {
1296
+ return html.replace(escape.escapeReplaceNoEncode, function (ch) { return escape.replacements[ch] });
1297
+ }
1298
+ }
1299
+
1300
+ return html;
1156
1301
  }
1157
1302
 
1303
+ escape.escapeTest = /[&<>"']/;
1304
+ escape.escapeReplace = /[&<>"']/g;
1305
+ escape.replacements = {
1306
+ '&': '&amp;',
1307
+ '<': '&lt;',
1308
+ '>': '&gt;',
1309
+ '"': '&quot;',
1310
+ "'": '&#39;'
1311
+ };
1312
+
1313
+ escape.escapeTestNoEncode = /[<>"']|&(?!#?\w+;)/;
1314
+ escape.escapeReplaceNoEncode = /[<>"']|&(?!#?\w+;)/g;
1315
+
1158
1316
  function unescape(html) {
1159
1317
  // explicitly match decimal, hex, and named HTML entities
1160
1318
  return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/ig, function(_, n) {
@@ -1170,7 +1328,7 @@ function unescape(html) {
1170
1328
  }
1171
1329
 
1172
1330
  function edit(regex, opt) {
1173
- regex = regex.source;
1331
+ regex = regex.source || regex;
1174
1332
  opt = opt || '';
1175
1333
  return {
1176
1334
  replace: function(name, val) {
@@ -1193,7 +1351,7 @@ function resolveUrl(base, href) {
1193
1351
  if (/^[^:]+:\/*[^/]*$/.test(base)) {
1194
1352
  baseUrls[' ' + base] = base + '/';
1195
1353
  } else {
1196
- baseUrls[' ' + base] = base.replace(/[^/]*$/, '');
1354
+ baseUrls[' ' + base] = rtrim(base, '/', true);
1197
1355
  }
1198
1356
  }
1199
1357
  base = baseUrls[' ' + base];
@@ -1229,6 +1387,64 @@ function merge(obj) {
1229
1387
  return obj;
1230
1388
  }
1231
1389
 
1390
+ function splitCells(tableRow, count) {
1391
+ // ensure that every cell-delimiting pipe has a space
1392
+ // before it to distinguish it from an escaped pipe
1393
+ var row = tableRow.replace(/\|/g, function (match, offset, str) {
1394
+ var escaped = false,
1395
+ curr = offset;
1396
+ while (--curr >= 0 && str[curr] === '\\') escaped = !escaped;
1397
+ if (escaped) {
1398
+ // odd number of slashes means | is escaped
1399
+ // so we leave it alone
1400
+ return '|';
1401
+ } else {
1402
+ // add space before unescaped |
1403
+ return ' |';
1404
+ }
1405
+ }),
1406
+ cells = row.split(/ \|/),
1407
+ i = 0;
1408
+
1409
+ if (cells.length > count) {
1410
+ cells.splice(count);
1411
+ } else {
1412
+ while (cells.length < count) cells.push('');
1413
+ }
1414
+
1415
+ for (; i < cells.length; i++) {
1416
+ // leading or trailing whitespace is ignored per the gfm spec
1417
+ cells[i] = cells[i].trim().replace(/\\\|/g, '|');
1418
+ }
1419
+ return cells;
1420
+ }
1421
+
1422
+ // Remove trailing 'c's. Equivalent to str.replace(/c*$/, '').
1423
+ // /c*$/ is vulnerable to REDOS.
1424
+ // invert: Remove suffix of non-c chars instead. Default falsey.
1425
+ function rtrim(str, c, invert) {
1426
+ if (str.length === 0) {
1427
+ return '';
1428
+ }
1429
+
1430
+ // Length of suffix matching the invert condition.
1431
+ var suffLen = 0;
1432
+
1433
+ // Step left until we fail to match the invert condition.
1434
+ while (suffLen < str.length) {
1435
+ var currChar = str.charAt(str.length - suffLen - 1);
1436
+ if (currChar === c && !invert) {
1437
+ suffLen++;
1438
+ } else if (currChar !== c && invert) {
1439
+ suffLen++;
1440
+ } else {
1441
+ break;
1442
+ }
1443
+ }
1444
+
1445
+ return str.substr(0, str.length - suffLen);
1446
+ }
1447
+
1232
1448
  /**
1233
1449
  * Marked
1234
1450
  */
@@ -1336,24 +1552,29 @@ marked.setOptions = function(opt) {
1336
1552
  return marked;
1337
1553
  };
1338
1554
 
1339
- marked.defaults = {
1340
- gfm: true,
1341
- tables: true,
1342
- breaks: false,
1343
- pedantic: false,
1344
- sanitize: false,
1345
- sanitizer: null,
1346
- mangle: true,
1347
- smartLists: false,
1348
- silent: false,
1349
- highlight: null,
1350
- langPrefix: 'lang-',
1351
- smartypants: false,
1352
- headerPrefix: '',
1353
- renderer: new Renderer(),
1354
- xhtml: false,
1355
- baseUrl: null
1356
- };
1555
+ marked.getDefaults = function () {
1556
+ return {
1557
+ baseUrl: null,
1558
+ breaks: false,
1559
+ gfm: true,
1560
+ headerIds: true,
1561
+ headerPrefix: '',
1562
+ highlight: null,
1563
+ langPrefix: 'language-',
1564
+ mangle: true,
1565
+ pedantic: false,
1566
+ renderer: new Renderer(),
1567
+ sanitize: false,
1568
+ sanitizer: null,
1569
+ silent: false,
1570
+ smartLists: false,
1571
+ smartypants: false,
1572
+ tables: true,
1573
+ xhtml: false
1574
+ };
1575
+ }
1576
+
1577
+ marked.defaults = marked.getDefaults();
1357
1578
 
1358
1579
  /**
1359
1580
  * Expose