kit_cms 2.3.16 → 2.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. data/app/assets/images/kit/toolbar/bold.png +0 -0
  2. data/app/assets/images/kit/toolbar/formatted.png +0 -0
  3. data/app/assets/images/kit/toolbar/image.png +0 -0
  4. data/app/assets/images/kit/toolbar/link.png +0 -0
  5. data/app/assets/images/kit/toolbar/list.png +0 -0
  6. data/app/assets/images/kit/toolbar/numbered_list.png +0 -0
  7. data/app/assets/images/kit/toolbar/preview.png +0 -0
  8. data/app/assets/images/kit/toolbar/quote.png +0 -0
  9. data/app/assets/images/kit/toolbar/redo.png +0 -0
  10. data/app/assets/images/kit/toolbar/underline.png +0 -0
  11. data/app/assets/images/kit/toolbar/undo.png +0 -0
  12. data/app/assets/images/kit/toolbar/video.png +0 -0
  13. data/app/assets/javascripts/codemirror/editor.js +183 -0
  14. data/app/assets/javascripts/codemirror/markdown.js +475 -0
  15. data/app/assets/javascripts/jquery.contextmenu.js +7 -7
  16. data/app/assets/javascripts/kit/kit.js +0 -1
  17. data/app/assets/javascripts/kit/markdown.js +1 -37
  18. data/app/assets/javascripts/kit/tree.js +18 -10
  19. data/app/assets/javascripts/kit/utilities.js +1 -1
  20. data/app/assets/stylesheets/codemirror/editor.css +3 -3
  21. data/app/assets/stylesheets/codemirror/theme.css +76 -1
  22. data/app/assets/stylesheets/kit/calendar.css +58 -97
  23. data/app/assets/stylesheets/kit/cms/cms.css +19 -0
  24. data/app/assets/stylesheets/kit/cms/dsc_tree.css +4 -0
  25. data/app/assets/stylesheets/kit/forums.css +30 -0
  26. data/app/assets/stylesheets/kit/kit.css +0 -1
  27. data/app/controllers/admin/dashboard_controller.rb +11 -2
  28. data/app/controllers/admin/page_templates_controller.rb +14 -1
  29. data/app/controllers/admin/user_controller.rb +2 -3
  30. data/app/controllers/category_controller.rb +5 -6
  31. data/app/controllers/form_controller.rb +1 -1
  32. data/app/controllers/forum_controller.rb +2 -2
  33. data/app/controllers/kit_controller.rb +2 -2
  34. data/app/controllers/pages_controller.rb +24 -0
  35. data/app/controllers/user/passwords_controller.rb +2 -0
  36. data/app/controllers/user/registrations_controller.rb +1 -4
  37. data/app/controllers/user/sessions_controller.rb +2 -0
  38. data/app/controllers/user/unlocks_controller.rb +2 -0
  39. data/app/controllers/user_controller.rb +1 -1
  40. data/app/helpers/calendar_helper.rb +4 -1
  41. data/app/helpers/kit_helper.rb +3 -3
  42. data/app/mailers/notification.rb +3 -3
  43. data/app/models/activity.rb +5 -2
  44. data/app/models/calendar.rb +1 -1
  45. data/app/models/category.rb +18 -4
  46. data/app/models/experiment.rb +2 -0
  47. data/app/models/form_submission.rb +2 -2
  48. data/app/models/html_asset.rb +3 -0
  49. data/app/models/mapping.rb +1 -0
  50. data/app/models/user.rb +1 -1
  51. data/app/views/admin/blocks/_form.html.haml +1 -1
  52. data/app/views/admin/dashboard/user_comments.html.haml +19 -8
  53. data/app/views/admin/experiments/show.html.haml +1 -1
  54. data/app/views/admin/form/_form.html.haml +8 -0
  55. data/app/views/admin/page_templates/_form.html.haml +1 -1
  56. data/app/views/admin/views/_form.html.haml +4 -4
  57. data/app/views/calendar/_calendar_scripts.js.erb +2 -2
  58. data/app/views/calendar/_month.html.haml +58 -27
  59. data/app/views/category/_select_options.html.erb +1 -1
  60. data/app/views/form/_show.html.haml +9 -1
  61. data/app/views/forum/_add_post.html.haml +3 -2
  62. data/app/views/forum/_mod_thread.js.erb +2 -2
  63. data/app/views/forum/_post_scripts.js.erb +1 -2
  64. data/app/views/forum/thread.html.haml +4 -0
  65. data/app/views/layouts/_application.html.erb +1 -0
  66. data/app/views/layouts/minimal.html.erb +2 -2
  67. data/app/views/notification/form_submission.text.erb +2 -0
  68. data/app/views/pages/_form.html.haml +7 -8
  69. data/app/views/pages/_page_scripts.js.erb +1 -3
  70. data/app/views/pages/_page_template_script.js.erb +28 -0
  71. data/app/views/pages/cookie_text.html.haml +1 -1
  72. data/app/views/pages/info.html.erb +2 -2
  73. data/app/views/pages/page_template.html.haml +40 -0
  74. data/app/views/utility/_cm_editor.html.erb +34 -5
  75. data/app/views/utility/_cm_toolbar_markdown.html.haml +49 -0
  76. data/config/initializers/devise_extender.rb +9 -0
  77. data/config/initializers/html_assets.rb +4 -0
  78. data/config/initializers/quiet_asserts.rb +1 -1
  79. data/config/routes.rb +2 -0
  80. metadata +21 -3
@@ -5,6 +5,7 @@
5
5
  *= require "./htmlmixed"
6
6
  *= require "./sass"
7
7
  *= require "./css"
8
+ *= require "./markdown"
8
9
  *= require "./xml"
9
10
  *= require "./javascript"
10
11
  *= require "./dialog"
@@ -16,4 +17,186 @@
16
17
  *
17
18
  */
18
19
 
20
+ var html_editors = new Array();
19
21
 
22
+ function do_bold(editor) {
23
+ insert_formatting(editor, "*", "*", "Bold");
24
+ }
25
+
26
+ function do_underline(editor) {
27
+ insert_formatting(editor, "_", "_", "Underlined");
28
+ }
29
+
30
+ function do_cmd(editor, cmd) {
31
+ if (cmd=='list') {
32
+ do_list(editor, '-');
33
+ }
34
+ if (cmd=='numbered_list') {
35
+ do_list(editor, '1.');
36
+ }
37
+ if (cmd=='image') {
38
+ do_image(editor);
39
+ }
40
+ if (cmd=='video') {
41
+ do_video(editor);
42
+ }
43
+ if (cmd=='link') {
44
+ do_link(editor);
45
+ }
46
+ if (cmd=='bold') {
47
+ insert_formatting(editor, '**', '**', 'Bold');
48
+ }
49
+ if (cmd=='underline') {
50
+ insert_formatting(editor, '_', '_', 'Underline');
51
+ }
52
+ if (cmd=='pre') {
53
+ do_prefix(editor, ' ');
54
+ }
55
+ if (cmd=='quote') {
56
+ do_prefix(editor, '> ');
57
+ }
58
+ if (cmd=='undo') {
59
+ do_undo(editor);
60
+ }
61
+ if (cmd=='redo') {
62
+ do_redo(editor);
63
+ }
64
+ if (cmd=='preview') {
65
+ do_preview(editor);
66
+ }
67
+ }
68
+
69
+ function do_prefix(editor, prefix) {
70
+ var editor = html_editors[editor];
71
+ var start_pos = editor.getCursor('start');
72
+ var end_pos = editor.getCursor('end');
73
+
74
+ if (editor.somethingSelected()) {
75
+ for (var i = start_pos.line; i <= end_pos.line; i++) {
76
+ editor.setLine(i, prefix + editor.getLine(i));
77
+ }
78
+ } else {
79
+ if (start_pos.ch==0) {
80
+ editor.replaceSelection(prefix);
81
+ }
82
+ else {
83
+ editor.setLine(start_pos.line, prefix + editor.getLine(start_pos.line));
84
+ }
85
+ start_pos.ch = start_pos.ch + prefix.length;
86
+ editor.setCursor(start_pos);
87
+ }
88
+ editor.focus();
89
+ }
90
+
91
+ function do_link(editor) {
92
+ var url = prompt("Enter the full URL for the link", "http://");
93
+
94
+ insert_formatting(editor, '[', '](' + url + ')', 'Link');
95
+ }
96
+
97
+ function do_video(editor) {
98
+ var editor = html_editors[editor];
99
+ var start_pos = editor.getCursor('start');
100
+ var url = prompt("Enter Youtube 'share' URL or Vimeo URL");
101
+
102
+ editor.replaceSelection(url);
103
+ start_pos.ch = start_pos.ch + url.length;
104
+ editor.setCursor(start_pos);
105
+ editor.focus();
106
+ }
107
+
108
+ function do_undo(editor) {
109
+ var editor = html_editors[editor];
110
+ editor.undo();
111
+ }
112
+
113
+ function do_redo(editor) {
114
+ var editor = html_editors[editor];
115
+ editor.redo();
116
+ }
117
+
118
+ function do_image(editor) {
119
+ var editor = html_editors[editor];
120
+ var url = prompt("Enter the full URL of the image", "http://");
121
+ var start_pos = editor.getCursor('start');
122
+
123
+ editor.replaceSelection("![](" + url + ")");
124
+ start_pos.ch = start_pos.ch + url.length + 5;
125
+ editor.setCursor(start_pos);
126
+ editor.focus();
127
+ }
128
+
129
+ function do_list(editor_name,list) {
130
+ var editor = html_editors[editor_name];
131
+ var start_pos = editor.getCursor('start');
132
+ var end_pos = editor.getCursor('end');
133
+ var to_insert = '';
134
+
135
+ if (editor.somethingSelected()) {
136
+ if (start_pos.line != end_pos.line) {
137
+ do_prefix(editor_name, list);
138
+ return;
139
+ }
140
+ else {
141
+ var selection = editor.getSelection();
142
+ editor.replaceSelection(((start_pos.ch==0) ? '' : '\r') + list + ' ' + selection + '\r' + list + ' ');
143
+ start_pos.line = start_pos.line + 2;
144
+ start_pos.ch = selection.length + list.length;
145
+ }
146
+ editor.setCursor(start_pos);
147
+ }
148
+ else {
149
+ if (start_pos.ch>0) {
150
+ var line = editor.getLine(start_pos.line);
151
+ if (line && line.indexOf(list)==0) {
152
+ editor.setLine(start_pos.line, editor.getLine(start_pos.line) + '\r' + list + ' ');
153
+ }
154
+ else {
155
+ editor.setLine(start_pos.line, list + ' ' + editor.getLine(start_pos.line) + '\r' + list + ' ');
156
+ }
157
+ start_pos.line = start_pos.line + 1;
158
+ editor.setCursor(start_pos);
159
+ } else {
160
+ editor.replaceSelection(list + ' ');
161
+ start_pos.ch = list.length+1;
162
+ editor.setCursor(start_pos);
163
+ }
164
+ }
165
+ editor.focus();
166
+ }
167
+
168
+ function insert_formatting(editor,start,end,placeholder) {
169
+ var editor = html_editors[editor];
170
+ var current = editor.getSelection();
171
+ var placeholder_mode = false;
172
+ var start_pos = editor.getCursor("start");
173
+ var end_pos = editor.getCursor("end");
174
+
175
+ if (!editor.somethingSelected()) {
176
+ current = placeholder;
177
+ placeholder_mode = true;
178
+ }
179
+
180
+ editor.replaceSelection(start + current + end);
181
+ if (placeholder_mode) {
182
+ start_pos.ch = start_pos.ch + start.length;
183
+ end_pos.ch = start_pos.ch + placeholder.length;
184
+ editor.setSelection( start_pos, end_pos);
185
+ }
186
+ else {
187
+ end_pos.ch = end_pos.ch + (start.length*2);
188
+ editor.setCursor(end_pos);
189
+ }
190
+ editor.focus();
191
+ }
192
+
193
+
194
+ function do_preview(field) {
195
+ var editor = html_editors[field];
196
+ $('#preview_' + field).dialog({ autoOpen: true,
197
+ width: 800, height: 500, modal: true, resizable: true, show: {effect:"drop"}, title: "Preview", closeOnEscape: true, dialogClass: "modal_markdown_preview" });
198
+
199
+ $.post('/utility/markdown_preview', {body: editor.getValue()}, function(data) {
200
+ $('#preview_' + field).html(data);
201
+ });
202
+ }
@@ -0,0 +1,475 @@
1
+ CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
2
+
3
+ var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html");
4
+ var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain");
5
+ var aliases = {
6
+ html: "htmlmixed",
7
+ js: "javascript",
8
+ json: "application/json",
9
+ c: "text/x-csrc",
10
+ "c++": "text/x-c++src",
11
+ java: "text/x-java",
12
+ csharp: "text/x-csharp",
13
+ "c#": "text/x-csharp",
14
+ scala: "text/x-scala"
15
+ };
16
+
17
+ var getMode = (function () {
18
+ var i, modes = {}, mimes = {}, mime;
19
+
20
+ var list = [];
21
+ for (var m in CodeMirror.modes)
22
+ if (CodeMirror.modes.propertyIsEnumerable(m)) list.push(m);
23
+ for (i = 0; i < list.length; i++) {
24
+ modes[list[i]] = list[i];
25
+ }
26
+ var mimesList = [];
27
+ for (var m in CodeMirror.mimeModes)
28
+ if (CodeMirror.mimeModes.propertyIsEnumerable(m))
29
+ mimesList.push({mime: m, mode: CodeMirror.mimeModes[m]});
30
+ for (i = 0; i < mimesList.length; i++) {
31
+ mime = mimesList[i].mime;
32
+ mimes[mime] = mimesList[i].mime;
33
+ }
34
+
35
+ for (var a in aliases) {
36
+ if (aliases[a] in modes || aliases[a] in mimes)
37
+ modes[a] = aliases[a];
38
+ }
39
+
40
+ return function (lang) {
41
+ return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null;
42
+ };
43
+ }());
44
+
45
+ // Should underscores in words open/close em/strong?
46
+ if (modeCfg.underscoresBreakWords === undefined)
47
+ modeCfg.underscoresBreakWords = true;
48
+
49
+ // Turn on fenced code blocks? ("```" to start/end)
50
+ if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
51
+
52
+ var codeDepth = 0;
53
+ var prevLineHasContent = false
54
+ , thisLineHasContent = false;
55
+
56
+ var header = 'header'
57
+ , code = 'comment'
58
+ , quote = 'quote'
59
+ , list = 'string'
60
+ , hr = 'hr'
61
+ , image = 'tag'
62
+ , linkinline = 'link'
63
+ , linkemail = 'link'
64
+ , linktext = 'link'
65
+ , linkhref = 'string'
66
+ , em = 'em'
67
+ , strong = 'strong'
68
+ , emstrong = 'emstrong';
69
+
70
+ var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
71
+ , ulRE = /^[*\-+]\s+/
72
+ , olRE = /^[0-9]+\.\s+/
73
+ , headerRE = /^(?:\={1,}|-{1,})$/
74
+ , textRE = /^[^!\[\]*_\\<>` "'(]+/;
75
+
76
+ function switchInline(stream, state, f) {
77
+ state.f = state.inline = f;
78
+ return f(stream, state);
79
+ }
80
+
81
+ function switchBlock(stream, state, f) {
82
+ state.f = state.block = f;
83
+ return f(stream, state);
84
+ }
85
+
86
+
87
+ // Blocks
88
+
89
+ function blankLine(state) {
90
+ // Reset linkTitle state
91
+ state.linkTitle = false;
92
+ // Reset EM state
93
+ state.em = false;
94
+ // Reset STRONG state
95
+ state.strong = false;
96
+ // Reset state.quote
97
+ state.quote = false;
98
+ if (!htmlFound && state.f == htmlBlock) {
99
+ state.f = inlineNormal;
100
+ state.block = blockNormal;
101
+ }
102
+ return null;
103
+ }
104
+
105
+ function blockNormal(stream, state) {
106
+
107
+ if (state.list !== false && state.indentationDiff >= 0) { // Continued list
108
+ if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
109
+ state.indentation -= state.indentationDiff;
110
+ }
111
+ state.list = null;
112
+ } else { // No longer a list
113
+ state.list = false;
114
+ }
115
+
116
+ if (state.indentationDiff >= 4) {
117
+ state.indentation -= 4;
118
+ stream.skipToEnd();
119
+ return code;
120
+ } else if (stream.eatSpace()) {
121
+ return null;
122
+ } else if (stream.peek() === '#' || (prevLineHasContent && stream.match(headerRE)) ) {
123
+ state.header = true;
124
+ } else if (stream.eat('>')) {
125
+ state.indentation++;
126
+ state.quote = true;
127
+ } else if (stream.peek() === '[') {
128
+ return switchInline(stream, state, footnoteLink);
129
+ } else if (stream.match(hrRE, true)) {
130
+ return hr;
131
+ } else if (stream.match(ulRE, true) || stream.match(olRE, true)) {
132
+ state.indentation += 4;
133
+ state.list = true;
134
+ } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) {
135
+ // try switching mode
136
+ state.localMode = getMode(RegExp.$1);
137
+ if (state.localMode) state.localState = state.localMode.startState();
138
+ switchBlock(stream, state, local);
139
+ return code;
140
+ }
141
+
142
+ return switchInline(stream, state, state.inline);
143
+ }
144
+
145
+ function htmlBlock(stream, state) {
146
+ var style = htmlMode.token(stream, state.htmlState);
147
+ if (htmlFound && style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
148
+ state.f = inlineNormal;
149
+ state.block = blockNormal;
150
+ }
151
+ if (state.md_inside && stream.current().indexOf(">")!=-1) {
152
+ state.f = inlineNormal;
153
+ state.block = blockNormal;
154
+ state.htmlState.context = undefined;
155
+ }
156
+ return style;
157
+ }
158
+
159
+ function local(stream, state) {
160
+ if (stream.sol() && stream.match(/^```/, true)) {
161
+ state.localMode = state.localState = null;
162
+ state.f = inlineNormal;
163
+ state.block = blockNormal;
164
+ return code;
165
+ } else if (state.localMode) {
166
+ return state.localMode.token(stream, state.localState);
167
+ } else {
168
+ stream.skipToEnd();
169
+ return code;
170
+ }
171
+ }
172
+
173
+ // Inline
174
+ function getType(state) {
175
+ var styles = [];
176
+
177
+ if (state.strong) { styles.push(state.em ? emstrong : strong); }
178
+ else if (state.em) { styles.push(em); }
179
+
180
+ if (state.linkText) { styles.push(linktext); }
181
+
182
+ if (state.code) { styles.push(code); }
183
+
184
+ if (state.header) { styles.push(header); }
185
+ if (state.quote) { styles.push(quote); }
186
+ if (state.list !== false) { styles.push(list); }
187
+
188
+ return styles.length ? styles.join(' ') : null;
189
+ }
190
+
191
+ function handleText(stream, state) {
192
+ if (stream.match(textRE, true)) {
193
+ return getType(state);
194
+ }
195
+ return undefined;
196
+ }
197
+
198
+ function inlineNormal(stream, state) {
199
+ var style = state.text(stream, state);
200
+ if (typeof style !== 'undefined')
201
+ return style;
202
+
203
+ if (state.list) { // List marker (*, +, -, 1., etc)
204
+ state.list = null;
205
+ return list;
206
+ }
207
+
208
+ var ch = stream.next();
209
+
210
+ if (ch === '\\') {
211
+ stream.next();
212
+ return getType(state);
213
+ }
214
+
215
+ // Matches link titles present on next line
216
+ if (state.linkTitle) {
217
+ state.linkTitle = false;
218
+ var matchCh = ch;
219
+ if (ch === '(') {
220
+ matchCh = ')';
221
+ }
222
+ matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
223
+ var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
224
+ if (stream.match(new RegExp(regex), true)) {
225
+ return linkhref;
226
+ }
227
+ }
228
+
229
+ // If this block is changed, it may need to be updated in GFM mode
230
+ if (ch === '`') {
231
+ var t = getType(state);
232
+ var before = stream.pos;
233
+ stream.eatWhile('`');
234
+ var difference = 1 + stream.pos - before;
235
+ if (!state.code) {
236
+ codeDepth = difference;
237
+ state.code = true;
238
+ return getType(state);
239
+ } else {
240
+ if (difference === codeDepth) { // Must be exact
241
+ state.code = false;
242
+ return t;
243
+ }
244
+ return getType(state);
245
+ }
246
+ } else if (state.code) {
247
+ return getType(state);
248
+ }
249
+
250
+ if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
251
+ stream.match(/\[[^\]]*\]/);
252
+ state.inline = state.f = linkHref;
253
+ return image;
254
+ }
255
+
256
+ if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
257
+ state.linkText = true;
258
+ return getType(state);
259
+ }
260
+
261
+ if (ch === ']' && state.linkText) {
262
+ var type = getType(state);
263
+ state.linkText = false;
264
+ state.inline = state.f = linkHref;
265
+ return type;
266
+ }
267
+
268
+ if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) {
269
+ return switchInline(stream, state, inlineElement(linkinline, '>'));
270
+ }
271
+
272
+ if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) {
273
+ return switchInline(stream, state, inlineElement(linkemail, '>'));
274
+ }
275
+
276
+ if (ch === '<' && stream.match(/^\w/, false)) {
277
+ if (stream.string.indexOf(">")!=-1) {
278
+ var atts = stream.string.substring(1,stream.string.indexOf(">"));
279
+ if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
280
+ state.md_inside = true;
281
+ }
282
+ }
283
+ stream.backUp(1);
284
+ return switchBlock(stream, state, htmlBlock);
285
+ }
286
+
287
+ if (ch === '<' && stream.match(/^\/\w*?>/)) {
288
+ state.md_inside = false;
289
+ return "tag";
290
+ }
291
+
292
+ var ignoreUnderscore = false;
293
+ if (!modeCfg.underscoresBreakWords) {
294
+ if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
295
+ var prevPos = stream.pos - 2;
296
+ if (prevPos >= 0) {
297
+ var prevCh = stream.string.charAt(prevPos);
298
+ if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
299
+ ignoreUnderscore = true;
300
+ }
301
+ }
302
+ }
303
+ }
304
+ var t = getType(state);
305
+ if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
306
+ if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
307
+ state.strong = false;
308
+ return t;
309
+ } else if (!state.strong && stream.eat(ch)) { // Add STRONG
310
+ state.strong = ch;
311
+ return getType(state);
312
+ } else if (state.em === ch) { // Remove EM
313
+ state.em = false;
314
+ return t;
315
+ } else if (!state.em) { // Add EM
316
+ state.em = ch;
317
+ return getType(state);
318
+ }
319
+ } else if (ch === ' ') {
320
+ if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
321
+ if (stream.peek() === ' ') { // Surrounded by spaces, ignore
322
+ return getType(state);
323
+ } else { // Not surrounded by spaces, back up pointer
324
+ stream.backUp(1);
325
+ }
326
+ }
327
+ }
328
+
329
+ return getType(state);
330
+ }
331
+
332
+ function linkHref(stream, state) {
333
+ // Check if space, and return NULL if so (to avoid marking the space)
334
+ if(stream.eatSpace()){
335
+ return null;
336
+ }
337
+ var ch = stream.next();
338
+ if (ch === '(' || ch === '[') {
339
+ return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
340
+ }
341
+ return 'error';
342
+ }
343
+
344
+ function footnoteLink(stream, state) {
345
+ if (stream.match(/^[^\]]*\]:/, true)) {
346
+ state.f = footnoteUrl;
347
+ return linktext;
348
+ }
349
+ return switchInline(stream, state, inlineNormal);
350
+ }
351
+
352
+ function footnoteUrl(stream, state) {
353
+ // Check if space, and return NULL if so (to avoid marking the space)
354
+ if(stream.eatSpace()){
355
+ return null;
356
+ }
357
+ // Match URL
358
+ stream.match(/^[^\s]+/, true);
359
+ // Check for link title
360
+ if (stream.peek() === undefined) { // End of line, set flag to check next line
361
+ state.linkTitle = true;
362
+ } else { // More content on line, check if link title
363
+ stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
364
+ }
365
+ state.f = state.inline = inlineNormal;
366
+ return linkhref;
367
+ }
368
+
369
+ var savedInlineRE = [];
370
+ function inlineRE(endChar) {
371
+ if (!savedInlineRE[endChar]) {
372
+ // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
373
+ endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
374
+ // Match any non-endChar, escaped character, as well as the closing
375
+ // endChar.
376
+ savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
377
+ }
378
+ return savedInlineRE[endChar];
379
+ }
380
+
381
+ function inlineElement(type, endChar, next) {
382
+ next = next || inlineNormal;
383
+ return function(stream, state) {
384
+ stream.match(inlineRE(endChar));
385
+ state.inline = state.f = next;
386
+ return type;
387
+ };
388
+ }
389
+
390
+ return {
391
+ startState: function() {
392
+ prevLineHasContent = false;
393
+ thisLineHasContent = false;
394
+ return {
395
+ f: blockNormal,
396
+
397
+ block: blockNormal,
398
+ htmlState: CodeMirror.startState(htmlMode),
399
+ indentation: 0,
400
+
401
+ inline: inlineNormal,
402
+ text: handleText,
403
+
404
+ linkText: false,
405
+ linkTitle: false,
406
+ em: false,
407
+ strong: false,
408
+ header: false,
409
+ list: false,
410
+ quote: false
411
+ };
412
+ },
413
+
414
+ copyState: function(s) {
415
+ return {
416
+ f: s.f,
417
+
418
+ block: s.block,
419
+ htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
420
+ indentation: s.indentation,
421
+
422
+ localMode: s.localMode,
423
+ localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
424
+
425
+ inline: s.inline,
426
+ text: s.text,
427
+ linkTitle: s.linkTitle,
428
+ em: s.em,
429
+ strong: s.strong,
430
+ header: s.header,
431
+ list: s.list,
432
+ quote: s.quote,
433
+ md_inside: s.md_inside
434
+ };
435
+ },
436
+
437
+ token: function(stream, state) {
438
+ if (stream.sol()) {
439
+ if (stream.match(/^\s*$/, true)) {
440
+ prevLineHasContent = false;
441
+ return blankLine(state);
442
+ } else {
443
+ if(thisLineHasContent){
444
+ prevLineHasContent = true;
445
+ thisLineHasContent = false;
446
+ }
447
+ thisLineHasContent = true;
448
+ }
449
+
450
+ // Reset state.header
451
+ state.header = false;
452
+
453
+ // Reset state.code
454
+ state.code = false;
455
+
456
+ state.f = state.block;
457
+ var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length;
458
+ var difference = Math.floor((indentation - state.indentation) / 4) * 4;
459
+ if (difference > 4) difference = 4;
460
+ var adjustedIndentation = state.indentation + difference;
461
+ state.indentationDiff = adjustedIndentation - state.indentation;
462
+ state.indentation = adjustedIndentation;
463
+ if (indentation > 0) return null;
464
+ }
465
+ return state.f(stream, state);
466
+ },
467
+
468
+ blankLine: blankLine,
469
+
470
+ getType: getType
471
+ };
472
+
473
+ }, "xml");
474
+
475
+ CodeMirror.defineMIME("text/x-markdown", "markdown");