UNPKG

3.29 kBJavaScriptView Raw
1var TAGS = {
2 '' : ['<em>','</em>'],
3 _ : ['<strong>','</strong>'],
4 '\n' : ['<br />'],
5 ' ' : ['<br />'],
6 '-': ['<hr />']
7};
8
9/** Outdent a string based on the first indented line's leading whitespace
10 * @private
11 */
12function outdent(str) {
13 return str.replace(RegExp('^'+(str.match(/^(\t| )+/) || '')[0], 'gm'), '');
14}
15
16/** Encode special attribute characters to HTML entities in a String.
17 * @private
18 */
19function encodeAttr(str) {
20 return (str+'').replace(/"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
21}
22
23/** Parse Markdown into an HTML String. */
24function parse(md) {
25 var tokenizer = /((?:^|\n+)(?:\n---+|\* \*(?: \*)+)\n)|(?:^```(\w*)\n([\s\S]*?)\n```$)|((?:(?:^|\n+)(?:\t| {2,}).+)+\n*)|((?:(?:^|\n)([>*+-]|\d+\.)\s+.*)+)|(?:\!\[([^\]]*?)\]\(([^\)]+?)\))|(\[)|(\](?:\(([^\)]+?)\))?)|(?:(?:^|\n+)([^\s].*)\n(\-{3,}|={3,})(?:\n+|$))|(?:(?:^|\n+)(#{1,3})\s*(.+)(?:\n+|$))|(?:`([^`].*?)`)|( \n\n*|\n{2,}|__|\*\*|[_*])/gm,
26 context = [],
27 out = '',
28 last = 0,
29 links = {},
30 chunk, prev, token, inner, t;
31
32 function tag(token) {
33 var desc = TAGS[token.replace(/\*/g,'_')[1] || ''],
34 end = context[context.length-1]==token;
35 if (!desc) { return token; }
36 if (!desc[1]) { return desc[0]; }
37 context[end?'pop':'push'](token);
38 return desc[end|0];
39 }
40
41 function flush() {
42 var str = '';
43 while (context.length) { str += tag(context[context.length-1]); }
44 return str;
45 }
46
47 md = md.replace(/^\[(.+?)\]:\s*(.+)$/gm, function (s, name, url) {
48 links[name.toLowerCase()] = url;
49 return '';
50 }).replace(/^\n+|\n+$/g, '');
51
52 while ( (token=tokenizer.exec(md)) ) {
53 prev = md.substring(last, token.index);
54 last = tokenizer.lastIndex;
55 chunk = token[0];
56 if (prev.match(/[^\\](\\\\)*\\$/)) {
57 // escaped
58 }
59 // Code/Indent blocks:
60 else if (token[3] || token[4]) {
61 chunk = '<pre class="code '+(token[4]?'poetry':token[2].toLowerCase())+'">'+outdent(encodeAttr(token[3] || token[4]).replace(/^\n+|\n+$/g, ''))+'</pre>';
62 }
63 // > Quotes, -* lists:
64 else if (token[6]) {
65 t = token[6];
66 if (t.match(/\./)) {
67 token[5] = token[5].replace(/^\d+/gm, '');
68 }
69 inner = parse(outdent(token[5].replace(/^\s*[>*+.-]/gm, '')));
70 if (t==='>') { t = 'blockquote'; }
71 else {
72 t = t.match(/\./) ? 'ol' : 'ul';
73 inner = inner.replace(/^(.*)(\n|$)/gm, '<li>$1</li>');
74 }
75 chunk = '<'+t+'>' + inner + '</'+t+'>';
76 }
77 // Images:
78 else if (token[8]) {
79 chunk = "<img src=\"" + (encodeAttr(token[8])) + "\" alt=\"" + (encodeAttr(token[7])) + "\">";
80 }
81 // Links:
82 else if (token[10]) {
83 out = out.replace('<a>', ("<a href=\"" + (encodeAttr(token[11] || links[prev.toLowerCase()])) + "\">"));
84 chunk = flush() + '</a>';
85 }
86 else if (token[9]) {
87 chunk = '<a>';
88 }
89 // Headings:
90 else if (token[12] || token[14]) {
91 t = 'h' + (token[14] ? token[14].length : (token[13][0]==='='?1:2));
92 chunk = '<'+t+'>' + parse(token[12] || token[15]) + '</'+t+'>';
93 }
94 // `code`:
95 else if (token[16]) {
96 chunk = '<code>'+encodeAttr(token[16])+'</code>';
97 }
98 // Inline formatting: *em*, **strong** & friends
99 else if (token[17] || token[1]) {
100 chunk = tag(token[17] || '--');
101 }
102 out += prev;
103 out += chunk;
104 }
105
106 return (out + md.substring(last) + flush()).trim();
107}
108
109export default parse;
110//# sourceMappingURL=snarkdown.es.js.map