github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/app/public/codemirror/mode/markdown/markdown.js (about)

     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
     8      define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
     9    else // Plain browser env
    10      mod(CodeMirror);
    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    // Use `fencedCodeBlocks` to configure fenced code blocks. false to
    43    // disable, string to specify a precise regexp that the fence should
    44    // match, and true to allow three or more backticks or tildes (as
    45    // per CommonMark).
    46  
    47    // Turn on task lists? ("- [ ] " and "- [x] ")
    48    if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
    49  
    50    // Turn on strikethrough syntax
    51    if (modeCfg.strikethrough === undefined)
    52      modeCfg.strikethrough = false;
    53  
    54    // Allow token types to be overridden by user-provided token types.
    55    if (modeCfg.tokenTypeOverrides === undefined)
    56      modeCfg.tokenTypeOverrides = {};
    57  
    58    var codeDepth = 0;
    59  
    60    var tokenTypes = {
    61      header: "header",
    62      code: "comment",
    63      quote: "quote",
    64      list1: "variable-2",
    65      list2: "variable-3",
    66      list3: "keyword",
    67      hr: "hr",
    68      image: "tag",
    69      formatting: "formatting",
    70      linkInline: "link",
    71      linkEmail: "link",
    72      linkText: "link",
    73      linkHref: "string",
    74      em: "em",
    75      strong: "strong",
    76      strikethrough: "strikethrough"
    77    };
    78  
    79    for (var tokenType in tokenTypes) {
    80      if (tokenTypes.hasOwnProperty(tokenType) && modeCfg.tokenTypeOverrides[tokenType]) {
    81        tokenTypes[tokenType] = modeCfg.tokenTypeOverrides[tokenType];
    82      }
    83    }
    84  
    85    var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
    86    ,   ulRE = /^[*\-+]\s+/
    87    ,   olRE = /^[0-9]+([.)])\s+/
    88    ,   taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
    89    ,   atxHeaderRE = modeCfg.allowAtxHeaderWithoutSpace ? /^(#+)/ : /^(#+)(?: |$)/
    90    ,   setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
    91    ,   textRE = /^[^#!\[\]*_\\<>` "'(~]+/
    92    ,   fencedCodeRE = new RegExp("^(" + (modeCfg.fencedCodeBlocks === true ? "~~~+|```+" : modeCfg.fencedCodeBlocks) +
    93                                  ")[ \\t]*([\\w+#]*)");
    94  
    95    function switchInline(stream, state, f) {
    96      state.f = state.inline = f;
    97      return f(stream, state);
    98    }
    99  
   100    function switchBlock(stream, state, f) {
   101      state.f = state.block = f;
   102      return f(stream, state);
   103    }
   104  
   105    function lineIsEmpty(line) {
   106      return !line || !/\S/.test(line.string)
   107    }
   108  
   109    // Blocks
   110  
   111    function blankLine(state) {
   112      // Reset linkTitle state
   113      state.linkTitle = false;
   114      // Reset EM state
   115      state.em = false;
   116      // Reset STRONG state
   117      state.strong = false;
   118      // Reset strikethrough state
   119      state.strikethrough = false;
   120      // Reset state.quote
   121      state.quote = 0;
   122      // Reset state.indentedCode
   123      state.indentedCode = false;
   124      if (!htmlFound && state.f == htmlBlock) {
   125        state.f = inlineNormal;
   126        state.block = blockNormal;
   127      }
   128      // Reset state.trailingSpace
   129      state.trailingSpace = 0;
   130      state.trailingSpaceNewLine = false;
   131      // Mark this line as blank
   132      state.prevLine = state.thisLine
   133      state.thisLine = null
   134      return null;
   135    }
   136  
   137    function blockNormal(stream, state) {
   138  
   139      var sol = stream.sol();
   140  
   141      var prevLineIsList = state.list !== false,
   142          prevLineIsIndentedCode = state.indentedCode;
   143  
   144      state.indentedCode = false;
   145  
   146      if (prevLineIsList) {
   147        if (state.indentationDiff >= 0) { // Continued list
   148          if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
   149            state.indentation -= state.indentationDiff;
   150          }
   151          state.list = null;
   152        } else if (state.indentation > 0) {
   153          state.list = null;
   154          state.listDepth = Math.floor(state.indentation / 4);
   155        } else { // No longer a list
   156          state.list = false;
   157          state.listDepth = 0;
   158        }
   159      }
   160  
   161      var match = null;
   162      if (state.indentationDiff >= 4) {
   163        stream.skipToEnd();
   164        if (prevLineIsIndentedCode || lineIsEmpty(state.prevLine)) {
   165          state.indentation -= 4;
   166          state.indentedCode = true;
   167          return tokenTypes.code;
   168        } else {
   169          return null;
   170        }
   171      } else if (stream.eatSpace()) {
   172        return null;
   173      } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
   174        state.header = match[1].length;
   175        if (modeCfg.highlightFormatting) state.formatting = "header";
   176        state.f = state.inline;
   177        return getType(state);
   178      } else if (!lineIsEmpty(state.prevLine) && !state.quote && !prevLineIsList &&
   179                 !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
   180        state.header = match[0].charAt(0) == '=' ? 1 : 2;
   181        if (modeCfg.highlightFormatting) state.formatting = "header";
   182        state.f = state.inline;
   183        return getType(state);
   184      } else if (stream.eat('>')) {
   185        state.quote = sol ? 1 : state.quote + 1;
   186        if (modeCfg.highlightFormatting) state.formatting = "quote";
   187        stream.eatSpace();
   188        return getType(state);
   189      } else if (stream.peek() === '[') {
   190        return switchInline(stream, state, footnoteLink);
   191      } else if (stream.match(hrRE, true)) {
   192        state.hr = true;
   193        return tokenTypes.hr;
   194      } else if ((lineIsEmpty(state.prevLine) || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
   195        var listType = null;
   196        if (stream.match(ulRE, true)) {
   197          listType = 'ul';
   198        } else {
   199          stream.match(olRE, true);
   200          listType = 'ol';
   201        }
   202        state.indentation = stream.column() + stream.current().length;
   203        state.list = true;
   204        state.listDepth++;
   205        if (modeCfg.taskLists && stream.match(taskListRE, false)) {
   206          state.taskList = true;
   207        }
   208        state.f = state.inline;
   209        if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
   210        return getType(state);
   211      } else if (modeCfg.fencedCodeBlocks && (match = stream.match(fencedCodeRE, true))) {
   212        state.fencedChars = match[1]
   213        // try switching mode
   214        state.localMode = getMode(match[2]);
   215        if (state.localMode) state.localState = state.localMode.startState();
   216        state.f = state.block = local;
   217        if (modeCfg.highlightFormatting) state.formatting = "code-block";
   218        state.code = true;
   219        return getType(state);
   220      }
   221  
   222      return switchInline(stream, state, state.inline);
   223    }
   224  
   225    function htmlBlock(stream, state) {
   226      var style = htmlMode.token(stream, state.htmlState);
   227      if ((htmlFound && state.htmlState.tagStart === null &&
   228           (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
   229          (state.md_inside && stream.current().indexOf(">") > -1)) {
   230        state.f = inlineNormal;
   231        state.block = blockNormal;
   232        state.htmlState = null;
   233      }
   234      return style;
   235    }
   236  
   237    function local(stream, state) {
   238      if (stream.sol() && state.fencedChars && stream.match(state.fencedChars, false)) {
   239        state.localMode = state.localState = null;
   240        state.f = state.block = leavingLocal;
   241        return null;
   242      } else if (state.localMode) {
   243        return state.localMode.token(stream, state.localState);
   244      } else {
   245        stream.skipToEnd();
   246        return tokenTypes.code;
   247      }
   248    }
   249  
   250    function leavingLocal(stream, state) {
   251      stream.match(state.fencedChars);
   252      state.block = blockNormal;
   253      state.f = inlineNormal;
   254      state.fencedChars = null;
   255      if (modeCfg.highlightFormatting) state.formatting = "code-block";
   256      state.code = true;
   257      var returnType = getType(state);
   258      state.code = false;
   259      return returnType;
   260    }
   261  
   262    // Inline
   263    function getType(state) {
   264      var styles = [];
   265  
   266      if (state.formatting) {
   267        styles.push(tokenTypes.formatting);
   268  
   269        if (typeof state.formatting === "string") state.formatting = [state.formatting];
   270  
   271        for (var i = 0; i < state.formatting.length; i++) {
   272          styles.push(tokenTypes.formatting + "-" + state.formatting[i]);
   273  
   274          if (state.formatting[i] === "header") {
   275            styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.header);
   276          }
   277  
   278          // Add `formatting-quote` and `formatting-quote-#` for blockquotes
   279          // Add `error` instead if the maximum blockquote nesting depth is passed
   280          if (state.formatting[i] === "quote") {
   281            if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
   282              styles.push(tokenTypes.formatting + "-" + state.formatting[i] + "-" + state.quote);
   283            } else {
   284              styles.push("error");
   285            }
   286          }
   287        }
   288      }
   289  
   290      if (state.taskOpen) {
   291        styles.push("meta");
   292        return styles.length ? styles.join(' ') : null;
   293      }
   294      if (state.taskClosed) {
   295        styles.push("property");
   296        return styles.length ? styles.join(' ') : null;
   297      }
   298  
   299      if (state.linkHref) {
   300        styles.push(tokenTypes.linkHref, "url");
   301      } else { // Only apply inline styles to non-url text
   302        if (state.strong) { styles.push(tokenTypes.strong); }
   303        if (state.em) { styles.push(tokenTypes.em); }
   304        if (state.strikethrough) { styles.push(tokenTypes.strikethrough); }
   305        if (state.linkText) { styles.push(tokenTypes.linkText); }
   306        if (state.code) { styles.push(tokenTypes.code); }
   307      }
   308  
   309      if (state.header) { styles.push(tokenTypes.header, tokenTypes.header + "-" + state.header); }
   310  
   311      if (state.quote) {
   312        styles.push(tokenTypes.quote);
   313  
   314        // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
   315        if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
   316          styles.push(tokenTypes.quote + "-" + state.quote);
   317        } else {
   318          styles.push(tokenTypes.quote + "-" + modeCfg.maxBlockquoteDepth);
   319        }
   320      }
   321  
   322      if (state.list !== false) {
   323        var listMod = (state.listDepth - 1) % 3;
   324        if (!listMod) {
   325          styles.push(tokenTypes.list1);
   326        } else if (listMod === 1) {
   327          styles.push(tokenTypes.list2);
   328        } else {
   329          styles.push(tokenTypes.list3);
   330        }
   331      }
   332  
   333      if (state.trailingSpaceNewLine) {
   334        styles.push("trailing-space-new-line");
   335      } else if (state.trailingSpace) {
   336        styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
   337      }
   338  
   339      return styles.length ? styles.join(' ') : null;
   340    }
   341  
   342    function handleText(stream, state) {
   343      if (stream.match(textRE, true)) {
   344        return getType(state);
   345      }
   346      return undefined;
   347    }
   348  
   349    function inlineNormal(stream, state) {
   350      var style = state.text(stream, state);
   351      if (typeof style !== 'undefined')
   352        return style;
   353  
   354      if (state.list) { // List marker (*, +, -, 1., etc)
   355        state.list = null;
   356        return getType(state);
   357      }
   358  
   359      if (state.taskList) {
   360        var taskOpen = stream.match(taskListRE, true)[1] !== "x";
   361        if (taskOpen) state.taskOpen = true;
   362        else state.taskClosed = true;
   363        if (modeCfg.highlightFormatting) state.formatting = "task";
   364        state.taskList = false;
   365        return getType(state);
   366      }
   367  
   368      state.taskOpen = false;
   369      state.taskClosed = false;
   370  
   371      if (state.header && stream.match(/^#+$/, true)) {
   372        if (modeCfg.highlightFormatting) state.formatting = "header";
   373        return getType(state);
   374      }
   375  
   376      // Get sol() value now, before character is consumed
   377      var sol = stream.sol();
   378  
   379      var ch = stream.next();
   380  
   381      if (ch === '\\') {
   382        stream.next();
   383        if (modeCfg.highlightFormatting) {
   384          var type = getType(state);
   385          var formattingEscape = tokenTypes.formatting + "-escape";
   386          return type ? type + " " + formattingEscape : formattingEscape;
   387        }
   388      }
   389  
   390      // Matches link titles present on next line
   391      if (state.linkTitle) {
   392        state.linkTitle = false;
   393        var matchCh = ch;
   394        if (ch === '(') {
   395          matchCh = ')';
   396        }
   397        matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
   398        var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
   399        if (stream.match(new RegExp(regex), true)) {
   400          return tokenTypes.linkHref;
   401        }
   402      }
   403  
   404      // If this block is changed, it may need to be updated in GFM mode
   405      if (ch === '`') {
   406        var previousFormatting = state.formatting;
   407        if (modeCfg.highlightFormatting) state.formatting = "code";
   408        var t = getType(state);
   409        var before = stream.pos;
   410        stream.eatWhile('`');
   411        var difference = 1 + stream.pos - before;
   412        if (!state.code) {
   413          codeDepth = difference;
   414          state.code = true;
   415          return getType(state);
   416        } else {
   417          if (difference === codeDepth) { // Must be exact
   418            state.code = false;
   419            return t;
   420          }
   421          state.formatting = previousFormatting;
   422          return getType(state);
   423        }
   424      } else if (state.code) {
   425        return getType(state);
   426      }
   427  
   428      if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
   429        stream.match(/\[[^\]]*\]/);
   430        state.inline = state.f = linkHref;
   431        return tokenTypes.image;
   432      }
   433  
   434      if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
   435        state.linkText = true;
   436        if (modeCfg.highlightFormatting) state.formatting = "link";
   437        return getType(state);
   438      }
   439  
   440      if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
   441        if (modeCfg.highlightFormatting) state.formatting = "link";
   442        var type = getType(state);
   443        state.linkText = false;
   444        state.inline = state.f = linkHref;
   445        return type;
   446      }
   447  
   448      if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
   449        state.f = state.inline = linkInline;
   450        if (modeCfg.highlightFormatting) state.formatting = "link";
   451        var type = getType(state);
   452        if (type){
   453          type += " ";
   454        } else {
   455          type = "";
   456        }
   457        return type + tokenTypes.linkInline;
   458      }
   459  
   460      if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
   461        state.f = state.inline = linkInline;
   462        if (modeCfg.highlightFormatting) state.formatting = "link";
   463        var type = getType(state);
   464        if (type){
   465          type += " ";
   466        } else {
   467          type = "";
   468        }
   469        return type + tokenTypes.linkEmail;
   470      }
   471  
   472      if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
   473        var end = stream.string.indexOf(">", stream.pos);
   474        if (end != -1) {
   475          var atts = stream.string.substring(stream.start, end);
   476          if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
   477        }
   478        stream.backUp(1);
   479        state.htmlState = CodeMirror.startState(htmlMode);
   480        return switchBlock(stream, state, htmlBlock);
   481      }
   482  
   483      if (ch === '<' && stream.match(/^\/\w*?>/)) {
   484        state.md_inside = false;
   485        return "tag";
   486      }
   487  
   488      var ignoreUnderscore = false;
   489      if (!modeCfg.underscoresBreakWords) {
   490        if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
   491          var prevPos = stream.pos - 2;
   492          if (prevPos >= 0) {
   493            var prevCh = stream.string.charAt(prevPos);
   494            if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
   495              ignoreUnderscore = true;
   496            }
   497          }
   498        }
   499      }
   500      if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
   501        if (sol && stream.peek() === ' ') {
   502          // Do nothing, surrounded by newline and space
   503        } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
   504          if (modeCfg.highlightFormatting) state.formatting = "strong";
   505          var t = getType(state);
   506          state.strong = false;
   507          return t;
   508        } else if (!state.strong && stream.eat(ch)) { // Add STRONG
   509          state.strong = ch;
   510          if (modeCfg.highlightFormatting) state.formatting = "strong";
   511          return getType(state);
   512        } else if (state.em === ch) { // Remove EM
   513          if (modeCfg.highlightFormatting) state.formatting = "em";
   514          var t = getType(state);
   515          state.em = false;
   516          return t;
   517        } else if (!state.em) { // Add EM
   518          state.em = ch;
   519          if (modeCfg.highlightFormatting) state.formatting = "em";
   520          return getType(state);
   521        }
   522      } else if (ch === ' ') {
   523        if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
   524          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
   525            return getType(state);
   526          } else { // Not surrounded by spaces, back up pointer
   527            stream.backUp(1);
   528          }
   529        }
   530      }
   531  
   532      if (modeCfg.strikethrough) {
   533        if (ch === '~' && stream.eatWhile(ch)) {
   534          if (state.strikethrough) {// Remove strikethrough
   535            if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
   536            var t = getType(state);
   537            state.strikethrough = false;
   538            return t;
   539          } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
   540            state.strikethrough = true;
   541            if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
   542            return getType(state);
   543          }
   544        } else if (ch === ' ') {
   545          if (stream.match(/^~~/, true)) { // Probably surrounded by space
   546            if (stream.peek() === ' ') { // Surrounded by spaces, ignore
   547              return getType(state);
   548            } else { // Not surrounded by spaces, back up pointer
   549              stream.backUp(2);
   550            }
   551          }
   552        }
   553      }
   554  
   555      if (ch === ' ') {
   556        if (stream.match(/ +$/, false)) {
   557          state.trailingSpace++;
   558        } else if (state.trailingSpace) {
   559          state.trailingSpaceNewLine = true;
   560        }
   561      }
   562  
   563      return getType(state);
   564    }
   565  
   566    function linkInline(stream, state) {
   567      var ch = stream.next();
   568  
   569      if (ch === ">") {
   570        state.f = state.inline = inlineNormal;
   571        if (modeCfg.highlightFormatting) state.formatting = "link";
   572        var type = getType(state);
   573        if (type){
   574          type += " ";
   575        } else {
   576          type = "";
   577        }
   578        return type + tokenTypes.linkInline;
   579      }
   580  
   581      stream.match(/^[^>]+/, true);
   582  
   583      return tokenTypes.linkInline;
   584    }
   585  
   586    function linkHref(stream, state) {
   587      // Check if space, and return NULL if so (to avoid marking the space)
   588      if(stream.eatSpace()){
   589        return null;
   590      }
   591      var ch = stream.next();
   592      if (ch === '(' || ch === '[') {
   593        state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
   594        if (modeCfg.highlightFormatting) state.formatting = "link-string";
   595        state.linkHref = true;
   596        return getType(state);
   597      }
   598      return 'error';
   599    }
   600  
   601    function getLinkHrefInside(endChar) {
   602      return function(stream, state) {
   603        var ch = stream.next();
   604  
   605        if (ch === endChar) {
   606          state.f = state.inline = inlineNormal;
   607          if (modeCfg.highlightFormatting) state.formatting = "link-string";
   608          var returnState = getType(state);
   609          state.linkHref = false;
   610          return returnState;
   611        }
   612  
   613        if (stream.match(inlineRE(endChar), true)) {
   614          stream.backUp(1);
   615        }
   616  
   617        state.linkHref = true;
   618        return getType(state);
   619      };
   620    }
   621  
   622    function footnoteLink(stream, state) {
   623      if (stream.match(/^([^\]\\]|\\.)*\]:/, false)) {
   624        state.f = footnoteLinkInside;
   625        stream.next(); // Consume [
   626        if (modeCfg.highlightFormatting) state.formatting = "link";
   627        state.linkText = true;
   628        return getType(state);
   629      }
   630      return switchInline(stream, state, inlineNormal);
   631    }
   632  
   633    function footnoteLinkInside(stream, state) {
   634      if (stream.match(/^\]:/, true)) {
   635        state.f = state.inline = footnoteUrl;
   636        if (modeCfg.highlightFormatting) state.formatting = "link";
   637        var returnType = getType(state);
   638        state.linkText = false;
   639        return returnType;
   640      }
   641  
   642      stream.match(/^([^\]\\]|\\.)+/, true);
   643  
   644      return tokenTypes.linkText;
   645    }
   646  
   647    function footnoteUrl(stream, state) {
   648      // Check if space, and return NULL if so (to avoid marking the space)
   649      if(stream.eatSpace()){
   650        return null;
   651      }
   652      // Match URL
   653      stream.match(/^[^\s]+/, true);
   654      // Check for link title
   655      if (stream.peek() === undefined) { // End of line, set flag to check next line
   656        state.linkTitle = true;
   657      } else { // More content on line, check if link title
   658        stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
   659      }
   660      state.f = state.inline = inlineNormal;
   661      return tokenTypes.linkHref + " url";
   662    }
   663  
   664    var savedInlineRE = [];
   665    function inlineRE(endChar) {
   666      if (!savedInlineRE[endChar]) {
   667        // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
   668        endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
   669        // Match any non-endChar, escaped character, as well as the closing
   670        // endChar.
   671        savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
   672      }
   673      return savedInlineRE[endChar];
   674    }
   675  
   676    var mode = {
   677      startState: function() {
   678        return {
   679          f: blockNormal,
   680  
   681          prevLine: null,
   682          thisLine: null,
   683  
   684          block: blockNormal,
   685          htmlState: null,
   686          indentation: 0,
   687  
   688          inline: inlineNormal,
   689          text: handleText,
   690  
   691          formatting: false,
   692          linkText: false,
   693          linkHref: false,
   694          linkTitle: false,
   695          em: false,
   696          strong: false,
   697          header: 0,
   698          hr: false,
   699          taskList: false,
   700          list: false,
   701          listDepth: 0,
   702          quote: 0,
   703          trailingSpace: 0,
   704          trailingSpaceNewLine: false,
   705          strikethrough: false,
   706          fencedChars: null
   707        };
   708      },
   709  
   710      copyState: function(s) {
   711        return {
   712          f: s.f,
   713  
   714          prevLine: s.prevLine,
   715          thisLine: s.thisLine,
   716  
   717          block: s.block,
   718          htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
   719          indentation: s.indentation,
   720  
   721          localMode: s.localMode,
   722          localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
   723  
   724          inline: s.inline,
   725          text: s.text,
   726          formatting: false,
   727          linkTitle: s.linkTitle,
   728          code: s.code,
   729          em: s.em,
   730          strong: s.strong,
   731          strikethrough: s.strikethrough,
   732          header: s.header,
   733          hr: s.hr,
   734          taskList: s.taskList,
   735          list: s.list,
   736          listDepth: s.listDepth,
   737          quote: s.quote,
   738          indentedCode: s.indentedCode,
   739          trailingSpace: s.trailingSpace,
   740          trailingSpaceNewLine: s.trailingSpaceNewLine,
   741          md_inside: s.md_inside,
   742          fencedChars: s.fencedChars
   743        };
   744      },
   745  
   746      token: function(stream, state) {
   747  
   748        // Reset state.formatting
   749        state.formatting = false;
   750  
   751        if (stream != state.thisLine) {
   752          var forceBlankLine = state.header || state.hr;
   753  
   754          // Reset state.header and state.hr
   755          state.header = 0;
   756          state.hr = false;
   757  
   758          if (stream.match(/^\s*$/, true) || forceBlankLine) {
   759            blankLine(state);
   760            if (!forceBlankLine) return null
   761            state.prevLine = null
   762          }
   763  
   764          state.prevLine = state.thisLine
   765          state.thisLine = stream
   766  
   767          // Reset state.taskList
   768          state.taskList = false;
   769  
   770          // Reset state.trailingSpace
   771          state.trailingSpace = 0;
   772          state.trailingSpaceNewLine = false;
   773  
   774          state.f = state.block;
   775          var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
   776          var difference = Math.floor((indentation - state.indentation) / 4) * 4;
   777          if (difference > 4) difference = 4;
   778          var adjustedIndentation = state.indentation + difference;
   779          state.indentationDiff = adjustedIndentation - state.indentation;
   780          state.indentation = adjustedIndentation;
   781          if (indentation > 0) return null;
   782        }
   783        return state.f(stream, state);
   784      },
   785  
   786      innerMode: function(state) {
   787        if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
   788        if (state.localState) return {state: state.localState, mode: state.localMode};
   789        return {state: state, mode: mode};
   790      },
   791  
   792      blankLine: blankLine,
   793  
   794      getType: getType,
   795  
   796      fold: "markdown"
   797    };
   798    return mode;
   799  }, "xml");
   800  
   801  CodeMirror.defineMIME("text/x-markdown", "markdown");
   802  
   803  });