marked 0.2.4 → 0.2.8

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,7 @@
1
1
  /**
2
- * marked - A markdown parser (https://github.com/chjj/marked)
3
- * Copyright (c) 2011-2012, Christopher Jeffrey. (MIT Licensed)
2
+ * marked - a markdown parser
3
+ * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
4
+ * https://github.com/chjj/marked
4
5
  */
5
6
 
6
7
  ;(function() {
@@ -13,83 +14,138 @@ var block = {
13
14
  newline: /^\n+/,
14
15
  code: /^( {4}[^\n]+\n*)+/,
15
16
  fences: noop,
16
- hr: /^( *[\-*_]){3,} *(?:\n+|$)/,
17
+ hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17
18
  heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19
+ nptable: noop,
18
20
  lheading: /^([^\n]+)\n *(=|-){3,} *\n*/,
19
21
  blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
20
- list: /^( *)([*+-]|\d+\.) [^\0]+?(?:\n{2,}(?! )(?!\1bullet)\n*|\s*$)/,
22
+ list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
21
23
  html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
22
- def: /^ *\[([^\]]+)\]: *([^\s]+)(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
23
- paragraph: /^([^\n]+\n?(?!body))+\n*/,
24
+ def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
25
+ table: noop,
26
+ paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
24
27
  text: /^[^\n]+/
25
28
  };
26
29
 
30
+ block.bullet = /(?:[*+-]|\d+\.)/;
31
+ block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
32
+ block.item = replace(block.item, 'gm')
33
+ (/bull/g, block.bullet)
34
+ ();
35
+
27
36
  block.list = replace(block.list)
28
- ('bullet', /(?:[*+-](?!(?: *[-*]){2,})|\d+\.)/)
37
+ (/bull/g, block.bullet)
38
+ ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
29
39
  ();
30
40
 
41
+ block._tag = '(?!(?:'
42
+ + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
43
+ + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
44
+ + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
45
+
31
46
  block.html = replace(block.html)
32
- ('comment', /<!--[^\0]*?-->/)
33
- ('closed', /<(tag)[^\0]+?<\/\1>/)
34
- ('closing', /<tag(?!:\/|@)\b(?:"[^"]*"|'[^']*'|[^'">])*?>/)
35
- (/tag/g, tag())
47
+ ('comment', /<!--[\s\S]*?-->/)
48
+ ('closed', /<(tag)[\s\S]+?<\/\1>/)
49
+ ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
50
+ (/tag/g, block._tag)
36
51
  ();
37
52
 
38
- block.paragraph = (function() {
39
- var paragraph = block.paragraph.source
40
- , body = [];
41
-
42
- (function push(rule) {
43
- rule = block[rule] ? block[rule].source : rule;
44
- body.push(rule.replace(/(^|[^\[])\^/g, '$1'));
45
- return push;
46
- })
47
- ('hr')
48
- ('heading')
49
- ('lheading')
50
- ('blockquote')
51
- ('<' + tag())
52
- ('def');
53
-
54
- return new
55
- RegExp(paragraph.replace('body', body.join('|')));
56
- })();
57
-
58
- block.normal = {
59
- fences: block.fences,
60
- paragraph: block.paragraph
61
- };
53
+ block.paragraph = replace(block.paragraph)
54
+ ('hr', block.hr)
55
+ ('heading', block.heading)
56
+ ('lheading', block.lheading)
57
+ ('blockquote', block.blockquote)
58
+ ('tag', '<' + block._tag)
59
+ ('def', block.def)
60
+ ();
61
+
62
+ /**
63
+ * Normal Block Grammar
64
+ */
62
65
 
63
- block.gfm = {
64
- fences: /^ *``` *(\w+)? *\n([^\0]+?)\s*``` *(?:\n+|$)/,
66
+ block.normal = merge({}, block);
67
+
68
+ /**
69
+ * GFM Block Grammar
70
+ */
71
+
72
+ block.gfm = merge({}, block.normal, {
73
+ fences: /^ *(`{3,}|~{3,}) *(\w+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
65
74
  paragraph: /^/
66
- };
75
+ });
67
76
 
68
77
  block.gfm.paragraph = replace(block.paragraph)
69
- ('(?!', '(?!' + block.gfm.fences.source.replace(/(^|[^\[])\^/g, '$1') + '|')
78
+ ('(?!', '(?!' + block.gfm.fences.source.replace('\\1', '\\2') + '|')
70
79
  ();
71
80
 
81
+ /**
82
+ * GFM + Tables Block Grammar
83
+ */
84
+
85
+ block.tables = merge({}, block.gfm, {
86
+ nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
87
+ table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
88
+ });
89
+
72
90
  /**
73
91
  * Block Lexer
74
92
  */
75
93
 
76
- block.lexer = function(src) {
77
- var tokens = [];
94
+ function Lexer(options) {
95
+ this.tokens = [];
96
+ this.tokens.links = {};
97
+ this.options = options || marked.defaults;
98
+ this.rules = block.normal;
99
+
100
+ if (this.options.gfm) {
101
+ if (this.options.tables) {
102
+ this.rules = block.tables;
103
+ } else {
104
+ this.rules = block.gfm;
105
+ }
106
+ }
107
+ }
78
108
 
79
- tokens.links = {};
109
+ /**
110
+ * Expose Block Rules
111
+ */
112
+
113
+ Lexer.rules = block;
114
+
115
+ /**
116
+ * Static Lex Method
117
+ */
118
+
119
+ Lexer.lex = function(src, options) {
120
+ var lexer = new Lexer(options);
121
+ return lexer.lex(src);
122
+ };
80
123
 
124
+ /**
125
+ * Preprocessing
126
+ */
127
+
128
+ Lexer.prototype.lex = function(src) {
81
129
  src = src
82
130
  .replace(/\r\n|\r/g, '\n')
83
- .replace(/\t/g, ' ');
131
+ .replace(/\t/g, ' ')
132
+ .replace(/\u00a0/g, ' ')
133
+ .replace(/\u2424/g, '\n');
84
134
 
85
- return block.token(src, tokens, true);
135
+ return this.token(src, true);
86
136
  };
87
137
 
88
- block.token = function(src, tokens, top) {
138
+ /**
139
+ * Lexing
140
+ */
141
+
142
+ Lexer.prototype.token = function(src, top) {
89
143
  var src = src.replace(/^ +$/gm, '')
90
144
  , next
91
145
  , loose
92
146
  , cap
147
+ , bull
148
+ , b
93
149
  , item
94
150
  , space
95
151
  , i
@@ -97,22 +153,22 @@ block.token = function(src, tokens, top) {
97
153
 
98
154
  while (src) {
99
155
  // newline
100
- if (cap = block.newline.exec(src)) {
156
+ if (cap = this.rules.newline.exec(src)) {
101
157
  src = src.substring(cap[0].length);
102
158
  if (cap[0].length > 1) {
103
- tokens.push({
159
+ this.tokens.push({
104
160
  type: 'space'
105
161
  });
106
162
  }
107
163
  }
108
164
 
109
165
  // code
110
- if (cap = block.code.exec(src)) {
166
+ if (cap = this.rules.code.exec(src)) {
111
167
  src = src.substring(cap[0].length);
112
168
  cap = cap[0].replace(/^ {4}/gm, '');
113
- tokens.push({
169
+ this.tokens.push({
114
170
  type: 'code',
115
- text: !options.pedantic
171
+ text: !this.options.pedantic
116
172
  ? cap.replace(/\n+$/, '')
117
173
  : cap
118
174
  });
@@ -120,20 +176,20 @@ block.token = function(src, tokens, top) {
120
176
  }
121
177
 
122
178
  // fences (gfm)
123
- if (cap = block.fences.exec(src)) {
179
+ if (cap = this.rules.fences.exec(src)) {
124
180
  src = src.substring(cap[0].length);
125
- tokens.push({
181
+ this.tokens.push({
126
182
  type: 'code',
127
- lang: cap[1],
128
- text: cap[2]
183
+ lang: cap[2],
184
+ text: cap[3]
129
185
  });
130
186
  continue;
131
187
  }
132
188
 
133
189
  // heading
134
- if (cap = block.heading.exec(src)) {
190
+ if (cap = this.rules.heading.exec(src)) {
135
191
  src = src.substring(cap[0].length);
136
- tokens.push({
192
+ this.tokens.push({
137
193
  type: 'heading',
138
194
  depth: cap[1].length,
139
195
  text: cap[2]
@@ -141,10 +197,42 @@ block.token = function(src, tokens, top) {
141
197
  continue;
142
198
  }
143
199
 
200
+ // table no leading pipe (gfm)
201
+ if (top && (cap = this.rules.nptable.exec(src))) {
202
+ src = src.substring(cap[0].length);
203
+
204
+ item = {
205
+ type: 'table',
206
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
207
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
208
+ cells: cap[3].replace(/\n$/, '').split('\n')
209
+ };
210
+
211
+ for (i = 0; i < item.align.length; i++) {
212
+ if (/^ *-+: *$/.test(item.align[i])) {
213
+ item.align[i] = 'right';
214
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
215
+ item.align[i] = 'center';
216
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
217
+ item.align[i] = 'left';
218
+ } else {
219
+ item.align[i] = null;
220
+ }
221
+ }
222
+
223
+ for (i = 0; i < item.cells.length; i++) {
224
+ item.cells[i] = item.cells[i].split(/ *\| */);
225
+ }
226
+
227
+ this.tokens.push(item);
228
+
229
+ continue;
230
+ }
231
+
144
232
  // lheading
145
- if (cap = block.lheading.exec(src)) {
233
+ if (cap = this.rules.lheading.exec(src)) {
146
234
  src = src.substring(cap[0].length);
147
- tokens.push({
235
+ this.tokens.push({
148
236
  type: 'heading',
149
237
  depth: cap[2] === '=' ? 1 : 2,
150
238
  text: cap[1]
@@ -153,19 +241,19 @@ block.token = function(src, tokens, top) {
153
241
  }
154
242
 
155
243
  // hr
156
- if (cap = block.hr.exec(src)) {
244
+ if (cap = this.rules.hr.exec(src)) {
157
245
  src = src.substring(cap[0].length);
158
- tokens.push({
246
+ this.tokens.push({
159
247
  type: 'hr'
160
248
  });
161
249
  continue;
162
250
  }
163
251
 
164
252
  // blockquote
165
- if (cap = block.blockquote.exec(src)) {
253
+ if (cap = this.rules.blockquote.exec(src)) {
166
254
  src = src.substring(cap[0].length);
167
255
 
168
- tokens.push({
256
+ this.tokens.push({
169
257
  type: 'blockquote_start'
170
258
  });
171
259
 
@@ -174,9 +262,9 @@ block.token = function(src, tokens, top) {
174
262
  // Pass `top` to keep the current
175
263
  // "toplevel" state. This is exactly
176
264
  // how markdown.pl works.
177
- block.token(cap, tokens, top);
265
+ this.token(cap, top);
178
266
 
179
- tokens.push({
267
+ this.tokens.push({
180
268
  type: 'blockquote_end'
181
269
  });
182
270
 
@@ -184,18 +272,21 @@ block.token = function(src, tokens, top) {
184
272
  }
185
273
 
186
274
  // list
187
- if (cap = block.list.exec(src)) {
275
+ if (cap = this.rules.list.exec(src)) {
188
276
  src = src.substring(cap[0].length);
189
277
 
190
- tokens.push({
278
+ this.tokens.push({
191
279
  type: 'list_start',
192
280
  ordered: isFinite(cap[2])
193
281
  });
194
282
 
195
283
  // Get each top-level item.
196
- cap = cap[0].match(
197
- /^( *)([*+-]|\d+\.) [^\n]*(?:\n(?!\1(?:[*+-]|\d+\.) )[^\n]*)*/gm
198
- );
284
+ cap = cap[0].match(this.rules.item);
285
+
286
+ // Get bullet.
287
+ if (this.options.smartLists) {
288
+ bull = block.bullet.exec(cap[0])[0];
289
+ }
199
290
 
200
291
  next = false;
201
292
  l = cap.length;
@@ -213,11 +304,21 @@ block.token = function(src, tokens, top) {
213
304
  // list item contains. Hacky.
214
305
  if (~item.indexOf('\n ')) {
215
306
  space -= item.length;
216
- item = !options.pedantic
307
+ item = !this.options.pedantic
217
308
  ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
218
309
  : item.replace(/^ {1,4}/gm, '');
219
310
  }
220
311
 
312
+ // Determine whether the next list item belongs here.
313
+ // Backpedal if it does not belong in this list.
314
+ if (this.options.smartLists && i !== l - 1) {
315
+ b = block.bullet.exec(cap[i+1])[0];
316
+ if (bull !== b && !(bull[1] === '.' && b[1] === '.')) {
317
+ src = cap.slice(i + 1).join('\n') + src;
318
+ i = l - 1;
319
+ }
320
+ }
321
+
221
322
  // Determine whether item is loose or not.
222
323
  // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
223
324
  // for discount behavior.
@@ -227,21 +328,21 @@ block.token = function(src, tokens, top) {
227
328
  if (!loose) loose = next;
228
329
  }
229
330
 
230
- tokens.push({
331
+ this.tokens.push({
231
332
  type: loose
232
333
  ? 'loose_item_start'
233
334
  : 'list_item_start'
234
335
  });
235
336
 
236
337
  // Recurse.
237
- block.token(item, tokens);
338
+ this.token(item, false);
238
339
 
239
- tokens.push({
340
+ this.tokens.push({
240
341
  type: 'list_item_end'
241
342
  });
242
343
  }
243
344
 
244
- tokens.push({
345
+ this.tokens.push({
245
346
  type: 'list_end'
246
347
  });
247
348
 
@@ -249,10 +350,12 @@ block.token = function(src, tokens, top) {
249
350
  }
250
351
 
251
352
  // html
252
- if (cap = block.html.exec(src)) {
353
+ if (cap = this.rules.html.exec(src)) {
253
354
  src = src.substring(cap[0].length);
254
- tokens.push({
255
- type: 'html',
355
+ this.tokens.push({
356
+ type: this.options.sanitize
357
+ ? 'paragraph'
358
+ : 'html',
256
359
  pre: cap[1] === 'pre',
257
360
  text: cap[0]
258
361
  });
@@ -260,95 +363,197 @@ block.token = function(src, tokens, top) {
260
363
  }
261
364
 
262
365
  // def
263
- if (top && (cap = block.def.exec(src))) {
366
+ if (top && (cap = this.rules.def.exec(src))) {
264
367
  src = src.substring(cap[0].length);
265
- tokens.links[cap[1].toLowerCase()] = {
368
+ this.tokens.links[cap[1].toLowerCase()] = {
266
369
  href: cap[2],
267
370
  title: cap[3]
268
371
  };
269
372
  continue;
270
373
  }
271
374
 
375
+ // table (gfm)
376
+ if (top && (cap = this.rules.table.exec(src))) {
377
+ src = src.substring(cap[0].length);
378
+
379
+ item = {
380
+ type: 'table',
381
+ header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
382
+ align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
383
+ cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
384
+ };
385
+
386
+ for (i = 0; i < item.align.length; i++) {
387
+ if (/^ *-+: *$/.test(item.align[i])) {
388
+ item.align[i] = 'right';
389
+ } else if (/^ *:-+: *$/.test(item.align[i])) {
390
+ item.align[i] = 'center';
391
+ } else if (/^ *:-+ *$/.test(item.align[i])) {
392
+ item.align[i] = 'left';
393
+ } else {
394
+ item.align[i] = null;
395
+ }
396
+ }
397
+
398
+ for (i = 0; i < item.cells.length; i++) {
399
+ item.cells[i] = item.cells[i]
400
+ .replace(/^ *\| *| *\| *$/g, '')
401
+ .split(/ *\| */);
402
+ }
403
+
404
+ this.tokens.push(item);
405
+
406
+ continue;
407
+ }
408
+
272
409
  // top-level paragraph
273
- if (top && (cap = block.paragraph.exec(src))) {
410
+ if (top && (cap = this.rules.paragraph.exec(src))) {
274
411
  src = src.substring(cap[0].length);
275
- tokens.push({
412
+ this.tokens.push({
276
413
  type: 'paragraph',
277
- text: cap[0]
414
+ text: cap[1][cap[1].length-1] === '\n'
415
+ ? cap[1].slice(0, -1)
416
+ : cap[1]
278
417
  });
279
418
  continue;
280
419
  }
281
420
 
282
421
  // text
283
- if (cap = block.text.exec(src)) {
422
+ if (cap = this.rules.text.exec(src)) {
284
423
  // Top-level should never reach here.
285
424
  src = src.substring(cap[0].length);
286
- tokens.push({
425
+ this.tokens.push({
287
426
  type: 'text',
288
427
  text: cap[0]
289
428
  });
290
429
  continue;
291
430
  }
431
+
432
+ if (src) {
433
+ throw new
434
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
435
+ }
292
436
  }
293
437
 
294
- return tokens;
438
+ return this.tokens;
295
439
  };
296
440
 
297
441
  /**
298
- * Inline Processing
442
+ * Inline-Level Grammar
299
443
  */
300
444
 
301
445
  var inline = {
302
446
  escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
303
447
  autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
304
448
  url: noop,
305
- tag: /^<!--[^\0]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
449
+ tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
306
450
  link: /^!?\[(inside)\]\(href\)/,
307
451
  reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
308
452
  nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
309
- strong: /^__([^\0]+?)__(?!_)|^\*\*([^\0]+?)\*\*(?!\*)/,
310
- em: /^\b_((?:__|[^\0])+?)_\b|^\*((?:\*\*|[^\0])+?)\*(?!\*)/,
311
- code: /^(`+)([^\0]*?[^`])\1(?!`)/,
453
+ strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
454
+ em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
455
+ code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
312
456
  br: /^ {2,}\n(?!\s*$)/,
313
- text: /^[^\0]+?(?=[\\<!\[_*`]| {2,}\n|$)/
457
+ del: noop,
458
+ text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
314
459
  };
315
460
 
316
- inline._linkInside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
317
- inline._linkHref = /\s*<?([^\s]*?)>?(?:\s+['"]([^\0]*?)['"])?\s*/;
461
+ inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
462
+ inline._href = /\s*<?([^\s]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
318
463
 
319
464
  inline.link = replace(inline.link)
320
- ('inside', inline._linkInside)
321
- ('href', inline._linkHref)
465
+ ('inside', inline._inside)
466
+ ('href', inline._href)
322
467
  ();
323
468
 
324
469
  inline.reflink = replace(inline.reflink)
325
- ('inside', inline._linkInside)
470
+ ('inside', inline._inside)
326
471
  ();
327
472
 
328
- inline.normal = {
329
- url: inline.url,
330
- strong: inline.strong,
331
- em: inline.em,
332
- text: inline.text
333
- };
473
+ /**
474
+ * Normal Inline Grammar
475
+ */
334
476
 
335
- inline.pedantic = {
336
- strong: /^__(?=\S)([^\0]*?\S)__(?!_)|^\*\*(?=\S)([^\0]*?\S)\*\*(?!\*)/,
337
- em: /^_(?=\S)([^\0]*?\S)_(?!_)|^\*(?=\S)([^\0]*?\S)\*(?!\*)/
338
- };
477
+ inline.normal = merge({}, inline);
478
+
479
+ /**
480
+ * Pedantic Inline Grammar
481
+ */
482
+
483
+ inline.pedantic = merge({}, inline.normal, {
484
+ strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
485
+ em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
486
+ });
487
+
488
+ /**
489
+ * GFM Inline Grammar
490
+ */
491
+
492
+ inline.gfm = merge({}, inline.normal, {
493
+ escape: replace(inline.escape)('])', '~|])')(),
494
+ url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
495
+ del: /^~~(?=\S)([\s\S]*?\S)~~/,
496
+ text: replace(inline.text)
497
+ (']|', '~]|')
498
+ ('|', '|https?://|')
499
+ ()
500
+ });
501
+
502
+ /**
503
+ * GFM + Line Breaks Inline Grammar
504
+ */
505
+
506
+ inline.breaks = merge({}, inline.gfm, {
507
+ br: replace(inline.br)('{2,}', '*')(),
508
+ text: replace(inline.gfm.text)('{2,}', '*')()
509
+ });
339
510
 
340
- inline.gfm = {
341
- url: /^(https?:\/\/[^\s]+[^.,:;"')\]\s])/,
342
- text: /^[^\0]+?(?=[\\<!\[_*`]|https?:\/\/| {2,}\n|$)/
511
+ /**
512
+ * Inline Lexer & Compiler
513
+ */
514
+
515
+ function InlineLexer(links, options) {
516
+ this.options = options || marked.defaults;
517
+ this.links = links;
518
+ this.rules = inline.normal;
519
+
520
+ if (!this.links) {
521
+ throw new
522
+ Error('Tokens array requires a `links` property.');
523
+ }
524
+
525
+ if (this.options.gfm) {
526
+ if (this.options.breaks) {
527
+ this.rules = inline.breaks;
528
+ } else {
529
+ this.rules = inline.gfm;
530
+ }
531
+ } else if (this.options.pedantic) {
532
+ this.rules = inline.pedantic;
533
+ }
534
+ }
535
+
536
+ /**
537
+ * Expose Inline Rules
538
+ */
539
+
540
+ InlineLexer.rules = inline;
541
+
542
+ /**
543
+ * Static Lexing/Compiling Method
544
+ */
545
+
546
+ InlineLexer.output = function(src, links, options) {
547
+ var inline = new InlineLexer(links, options);
548
+ return inline.output(src);
343
549
  };
344
550
 
345
551
  /**
346
- * Inline Lexer
552
+ * Lexing/Compiling
347
553
  */
348
554
 
349
- inline.lexer = function(src) {
555
+ InlineLexer.prototype.output = function(src) {
350
556
  var out = ''
351
- , links = tokens.links
352
557
  , link
353
558
  , text
354
559
  , href
@@ -356,20 +561,20 @@ inline.lexer = function(src) {
356
561
 
357
562
  while (src) {
358
563
  // escape
359
- if (cap = inline.escape.exec(src)) {
564
+ if (cap = this.rules.escape.exec(src)) {
360
565
  src = src.substring(cap[0].length);
361
566
  out += cap[1];
362
567
  continue;
363
568
  }
364
569
 
365
570
  // autolink
366
- if (cap = inline.autolink.exec(src)) {
571
+ if (cap = this.rules.autolink.exec(src)) {
367
572
  src = src.substring(cap[0].length);
368
573
  if (cap[2] === '@') {
369
574
  text = cap[1][6] === ':'
370
- ? mangle(cap[1].substring(7))
371
- : mangle(cap[1]);
372
- href = mangle('mailto:') + text;
575
+ ? this.mangle(cap[1].substring(7))
576
+ : this.mangle(cap[1]);
577
+ href = this.mangle('mailto:') + text;
373
578
  } else {
374
579
  text = escape(cap[1]);
375
580
  href = text;
@@ -383,7 +588,7 @@ inline.lexer = function(src) {
383
588
  }
384
589
 
385
590
  // url (gfm)
386
- if (cap = inline.url.exec(src)) {
591
+ if (cap = this.rules.url.exec(src)) {
387
592
  src = src.substring(cap[0].length);
388
593
  text = escape(cap[1]);
389
594
  href = text;
@@ -396,18 +601,18 @@ inline.lexer = function(src) {
396
601
  }
397
602
 
398
603
  // tag
399
- if (cap = inline.tag.exec(src)) {
604
+ if (cap = this.rules.tag.exec(src)) {
400
605
  src = src.substring(cap[0].length);
401
- out += options.sanitize
606
+ out += this.options.sanitize
402
607
  ? escape(cap[0])
403
608
  : cap[0];
404
609
  continue;
405
610
  }
406
611
 
407
612
  // link
408
- if (cap = inline.link.exec(src)) {
613
+ if (cap = this.rules.link.exec(src)) {
409
614
  src = src.substring(cap[0].length);
410
- out += outputLink(cap, {
615
+ out += this.outputLink(cap, {
411
616
  href: cap[2],
412
617
  title: cap[3]
413
618
  });
@@ -415,40 +620,40 @@ inline.lexer = function(src) {
415
620
  }
416
621
 
417
622
  // reflink, nolink
418
- if ((cap = inline.reflink.exec(src))
419
- || (cap = inline.nolink.exec(src))) {
623
+ if ((cap = this.rules.reflink.exec(src))
624
+ || (cap = this.rules.nolink.exec(src))) {
420
625
  src = src.substring(cap[0].length);
421
626
  link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
422
- link = links[link.toLowerCase()];
627
+ link = this.links[link.toLowerCase()];
423
628
  if (!link || !link.href) {
424
629
  out += cap[0][0];
425
630
  src = cap[0].substring(1) + src;
426
631
  continue;
427
632
  }
428
- out += outputLink(cap, link);
633
+ out += this.outputLink(cap, link);
429
634
  continue;
430
635
  }
431
636
 
432
637
  // strong
433
- if (cap = inline.strong.exec(src)) {
638
+ if (cap = this.rules.strong.exec(src)) {
434
639
  src = src.substring(cap[0].length);
435
640
  out += '<strong>'
436
- + inline.lexer(cap[2] || cap[1])
641
+ + this.output(cap[2] || cap[1])
437
642
  + '</strong>';
438
643
  continue;
439
644
  }
440
645
 
441
646
  // em
442
- if (cap = inline.em.exec(src)) {
647
+ if (cap = this.rules.em.exec(src)) {
443
648
  src = src.substring(cap[0].length);
444
649
  out += '<em>'
445
- + inline.lexer(cap[2] || cap[1])
650
+ + this.output(cap[2] || cap[1])
446
651
  + '</em>';
447
652
  continue;
448
653
  }
449
654
 
450
655
  // code
451
- if (cap = inline.code.exec(src)) {
656
+ if (cap = this.rules.code.exec(src)) {
452
657
  src = src.substring(cap[0].length);
453
658
  out += '<code>'
454
659
  + escape(cap[2], true)
@@ -457,24 +662,42 @@ inline.lexer = function(src) {
457
662
  }
458
663
 
459
664
  // br
460
- if (cap = inline.br.exec(src)) {
665
+ if (cap = this.rules.br.exec(src)) {
461
666
  src = src.substring(cap[0].length);
462
667
  out += '<br>';
463
668
  continue;
464
669
  }
465
670
 
671
+ // del (gfm)
672
+ if (cap = this.rules.del.exec(src)) {
673
+ src = src.substring(cap[0].length);
674
+ out += '<del>'
675
+ + this.output(cap[1])
676
+ + '</del>';
677
+ continue;
678
+ }
679
+
466
680
  // text
467
- if (cap = inline.text.exec(src)) {
681
+ if (cap = this.rules.text.exec(src)) {
468
682
  src = src.substring(cap[0].length);
469
683
  out += escape(cap[0]);
470
684
  continue;
471
685
  }
686
+
687
+ if (src) {
688
+ throw new
689
+ Error('Infinite loop on byte: ' + src.charCodeAt(0));
690
+ }
472
691
  }
473
692
 
474
693
  return out;
475
694
  };
476
695
 
477
- var outputLink = function(cap, link) {
696
+ /**
697
+ * Compile Link
698
+ */
699
+
700
+ InlineLexer.prototype.outputLink = function(cap, link) {
478
701
  if (cap[0][0] !== '!') {
479
702
  return '<a href="'
480
703
  + escape(link.href)
@@ -485,7 +708,7 @@ var outputLink = function(cap, link) {
485
708
  + '"'
486
709
  : '')
487
710
  + '>'
488
- + inline.lexer(cap[1])
711
+ + this.output(cap[1])
489
712
  + '</a>';
490
713
  } else {
491
714
  return '<img src="'
@@ -503,18 +726,97 @@ var outputLink = function(cap, link) {
503
726
  };
504
727
 
505
728
  /**
506
- * Parsing
729
+ * Mangle Links
507
730
  */
508
731
 
509
- var tokens
510
- , token;
732
+ InlineLexer.prototype.mangle = function(text) {
733
+ var out = ''
734
+ , l = text.length
735
+ , i = 0
736
+ , ch;
737
+
738
+ for (; i < l; i++) {
739
+ ch = text.charCodeAt(i);
740
+ if (Math.random() > 0.5) {
741
+ ch = 'x' + ch.toString(16);
742
+ }
743
+ out += '&#' + ch + ';';
744
+ }
745
+
746
+ return out;
747
+ };
748
+
749
+ /**
750
+ * Parsing & Compiling
751
+ */
511
752
 
512
- var next = function() {
513
- return token = tokens.pop();
753
+ function Parser(options) {
754
+ this.tokens = [];
755
+ this.token = null;
756
+ this.options = options || marked.defaults;
757
+ }
758
+
759
+ /**
760
+ * Static Parse Method
761
+ */
762
+
763
+ Parser.parse = function(src, options) {
764
+ var parser = new Parser(options);
765
+ return parser.parse(src);
766
+ };
767
+
768
+ /**
769
+ * Parse Loop
770
+ */
771
+
772
+ Parser.prototype.parse = function(src) {
773
+ this.inline = new InlineLexer(src.links, this.options);
774
+ this.tokens = src.reverse();
775
+
776
+ var out = '';
777
+ while (this.next()) {
778
+ out += this.tok();
779
+ }
780
+
781
+ return out;
514
782
  };
515
783
 
516
- var tok = function() {
517
- switch (token.type) {
784
+ /**
785
+ * Next Token
786
+ */
787
+
788
+ Parser.prototype.next = function() {
789
+ return this.token = this.tokens.pop();
790
+ };
791
+
792
+ /**
793
+ * Preview Next Token
794
+ */
795
+
796
+ Parser.prototype.peek = function() {
797
+ return this.tokens[this.tokens.length-1] || 0;
798
+ };
799
+
800
+ /**
801
+ * Parse Text Tokens
802
+ */
803
+
804
+ Parser.prototype.parseText = function() {
805
+ var body = this.token.text;
806
+
807
+ while (this.peek().type === 'text') {
808
+ body += '\n' + this.next().text;
809
+ }
810
+
811
+ return this.inline.output(body);
812
+ };
813
+
814
+ /**
815
+ * Parse Current Token
816
+ */
817
+
818
+ Parser.prototype.tok = function() {
819
+ switch (this.token.type) {
518
820
  case 'space': {
519
821
  return '';
520
822
  }
@@ -523,39 +825,79 @@ var tok = function() {
523
825
  }
524
826
  case 'heading': {
525
827
  return '<h'
526
- + token.depth
828
+ + this.token.depth
527
829
  + '>'
528
- + inline.lexer(token.text)
830
+ + this.inline.output(this.token.text)
529
831
  + '</h'
530
- + token.depth
832
+ + this.token.depth
531
833
  + '>\n';
532
834
  }
533
835
  case 'code': {
534
- if (options.highlight) {
535
- token.code = options.highlight(token.text, token.lang);
536
- if (token.code != null && token.code !== token.text) {
537
- token.escaped = true;
538
- token.text = token.code;
836
+ if (this.options.highlight) {
837
+ var code = this.options.highlight(this.token.text, this.token.lang);
838
+ if (code != null && code !== this.token.text) {
839
+ this.token.escaped = true;
840
+ this.token.text = code;
539
841
  }
540
842
  }
541
- if (!token.escaped) {
542
- token.text = escape(token.text, true);
843
+
844
+ if (!this.token.escaped) {
845
+ this.token.text = escape(this.token.text, true);
543
846
  }
847
+
544
848
  return '<pre><code'
545
- + (token.lang
546
- ? ' class="lang-'
547
- + token.lang
849
+ + (this.token.lang
850
+ ? ' class="'
851
+ + this.options.langPrefix
852
+ + this.token.lang
548
853
  + '"'
549
854
  : '')
550
855
  + '>'
551
- + token.text
856
+ + this.token.text
552
857
  + '</code></pre>\n';
553
858
  }
859
+ case 'table': {
860
+ var body = ''
861
+ , heading
862
+ , i
863
+ , row
864
+ , cell
865
+ , j;
866
+
867
+ // header
868
+ body += '<thead>\n<tr>\n';
869
+ for (i = 0; i < this.token.header.length; i++) {
870
+ heading = this.inline.output(this.token.header[i]);
871
+ body += this.token.align[i]
872
+ ? '<th align="' + this.token.align[i] + '">' + heading + '</th>\n'
873
+ : '<th>' + heading + '</th>\n';
874
+ }
875
+ body += '</tr>\n</thead>\n';
876
+
877
+ // body
878
+ body += '<tbody>\n'
879
+ for (i = 0; i < this.token.cells.length; i++) {
880
+ row = this.token.cells[i];
881
+ body += '<tr>\n';
882
+ for (j = 0; j < row.length; j++) {
883
+ cell = this.inline.output(row[j]);
884
+ body += this.token.align[j]
885
+ ? '<td align="' + this.token.align[j] + '">' + cell + '</td>\n'
886
+ : '<td>' + cell + '</td>\n';
887
+ }
888
+ body += '</tr>\n';
889
+ }
890
+ body += '</tbody>\n';
891
+
892
+ return '<table>\n'
893
+ + body
894
+ + '</table>\n';
895
+ }
554
896
  case 'blockquote_start': {
555
897
  var body = '';
556
898
 
557
- while (next().type !== 'blockquote_end') {
558
- body += tok();
899
+ while (this.next().type !== 'blockquote_end') {
900
+ body += this.tok();
559
901
  }
560
902
 
561
903
  return '<blockquote>\n'
@@ -563,11 +905,11 @@ var tok = function() {
563
905
  + '</blockquote>\n';
564
906
  }
565
907
  case 'list_start': {
566
- var type = token.ordered ? 'ol' : 'ul'
908
+ var type = this.token.ordered ? 'ol' : 'ul'
567
909
  , body = '';
568
910
 
569
- while (next().type !== 'list_end') {
570
- body += tok();
911
+ while (this.next().type !== 'list_end') {
912
+ body += this.tok();
571
913
  }
572
914
 
573
915
  return '<'
@@ -581,10 +923,10 @@ var tok = function() {
581
923
  case 'list_item_start': {
582
924
  var body = '';
583
925
 
584
- while (next().type !== 'list_item_end') {
585
- body += token.type === 'text'
586
- ? parseText()
587
- : tok();
926
+ while (this.next().type !== 'list_item_end') {
927
+ body += this.token.type === 'text'
928
+ ? this.parseText()
929
+ : this.tok();
588
930
  }
589
931
 
590
932
  return '<li>'
@@ -594,8 +936,8 @@ var tok = function() {
594
936
  case 'loose_item_start': {
595
937
  var body = '';
596
938
 
597
- while (next().type !== 'list_item_end') {
598
- body += tok();
939
+ while (this.next().type !== 'list_item_end') {
940
+ body += this.tok();
599
941
  }
600
942
 
601
943
  return '<li>'
@@ -603,96 +945,44 @@ var tok = function() {
603
945
  + '</li>\n';
604
946
  }
605
947
  case 'html': {
606
- if (options.sanitize) {
607
- return inline.lexer(token.text);
608
- }
609
- return !token.pre && !options.pedantic
610
- ? inline.lexer(token.text)
611
- : token.text;
948
+ return !this.token.pre && !this.options.pedantic
949
+ ? this.inline.output(this.token.text)
950
+ : this.token.text;
612
951
  }
613
952
  case 'paragraph': {
614
953
  return '<p>'
615
- + inline.lexer(token.text)
954
+ + this.inline.output(this.token.text)
616
955
  + '</p>\n';
617
956
  }
618
957
  case 'text': {
619
958
  return '<p>'
620
- + parseText()
959
+ + this.parseText()
621
960
  + '</p>\n';
622
961
  }
623
962
  }
624
963
  };
625
964
 
626
- var parseText = function() {
627
- var body = token.text
628
- , top;
629
-
630
- while ((top = tokens[tokens.length-1])
631
- && top.type === 'text') {
632
- body += '\n' + next().text;
633
- }
634
-
635
- return inline.lexer(body);
636
- };
637
-
638
- var parse = function(src) {
639
- tokens = src.reverse();
640
-
641
- var out = '';
642
- while (next()) {
643
- out += tok();
644
- }
645
-
646
- tokens = null;
647
- token = null;
648
-
649
- return out;
650
- };
651
-
652
965
  /**
653
966
  * Helpers
654
967
  */
655
968
 
656
- var escape = function(html, encode) {
969
+ function escape(html, encode) {
657
970
  return html
658
971
  .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
659
972
  .replace(/</g, '&lt;')
660
973
  .replace(/>/g, '&gt;')
661
974
  .replace(/"/g, '&quot;')
662
975
  .replace(/'/g, '&#39;');
663
- };
664
-
665
- var mangle = function(text) {
666
- var out = ''
667
- , l = text.length
668
- , i = 0
669
- , ch;
670
-
671
- for (; i < l; i++) {
672
- ch = text.charCodeAt(i);
673
- if (Math.random() > 0.5) {
674
- ch = 'x' + ch.toString(16);
675
- }
676
- out += '&#' + ch + ';';
677
- }
678
-
679
- return out;
680
- };
681
-
682
- function tag() {
683
- var tag = '(?!(?:'
684
- + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
685
- + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
686
- + '|span|br|wbr|ins|del|img)\\b)\\w+';
687
-
688
- return tag;
689
976
  }
690
977
 
691
- function replace(regex) {
978
+ function replace(regex, opt) {
692
979
  regex = regex.source;
980
+ opt = opt || '';
693
981
  return function self(name, val) {
694
- if (!name) return new RegExp(regex);
695
- regex = regex.replace(name, val.source || val);
982
+ if (!name) return new RegExp(regex, opt);
983
+ val = val.source || val;
984
+ val = val.replace(/(^|[^\[])\^/g, '$1');
985
+ regex = regex.replace(name, val);
696
986
  return self;
697
987
  };
698
988
  }
@@ -700,80 +990,83 @@ function replace(regex) {
700
990
  function noop() {}
701
991
  noop.exec = noop;
702
992
 
993
+ function merge(obj) {
994
+ var i = 1
995
+ , target
996
+ , key;
997
+
998
+ for (; i < arguments.length; i++) {
999
+ target = arguments[i];
1000
+ for (key in target) {
1001
+ if (Object.prototype.hasOwnProperty.call(target, key)) {
1002
+ obj[key] = target[key];
1003
+ }
1004
+ }
1005
+ }
1006
+
1007
+ return obj;
1008
+ }
1009
+
703
1010
  /**
704
1011
  * Marked
705
1012
  */
706
1013
 
707
- var marked = function(src, opt) {
708
- setOptions(opt);
709
- return parse(block.lexer(src));
710
- };
1014
+ function marked(src, opt) {
1015
+ try {
1016
+ if (opt) opt = merge({}, marked.defaults, opt);
1017
+ return Parser.parse(Lexer.lex(src, opt), opt);
1018
+ } catch (e) {
1019
+ e.message += '\nPlease report this to https://github.com/chjj/marked.';
1020
+ if ((opt || marked.defaults).silent) {
1021
+ return '<p>An error occured:</p><pre>'
1022
+ + escape(e.message + '', true)
1023
+ + '</pre>';
1024
+ }
1025
+ throw e;
1026
+ }
1027
+ }
711
1028
 
712
1029
  /**
713
1030
  * Options
714
1031
  */
715
1032
 
716
- var options
717
- , defaults;
718
-
719
- var setOptions = function(opt) {
720
- if (!opt) opt = defaults;
721
- if (options === opt) return;
722
- options = opt;
723
-
724
- if (options.gfm) {
725
- block.fences = block.gfm.fences;
726
- block.paragraph = block.gfm.paragraph;
727
- inline.text = inline.gfm.text;
728
- inline.url = inline.gfm.url;
729
- } else {
730
- block.fences = block.normal.fences;
731
- block.paragraph = block.normal.paragraph;
732
- inline.text = inline.normal.text;
733
- inline.url = inline.normal.url;
734
- }
735
-
736
- if (options.pedantic) {
737
- inline.em = inline.pedantic.em;
738
- inline.strong = inline.pedantic.strong;
739
- } else {
740
- inline.em = inline.normal.em;
741
- inline.strong = inline.normal.strong;
742
- }
743
- };
744
-
745
1033
  marked.options =
746
1034
  marked.setOptions = function(opt) {
747
- defaults = opt;
748
- setOptions(opt);
1035
+ merge(marked.defaults, opt);
749
1036
  return marked;
750
1037
  };
751
1038
 
752
- marked.options({
1039
+ marked.defaults = {
753
1040
  gfm: true,
1041
+ tables: true,
1042
+ breaks: false,
754
1043
  pedantic: false,
755
1044
  sanitize: false,
756
- highlight: null
757
- });
1045
+ smartLists: false,
1046
+ silent: false,
1047
+ highlight: null,
1048
+ langPrefix: 'lang-'
1049
+ };
758
1050
 
759
1051
  /**
760
1052
  * Expose
761
1053
  */
762
1054
 
763
- marked.parser = function(src, opt) {
764
- setOptions(opt);
765
- return parse(src);
766
- };
1055
+ marked.Parser = Parser;
1056
+ marked.parser = Parser.parse;
767
1057
 
768
- marked.lexer = function(src, opt) {
769
- setOptions(opt);
770
- return block.lexer(src);
771
- };
1058
+ marked.Lexer = Lexer;
1059
+ marked.lexer = Lexer.lex;
1060
+
1061
+ marked.InlineLexer = InlineLexer;
1062
+ marked.inlineLexer = InlineLexer.output;
772
1063
 
773
1064
  marked.parse = marked;
774
1065
 
775
- if (typeof module !== 'undefined') {
1066
+ if (typeof exports === 'object') {
776
1067
  module.exports = marked;
1068
+ } else if (typeof define === 'function' && define.amd) {
1069
+ define(function() { return marked; });
777
1070
  } else {
778
1071
  this.marked = marked;
779
1072
  }