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