aboutsummaryrefslogtreecommitdiff
path: root/imports/codemirror/mode/markdown/markdown.js
diff options
context:
space:
mode:
Diffstat (limited to 'imports/codemirror/mode/markdown/markdown.js')
-rwxr-xr-ximports/codemirror/mode/markdown/markdown.js242
1 files changed, 242 insertions, 0 deletions
diff --git a/imports/codemirror/mode/markdown/markdown.js b/imports/codemirror/mode/markdown/markdown.js
new file mode 100755
index 00000000..455bb43c
--- /dev/null
+++ b/imports/codemirror/mode/markdown/markdown.js
@@ -0,0 +1,242 @@
1CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
2
3 var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
4
5 var header = 'header'
6 , code = 'comment'
7 , quote = 'quote'
8 , list = 'string'
9 , hr = 'hr'
10 , linktext = 'link'
11 , linkhref = 'string'
12 , em = 'em'
13 , strong = 'strong'
14 , emstrong = 'emstrong';
15
16 var hrRE = /^[*-=_]/
17 , ulRE = /^[*-+]\s+/
18 , olRE = /^[0-9]\.\s+/
19 , headerRE = /^(?:\={3,}|-{3,})$/
20 , codeRE = /^(k:\t|\s{4,})/
21 , textRE = /^[^\[*_\\<>`]+/;
22
23 function switchInline(stream, state, f) {
24 state.f = state.inline = f;
25 return f(stream, state);
26 }
27
28 function switchBlock(stream, state, f) {
29 state.f = state.block = f;
30 return f(stream, state);
31 }
32
33
34 // Blocks
35
36 function blockNormal(stream, state) {
37 if (stream.match(codeRE)) {
38 stream.skipToEnd();
39 return code;
40 }
41
42 if (stream.eatSpace()) {
43 return null;
44 }
45
46 if (stream.peek() === '#' || stream.match(headerRE)) {
47 stream.skipToEnd();
48 return header;
49 }
50 if (stream.eat('>')) {
51 state.indentation++;
52 return quote;
53 }
54 if (stream.peek() === '[') {
55 return switchInline(stream, state, footnoteLink);
56 }
57 if (hrRE.test(stream.peek())) {
58 var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$');
59 if (stream.match(re, true)) {
60 return hr;
61 }
62 }
63
64 var match;
65 if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
66 state.indentation += match[0].length;
67 return list;
68 }
69
70 return switchInline(stream, state, state.inline);
71 }
72
73 function htmlBlock(stream, state) {
74 var style = htmlMode.token(stream, state.htmlState);
75 if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
76 state.f = inlineNormal;
77 state.block = blockNormal;
78 }
79 return style;
80 }
81
82
83 // Inline
84 function getType(state) {
85 return state.strong ? (state.em ? emstrong : strong)
86 : (state.em ? em : null);
87 }
88
89 function handleText(stream, state) {
90 if (stream.match(textRE, true)) {
91 return getType(state);
92 }
93 return undefined;
94 }
95
96 function inlineNormal(stream, state) {
97 var style = state.text(stream, state)
98 if (typeof style !== 'undefined')
99 return style;
100
101 var ch = stream.next();
102
103 if (ch === '\\') {
104 stream.next();
105 return getType(state);
106 }
107 if (ch === '`') {
108 return switchInline(stream, state, inlineElement(code, '`'));
109 }
110 if (ch === '[') {
111 return switchInline(stream, state, linkText);
112 }
113 if (ch === '<' && stream.match(/^\w/, false)) {
114 stream.backUp(1);
115 return switchBlock(stream, state, htmlBlock);
116 }
117
118 var t = getType(state);
119 if (ch === '*' || ch === '_') {
120 if (stream.eat(ch)) {
121 return (state.strong = !state.strong) ? getType(state) : t;
122 }
123 return (state.em = !state.em) ? getType(state) : t;
124 }
125
126 return getType(state);
127 }
128
129 function linkText(stream, state) {
130 while (!stream.eol()) {
131 var ch = stream.next();
132 if (ch === '\\') stream.next();
133 if (ch === ']') {
134 state.inline = state.f = linkHref;
135 return linktext;
136 }
137 }
138 return linktext;
139 }
140
141 function linkHref(stream, state) {
142 stream.eatSpace();
143 var ch = stream.next();
144 if (ch === '(' || ch === '[') {
145 return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
146 }
147 return 'error';
148 }
149
150 function footnoteLink(stream, state) {
151 if (stream.match(/^[^\]]*\]:/, true)) {
152 state.f = footnoteUrl;
153 return linktext;
154 }
155 return switchInline(stream, state, inlineNormal);
156 }
157
158 function footnoteUrl(stream, state) {
159 stream.eatSpace();
160 stream.match(/^[^\s]+/, true);
161 state.f = state.inline = inlineNormal;
162 return linkhref;
163 }
164
165 function inlineRE(endChar) {
166 if (!inlineRE[endChar]) {
167 // match any not-escaped-non-endChar and any escaped char
168 // then match endChar or eol
169 inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)');
170 }
171 return inlineRE[endChar];
172 }
173
174 function inlineElement(type, endChar, next) {
175 next = next || inlineNormal;
176 return function(stream, state) {
177 stream.match(inlineRE(endChar));
178 state.inline = state.f = next;
179 return type;
180 };
181 }
182
183 return {
184 startState: function() {
185 return {
186 f: blockNormal,
187
188 block: blockNormal,
189 htmlState: htmlMode.startState(),
190 indentation: 0,
191
192 inline: inlineNormal,
193 text: handleText,
194 em: false,
195 strong: false
196 };
197 },
198
199 copyState: function(s) {
200 return {
201 f: s.f,
202
203 block: s.block,
204 htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
205 indentation: s.indentation,
206
207 inline: s.inline,
208 text: s.text,
209 em: s.em,
210 strong: s.strong
211 };
212 },
213
214 token: function(stream, state) {
215 if (stream.sol()) {
216 state.f = state.block;
217 var previousIndentation = state.indentation
218 , currentIndentation = 0;
219 while (previousIndentation > 0) {
220 if (stream.eat(' ')) {
221 previousIndentation--;
222 currentIndentation++;
223 } else if (previousIndentation >= 4 && stream.eat('\t')) {
224 previousIndentation -= 4;
225 currentIndentation += 4;
226 } else {
227 break;
228 }
229 }
230 state.indentation = currentIndentation;
231
232 if (currentIndentation > 0) return null;
233 }
234 return state.f(stream, state);
235 },