Passed
Push — master ( 3324e2...2ba76b )
by Ralf
13:44
created

markdown.js ➔ blockNormal   F

Complexity

Conditions 25

Size

Total Lines 74
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 63
c 0
b 0
f 0
dl 0
loc 74
rs 0
cc 25

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like markdown.js ➔ blockNormal often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
// CodeMirror, copyright (c) by Marijn Haverbeke and others
2
// Distributed under an MIT license: http://codemirror.net/LICENSE
3
4
(function(mod) {
5
  if (typeof exports == "object" && typeof module == "object") // CommonJS
6
    mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
7
  else if (typeof define == "function" && define.amd) // AMD
0 ignored issues
show
Bug introduced by
The variable define seems to be never declared. If this is a global, consider adding a /** global: define */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
8
    define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
9
  else // Plain browser env
10
    mod(CodeMirror);
0 ignored issues
show
Bug introduced by
The variable CodeMirror seems to be never declared. If this is a global, consider adding a /** global: CodeMirror */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
11
})(function(CodeMirror) {
12
"use strict";
13
14
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
15
16
  var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
17
  var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
18
19
  function getMode(name) {
20
    if (CodeMirror.findModeByName) {
21
      var found = CodeMirror.findModeByName(name);
22
      if (found) name = found.mime || found.mimes[0];
23
    }
24
    var mode = CodeMirror.getMode(cmCfg, name);
25
    return mode.name == "null" ? null : mode;
26
  }
27
28
  // Should characters that affect highlighting be highlighted separate?
29
  // Does not include characters that will be output (such as `1.` and `-` for lists)
30
  if (modeCfg.highlightFormatting === undefined)
31
    modeCfg.highlightFormatting = false;
32
33
  // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
34
  // Excess `>` will emit `error` token.
35
  if (modeCfg.maxBlockquoteDepth === undefined)
36
    modeCfg.maxBlockquoteDepth = 0;
37
38
  // Should underscores in words open/close em/strong?
39
  if (modeCfg.underscoresBreakWords === undefined)
40
    modeCfg.underscoresBreakWords = true;
41
42
  // Turn on fenced code blocks? ("```" to start/end)
43
  if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
44
45
  // Turn on task lists? ("- [ ] " and "- [x] ")
46
  if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
47
48
  // Turn on strikethrough syntax
49
  if (modeCfg.strikethrough === undefined)
50
    modeCfg.strikethrough = false;
51
52
  var codeDepth = 0;
53
54
  var header   = 'header'
55
  ,   code     = 'comment'
56
  ,   quote    = 'quote'
57
  ,   list1    = 'variable-2'
58
  ,   list2    = 'variable-3'
59
  ,   list3    = 'keyword'
60
  ,   hr       = 'hr'
61
  ,   image    = 'tag'
62
  ,   formatting = 'formatting'
63
  ,   linkinline = 'link'
64
  ,   linkemail = 'link'
65
  ,   linktext = 'link'
66
  ,   linkhref = 'string'
67
  ,   em       = 'em'
68
  ,   strong   = 'strong'
69
  ,   strikethrough = 'strikethrough';
70
71
  var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
72
  ,   ulRE = /^[*\-+]\s+/
73
  ,   olRE = /^[0-9]+\.\s+/
74
  ,   taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
75
  ,   atxHeaderRE = /^#+/
76
  ,   setextHeaderRE = /^(?:\={1,}|-{1,})$/
77
  ,   textRE = /^[^#!\[\]*_\\<>` "'(~]+/;
78
79
  function switchInline(stream, state, f) {
80
    state.f = state.inline = f;
81
    return f(stream, state);
82
  }
83
84
  function switchBlock(stream, state, f) {
85
    state.f = state.block = f;
86
    return f(stream, state);
87
  }
88
89
90
  // Blocks
91
92
  function blankLine(state) {
93
    // Reset linkTitle state
94
    state.linkTitle = false;
95
    // Reset EM state
96
    state.em = false;
97
    // Reset STRONG state
98
    state.strong = false;
99
    // Reset strikethrough state
100
    state.strikethrough = false;
101
    // Reset state.quote
102
    state.quote = 0;
103
    if (!htmlFound && state.f == htmlBlock) {
104
      state.f = inlineNormal;
105
      state.block = blockNormal;
106
    }
107
    // Reset state.trailingSpace
108
    state.trailingSpace = 0;
109
    state.trailingSpaceNewLine = false;
110
    // Mark this line as blank
111
    state.thisLineHasContent = false;
112
    return null;
113
  }
114
115
  function blockNormal(stream, state) {
116
117
    var sol = stream.sol();
118
119
    var prevLineIsList = (state.list !== false);
120
    if (state.list !== false && state.indentationDiff >= 0) { // Continued list
121
      if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
122
        state.indentation -= state.indentationDiff;
123
      }
124
      state.list = null;
125
    } else if (state.list !== false && state.indentation > 0) {
126
      state.list = null;
127
      state.listDepth = Math.floor(state.indentation / 4);
128
    } else if (state.list !== false) { // No longer a list
129
      state.list = false;
130
      state.listDepth = 0;
131
    }
132
133
    var match = null;
134
    if (state.indentationDiff >= 4) {
135
      state.indentation -= 4;
136
      stream.skipToEnd();
137
      return code;
138
    } else if (stream.eatSpace()) {
139
      return null;
140
    } else if (match = stream.match(atxHeaderRE)) {
141
      state.header = match[0].length <= 6 ? match[0].length : 6;
142
      if (modeCfg.highlightFormatting) state.formatting = "header";
143
      state.f = state.inline;
144
      return getType(state);
145
    } else if (state.prevLineHasContent && (match = stream.match(setextHeaderRE))) {
146
      state.header = match[0].charAt(0) == '=' ? 1 : 2;
147
      if (modeCfg.highlightFormatting) state.formatting = "header";
148
      state.f = state.inline;
149
      return getType(state);
150
    } else if (stream.eat('>')) {
151
      state.indentation++;
152
      state.quote = sol ? 1 : state.quote + 1;
153
      if (modeCfg.highlightFormatting) state.formatting = "quote";
154
      stream.eatSpace();
155
      return getType(state);
156
    } else if (stream.peek() === '[') {
157
      return switchInline(stream, state, footnoteLink);
158
    } else if (stream.match(hrRE, true)) {
159
      return hr;
160
    } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
161
      var listType = null;
0 ignored issues
show
Unused Code introduced by
The assignment to listType seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
162
      if (stream.match(ulRE, true)) {
163
        listType = 'ul';
164
      } else {
165
        stream.match(olRE, true);
166
        listType = 'ol';
167
      }
168
      state.indentation += 4;
169
      state.list = true;
170
      state.listDepth++;
171
      if (modeCfg.taskLists && stream.match(taskListRE, false)) {
172
        state.taskList = true;
173
      }
174
      state.f = state.inline;
175
      if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
176
      return getType(state);
177
    } else if (modeCfg.fencedCodeBlocks && stream.match(/^```[ \t]*([\w+#]*)/, true)) {
178
      // try switching mode
179
      state.localMode = getMode(RegExp.$1);
180
      if (state.localMode) state.localState = state.localMode.startState();
181
      state.f = state.block = local;
182
      if (modeCfg.highlightFormatting) state.formatting = "code-block";
183
      state.code = true;
184
      return getType(state);
185
    }
186
187
    return switchInline(stream, state, state.inline);
188
  }
189
190
  function htmlBlock(stream, state) {
191
    var style = htmlMode.token(stream, state.htmlState);
192
    if ((htmlFound && state.htmlState.tagStart === null && !state.htmlState.context) ||
193
        (state.md_inside && stream.current().indexOf(">") > -1)) {
194
      state.f = inlineNormal;
195
      state.block = blockNormal;
196
      state.htmlState = null;
197
    }
198
    return style;
199
  }
200
201
  function local(stream, state) {
202
    if (stream.sol() && stream.match("```", false)) {
203
      state.localMode = state.localState = null;
204
      state.f = state.block = leavingLocal;
205
      return null;
206
    } else if (state.localMode) {
207
      return state.localMode.token(stream, state.localState);
208
    } else {
209
      stream.skipToEnd();
210
      return code;
211
    }
212
  }
213
214
  function leavingLocal(stream, state) {
215
    stream.match("```");
216
    state.block = blockNormal;
217
    state.f = inlineNormal;
218
    if (modeCfg.highlightFormatting) state.formatting = "code-block";
219
    state.code = true;
220
    var returnType = getType(state);
221
    state.code = false;
222
    return returnType;
223
  }
224
225
  // Inline
226
  function getType(state) {
227
    var styles = [];
228
229
    if (state.formatting) {
230
      styles.push(formatting);
231
232
      if (typeof state.formatting === "string") state.formatting = [state.formatting];
233
234
      for (var i = 0; i < state.formatting.length; i++) {
235
        styles.push(formatting + "-" + state.formatting[i]);
236
237
        if (state.formatting[i] === "header") {
238
          styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
239
        }
240
241
        // Add `formatting-quote` and `formatting-quote-#` for blockquotes
242
        // Add `error` instead if the maximum blockquote nesting depth is passed
243
        if (state.formatting[i] === "quote") {
244
          if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
245
            styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
246
          } else {
247
            styles.push("error");
248
          }
249
        }
250
      }
251
    }
252
253
    if (state.taskOpen) {
254
      styles.push("meta");
255
      return styles.length ? styles.join(' ') : null;
256
    }
257
    if (state.taskClosed) {
258
      styles.push("property");
259
      return styles.length ? styles.join(' ') : null;
260
    }
261
262
    if (state.linkHref) {
263
      styles.push(linkhref);
264
      return styles.length ? styles.join(' ') : null;
265
    }
266
267
    if (state.strong) { styles.push(strong); }
268
    if (state.em) { styles.push(em); }
269
    if (state.strikethrough) { styles.push(strikethrough); }
270
271
    if (state.linkText) { styles.push(linktext); }
272
273
    if (state.code) { styles.push(code); }
274
275
    if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
276
277
    if (state.quote) {
278
      styles.push(quote);
279
280
      // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
281
      if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
282
        styles.push(quote + "-" + state.quote);
283
      } else {
284
        styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
285
      }
286
    }
287
288
    if (state.list !== false) {
289
      var listMod = (state.listDepth - 1) % 3;
290
      if (!listMod) {
291
        styles.push(list1);
292
      } else if (listMod === 1) {
293
        styles.push(list2);
294
      } else {
295
        styles.push(list3);
296
      }
297
    }
298
299
    if (state.trailingSpaceNewLine) {
300
      styles.push("trailing-space-new-line");
301
    } else if (state.trailingSpace) {
302
      styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
303
    }
304
305
    return styles.length ? styles.join(' ') : null;
306
  }
307
308
  function handleText(stream, state) {
309
    if (stream.match(textRE, true)) {
310
      return getType(state);
311
    }
312
    return undefined;
313
  }
314
315
  function inlineNormal(stream, state) {
316
    var style = state.text(stream, state);
317
    if (typeof style !== 'undefined')
318
      return style;
319
320
    if (state.list) { // List marker (*, +, -, 1., etc)
321
      state.list = null;
322
      return getType(state);
323
    }
324
325
    if (state.taskList) {
326
      var taskOpen = stream.match(taskListRE, true)[1] !== "x";
327
      if (taskOpen) state.taskOpen = true;
328
      else state.taskClosed = true;
329
      if (modeCfg.highlightFormatting) state.formatting = "task";
330
      state.taskList = false;
331
      return getType(state);
332
    }
333
334
    state.taskOpen = false;
335
    state.taskClosed = false;
336
337
    if (state.header && stream.match(/^#+$/, true)) {
338
      if (modeCfg.highlightFormatting) state.formatting = "header";
339
      return getType(state);
340
    }
341
342
    // Get sol() value now, before character is consumed
343
    var sol = stream.sol();
344
345
    var ch = stream.next();
346
347
    if (ch === '\\') {
348
      stream.next();
349
      if (modeCfg.highlightFormatting) {
350
        var type = getType(state);
351
        return type ? type + " formatting-escape" : "formatting-escape";
352
      }
353
    }
354
355
    // Matches link titles present on next line
356
    if (state.linkTitle) {
357
      state.linkTitle = false;
358
      var matchCh = ch;
359
      if (ch === '(') {
360
        matchCh = ')';
361
      }
362
      matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
363
      var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
364
      if (stream.match(new RegExp(regex), true)) {
365
        return linkhref;
366
      }
367
    }
368
369
    // If this block is changed, it may need to be updated in GFM mode
370
    if (ch === '`') {
371
      var previousFormatting = state.formatting;
372
      if (modeCfg.highlightFormatting) state.formatting = "code";
373
      var t = getType(state);
374
      var before = stream.pos;
375
      stream.eatWhile('`');
376
      var difference = 1 + stream.pos - before;
377
      if (!state.code) {
378
        codeDepth = difference;
379
        state.code = true;
380
        return getType(state);
381
      } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
382
        if (difference === codeDepth) { // Must be exact
383
          state.code = false;
384
          return t;
385
        }
386
        state.formatting = previousFormatting;
387
        return getType(state);
388
      }
389
    } else if (state.code) {
390
      return getType(state);
391
    }
392
393
    if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
394
      stream.match(/\[[^\]]*\]/);
395
      state.inline = state.f = linkHref;
396
      return image;
397
    }
398
399
    if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
400
      state.linkText = true;
401
      if (modeCfg.highlightFormatting) state.formatting = "link";
402
      return getType(state);
403
    }
404
405
    if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
406
      if (modeCfg.highlightFormatting) state.formatting = "link";
407
      var type = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 350. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
408
      state.linkText = false;
409
      state.inline = state.f = linkHref;
410
      return type;
411
    }
412
413
    if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
414
      state.f = state.inline = linkInline;
415
      if (modeCfg.highlightFormatting) state.formatting = "link";
416
      var type = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 350. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
417
      if (type){
418
        type += " ";
419
      } else {
420
        type = "";
421
      }
422
      return type + linkinline;
423
    }
424
425
    if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
426
      state.f = state.inline = linkInline;
427
      if (modeCfg.highlightFormatting) state.formatting = "link";
428
      var type = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable type already seems to be declared on line 350. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
429
      if (type){
430
        type += " ";
431
      } else {
432
        type = "";
433
      }
434
      return type + linkemail;
435
    }
436
437
    if (ch === '<' && stream.match(/^\w/, false)) {
438
      if (stream.string.indexOf(">") != -1) {
439
        var atts = stream.string.substring(1,stream.string.indexOf(">"));
440
        if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
441
          state.md_inside = true;
442
        }
443
      }
444
      stream.backUp(1);
445
      state.htmlState = CodeMirror.startState(htmlMode);
446
      return switchBlock(stream, state, htmlBlock);
447
    }
448
449
    if (ch === '<' && stream.match(/^\/\w*?>/)) {
450
      state.md_inside = false;
451
      return "tag";
452
    }
453
454
    var ignoreUnderscore = false;
455
    if (!modeCfg.underscoresBreakWords) {
456
      if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
457
        var prevPos = stream.pos - 2;
458
        if (prevPos >= 0) {
459
          var prevCh = stream.string.charAt(prevPos);
460
          if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
461
            ignoreUnderscore = true;
462
          }
463
        }
464
      }
465
    }
466
    if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
467
      if (sol && stream.peek() === ' ') {
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
468
        // Do nothing, surrounded by newline and space
469
      } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
470
        if (modeCfg.highlightFormatting) state.formatting = "strong";
471
        var t = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable t already seems to be declared on line 373. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
472
        state.strong = false;
473
        return t;
474
      } else if (!state.strong && stream.eat(ch)) { // Add STRONG
475
        state.strong = ch;
476
        if (modeCfg.highlightFormatting) state.formatting = "strong";
477
        return getType(state);
478
      } else if (state.em === ch) { // Remove EM
479
        if (modeCfg.highlightFormatting) state.formatting = "em";
480
        var t = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable t already seems to be declared on line 373. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
481
        state.em = false;
482
        return t;
483
      } else if (!state.em) { // Add EM
484
        state.em = ch;
485
        if (modeCfg.highlightFormatting) state.formatting = "em";
486
        return getType(state);
487
      }
488
    } else if (ch === ' ') {
489
      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
490
        if (stream.peek() === ' ') { // Surrounded by spaces, ignore
491
          return getType(state);
492
        } else { // Not surrounded by spaces, back up pointer
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
493
          stream.backUp(1);
494
        }
495
      }
496
    }
497
498
    if (modeCfg.strikethrough) {
499
      if (ch === '~' && stream.eatWhile(ch)) {
500
        if (state.strikethrough) {// Remove strikethrough
501
          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
502
          var t = getType(state);
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable t already seems to be declared on line 373. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
503
          state.strikethrough = false;
504
          return t;
505
        } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
506
          state.strikethrough = true;
507
          if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
508
          return getType(state);
509
        }
510
      } else if (ch === ' ') {
511
        if (stream.match(/^~~/, true)) { // Probably surrounded by space
512
          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
513
            return getType(state);
514
          } else { // Not surrounded by spaces, back up pointer
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
515
            stream.backUp(2);
516
          }
517
        }
518
      }
519
    }
520
521
    if (ch === ' ') {
522
      if (stream.match(/ +$/, false)) {
523
        state.trailingSpace++;
524
      } else if (state.trailingSpace) {
525
        state.trailingSpaceNewLine = true;
526
      }
527
    }
528
529
    return getType(state);
530
  }
531
532
  function linkInline(stream, state) {
533
    var ch = stream.next();
534
535
    if (ch === ">") {
536
      state.f = state.inline = inlineNormal;
537
      if (modeCfg.highlightFormatting) state.formatting = "link";
538
      var type = getType(state);
539
      if (type){
540
        type += " ";
541
      } else {
542
        type = "";
543
      }
544
      return type + linkinline;
545
    }
546
547
    stream.match(/^[^>]+/, true);
548
549
    return linkinline;
550
  }
551
552
  function linkHref(stream, state) {
553
    // Check if space, and return NULL if so (to avoid marking the space)
554
    if(stream.eatSpace()){
555
      return null;
556
    }
557
    var ch = stream.next();
558
    if (ch === '(' || ch === '[') {
559
      state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
560
      if (modeCfg.highlightFormatting) state.formatting = "link-string";
561
      state.linkHref = true;
562
      return getType(state);
563
    }
564
    return 'error';
565
  }
566
567
  function getLinkHrefInside(endChar) {
568
    return function(stream, state) {
569
      var ch = stream.next();
570
571
      if (ch === endChar) {
572
        state.f = state.inline = inlineNormal;
573
        if (modeCfg.highlightFormatting) state.formatting = "link-string";
574
        var returnState = getType(state);
575
        state.linkHref = false;
576
        return returnState;
577
      }
578
579
      if (stream.match(inlineRE(endChar), true)) {
580
        stream.backUp(1);
581
      }
582
583
      state.linkHref = true;
584
      return getType(state);
585
    };
586
  }
587
588
  function footnoteLink(stream, state) {
589
    if (stream.match(/^[^\]]*\]:/, false)) {
590
      state.f = footnoteLinkInside;
591
      stream.next(); // Consume [
592
      if (modeCfg.highlightFormatting) state.formatting = "link";
593
      state.linkText = true;
594
      return getType(state);
595
    }
596
    return switchInline(stream, state, inlineNormal);
597
  }
598
599
  function footnoteLinkInside(stream, state) {
600
    if (stream.match(/^\]:/, true)) {
601
      state.f = state.inline = footnoteUrl;
602
      if (modeCfg.highlightFormatting) state.formatting = "link";
603
      var returnType = getType(state);
604
      state.linkText = false;
605
      return returnType;
606
    }
607
608
    stream.match(/^[^\]]+/, true);
609
610
    return linktext;
611
  }
612
613
  function footnoteUrl(stream, state) {
614
    // Check if space, and return NULL if so (to avoid marking the space)
615
    if(stream.eatSpace()){
616
      return null;
617
    }
618
    // Match URL
619
    stream.match(/^[^\s]+/, true);
620
    // Check for link title
621
    if (stream.peek() === undefined) { // End of line, set flag to check next line
622
      state.linkTitle = true;
623
    } else { // More content on line, check if link title
624
      stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
625
    }
626
    state.f = state.inline = inlineNormal;
627
    return linkhref;
628
  }
629
630
  var savedInlineRE = [];
631
  function inlineRE(endChar) {
632
    if (!savedInlineRE[endChar]) {
633
      // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
634
      endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
635
      // Match any non-endChar, escaped character, as well as the closing
636
      // endChar.
637
      savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
638
    }
639
    return savedInlineRE[endChar];
640
  }
641
642
  var mode = {
643
    startState: function() {
644
      return {
645
        f: blockNormal,
646
647
        prevLineHasContent: false,
648
        thisLineHasContent: false,
649
650
        block: blockNormal,
651
        htmlState: null,
652
        indentation: 0,
653
654
        inline: inlineNormal,
655
        text: handleText,
656
657
        formatting: false,
658
        linkText: false,
659
        linkHref: false,
660
        linkTitle: false,
661
        em: false,
662
        strong: false,
663
        header: 0,
664
        taskList: false,
665
        list: false,
666
        listDepth: 0,
667
        quote: 0,
668
        trailingSpace: 0,
669
        trailingSpaceNewLine: false,
670
        strikethrough: false
671
      };
672
    },
673
674
    copyState: function(s) {
675
      return {
676
        f: s.f,
677
678
        prevLineHasContent: s.prevLineHasContent,
679
        thisLineHasContent: s.thisLineHasContent,
680
681
        block: s.block,
682
        htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
683
        indentation: s.indentation,
684
685
        localMode: s.localMode,
686
        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
687
688
        inline: s.inline,
689
        text: s.text,
690
        formatting: false,
691
        linkTitle: s.linkTitle,
692
        em: s.em,
693
        strong: s.strong,
694
        strikethrough: s.strikethrough,
695
        header: s.header,
696
        taskList: s.taskList,
697
        list: s.list,
698
        listDepth: s.listDepth,
699
        quote: s.quote,
700
        trailingSpace: s.trailingSpace,
701
        trailingSpaceNewLine: s.trailingSpaceNewLine,
702
        md_inside: s.md_inside
703
      };
704
    },
705
706
    token: function(stream, state) {
707
708
      // Reset state.formatting
709
      state.formatting = false;
710
711
      if (stream.sol()) {
712
        var forceBlankLine = !!state.header;
713
714
        // Reset state.header
715
        state.header = 0;
716
717
        if (stream.match(/^\s*$/, true) || forceBlankLine) {
718
          state.prevLineHasContent = false;
719
          blankLine(state);
720
          return forceBlankLine ? this.token(stream, state) : null;
721
        } else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
722
          state.prevLineHasContent = state.thisLineHasContent;
723
          state.thisLineHasContent = true;
724
        }
725
726
        // Reset state.taskList
727
        state.taskList = false;
728
729
        // Reset state.code
730
        state.code = false;
731
732
        // Reset state.trailingSpace
733
        state.trailingSpace = 0;
734
        state.trailingSpaceNewLine = false;
735
736
        state.f = state.block;
737
        var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
738
        var difference = Math.floor((indentation - state.indentation) / 4) * 4;
739
        if (difference > 4) difference = 4;
740
        var adjustedIndentation = state.indentation + difference;
741
        state.indentationDiff = adjustedIndentation - state.indentation;
742
        state.indentation = adjustedIndentation;
743
        if (indentation > 0) return null;
744
      }
745
      return state.f(stream, state);
746
    },
747
748
    innerMode: function(state) {
749
      if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
750
      if (state.localState) return {state: state.localState, mode: state.localMode};
751
      return {state: state, mode: mode};
752
    },
753
754
    blankLine: blankLine,
755
756
    getType: getType,
757
758
    fold: "markdown"
759
  };
760
  return mode;
761
}, "xml");
762
763
CodeMirror.defineMIME("text/x-markdown", "markdown");
764
765
});
766