text: // CodeMirror, copyright (c) by Marijn Haverbeke and others
  // Distributed under an MIT license: http://codemirror.net/LICENSE
  
  (function(mod) {
    if (typeof exports == "object" && typeof module == "object") // CommonJS
      mod(require("../../lib/codemirror"), require("../xml/xml"), require("../meta"));
    else if (typeof define == "function" && define.amd) // AMD
      define(["../../lib/codemirror", "../xml/xml", "../meta"], mod);
    else // Plain browser env
      mod(CodeMirror);
  })(function(CodeMirror) {
  "use strict";
  
  CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
  
    var htmlFound = CodeMirror.modes.hasOwnProperty("xml");
    var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? {name: "xml", htmlMode: true} : "text/plain");
  
    function getMode(name) {
      if (CodeMirror.findModeByName) {
        var found = CodeMirror.findModeByName(name);
        if (found) name = found.mime || found.mimes[0];
      }
      var mode = CodeMirror.getMode(cmCfg, name);
      return mode.name == "null" ? null : mode;
    }
  
    // Should characters that affect highlighting be highlighted separate?
    // Does not include characters that will be output (such as `1.` and `-` for lists)
    if (modeCfg.highlightFormatting === undefined)
      modeCfg.highlightFormatting = false;
  
    // Maximum number of nested blockquotes. Set to 0 for infinite nesting.
    // Excess `>` will emit `error` token.
    if (modeCfg.maxBlockquoteDepth === undefined)
      modeCfg.maxBlockquoteDepth = 0;
  
    // Should underscores in words open/close em/strong?
    if (modeCfg.underscoresBreakWords === undefined)
      modeCfg.underscoresBreakWords = true;
  
    // Turn on fenced code blocks? ("```" to start/end)
    if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
  
    // Turn on task lists? ("- [ ] " and "- [x] ")
    if (modeCfg.taskLists === undefined) modeCfg.taskLists = false;
  
    // Turn on strikethrough syntax
    if (modeCfg.strikethrough === undefined)
      modeCfg.strikethrough = false;
  
    var codeDepth = 0;
  
    var header   = 'header'
    ,   code     = 'comment'
    ,   quote    = 'quote'
    ,   list1    = 'variable-2'
    ,   list2    = 'variable-3'
    ,   list3    = 'keyword'
    ,   hr       = 'hr'
    ,   image    = 'tag'
    ,   formatting = 'formatting'
    ,   linkinline = 'link'
    ,   linkemail = 'link'
    ,   linktext = 'link'
    ,   linkhref = 'string'
    ,   em       = 'em'
    ,   strong   = 'strong'
    ,   strikethrough = 'strikethrough';
  
    var hrRE = /^([*\-_])(?:\s*\1){2,}\s*$/
    ,   ulRE = /^[*\-+]\s+/
    ,   olRE = /^[0-9]+([.)])\s+/
    ,   taskListRE = /^\[(x| )\](?=\s)/ // Must follow ulRE or olRE
    ,   atxHeaderRE = /^(#+)(?: |$)/
    ,   setextHeaderRE = /^ *(?:\={1,}|-{1,})\s*$/
    ,   textRE = /^[^#!\[\]*_\\<>` "'(~]+/;
  
    function switchInline(stream, state, f) {
      state.f = state.inline = f;
      return f(stream, state);
    }
  
    function switchBlock(stream, state, f) {
      state.f = state.block = f;
      return f(stream, state);
    }
  
  
    // Blocks
  
    function blankLine(state) {
      // Reset linkTitle state
      state.linkTitle = false;
      // Reset EM state
      state.em = false;
      // Reset STRONG state
      state.strong = false;
      // Reset strikethrough state
      state.strikethrough = false;
      // Reset state.quote
      state.quote = 0;
      // Reset state.indentedCode
      state.indentedCode = false;
      if (!htmlFound && state.f == htmlBlock) {
        state.f = inlineNormal;
        state.block = blockNormal;
      }
      // Reset state.trailingSpace
      state.trailingSpace = 0;
      state.trailingSpaceNewLine = false;
      // Mark this line as blank
      state.thisLineHasContent = false;
      return null;
    }
  
    function blockNormal(stream, state) {
  
      var sol = stream.sol();
  
      var prevLineIsList = state.list !== false,
          prevLineIsIndentedCode = state.indentedCode;
  
      state.indentedCode = false;
  
      if (prevLineIsList) {
        if (state.indentationDiff >= 0) { // Continued list
          if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
            state.indentation -= state.indentationDiff;
          }
          state.list = null;
        } else if (state.indentation > 0) {
          state.list = null;
          state.listDepth = Math.floor(state.indentation / 4);
        } else { // No longer a list
          state.list = false;
          state.listDepth = 0;
        }
      }
  
      var match = null;
      if (state.indentationDiff >= 4) {
        stream.skipToEnd();
        if (prevLineIsIndentedCode || !state.prevLineHasContent) {
          state.indentation -= 4;
          state.indentedCode = true;
          return code;
        } else {
          return null;
        }
      } else if (stream.eatSpace()) {
        return null;
      } else if ((match = stream.match(atxHeaderRE)) && match[1].length <= 6) {
        state.header = match[1].length;
        if (modeCfg.highlightFormatting) state.formatting = "header";
        state.f = state.inline;
        return getType(state);
      } else if (state.prevLineHasContent && !state.quote && !prevLineIsList && !prevLineIsIndentedCode && (match = stream.match(setextHeaderRE))) {
        state.header = match[0].charAt(0) == '=' ? 1 : 2;
        if (modeCfg.highlightFormatting) state.formatting = "header";
        state.f = state.inline;
        return getType(state);
      } else if (stream.eat('>')) {
        state.quote = sol ? 1 : state.quote + 1;
        if (modeCfg.highlightFormatting) state.formatting = "quote";
        stream.eatSpace();
        return getType(state);
      } else if (stream.peek() === '[') {
        return switchInline(stream, state, footnoteLink);
      } else if (stream.match(hrRE, true)) {
        state.hr = true;
        return hr;
      } else if ((!state.prevLineHasContent || prevLineIsList) && (stream.match(ulRE, false) || stream.match(olRE, false))) {
        var listType = null;
        if (stream.match(ulRE, true)) {
          listType = 'ul';
        } else {
          stream.match(olRE, true);
          listType = 'ol';
        }
        state.indentation += 4;
        state.list = true;
        state.listDepth++;
        if (modeCfg.taskLists && stream.match(taskListRE, false)) {
          state.taskList = true;
        }
        state.f = state.inline;
        if (modeCfg.highlightFormatting) state.formatting = ["list", "list-" + listType];
        return getType(state);
      } else if (modeCfg.fencedCodeBlocks && stream.match(/^```[ \t]*([\w+#]*)/, true)) {
        // try switching mode
        state.localMode = getMode(RegExp.$1);
        if (state.localMode) state.localState = state.localMode.startState();
        state.f = state.block = local;
        if (modeCfg.highlightFormatting) state.formatting = "code-block";
        state.code = true;
        return getType(state);
      }
  
      return switchInline(stream, state, state.inline);
    }
  
    function htmlBlock(stream, state) {
      var style = htmlMode.token(stream, state.htmlState);
      if ((htmlFound && state.htmlState.tagStart === null &&
           (!state.htmlState.context && state.htmlState.tokenize.isInText)) ||
          (state.md_inside && stream.current().indexOf(">") > -1)) {
        state.f = inlineNormal;
        state.block = blockNormal;
        state.htmlState = null;
      }
      return style;
    }
  
    function local(stream, state) {
      if (stream.sol() && stream.match("```", false)) {
        state.localMode = state.localState = null;
        state.f = state.block = leavingLocal;
        return null;
      } else if (state.localMode) {
        return state.localMode.token(stream, state.localState);
      } else {
        stream.skipToEnd();
        return code;
      }
    }
  
    function leavingLocal(stream, state) {
      stream.match("```");
      state.block = blockNormal;
      state.f = inlineNormal;
      if (modeCfg.highlightFormatting) state.formatting = "code-block";
      state.code = true;
      var returnType = getType(state);
      state.code = false;
      return returnType;
    }
  
    // Inline
    function getType(state) {
      var styles = [];
  
      if (state.formatting) {
        styles.push(formatting);
  
        if (typeof state.formatting === "string") state.formatting = [state.formatting];
  
        for (var i = 0; i < state.formatting.length; i++) {
          styles.push(formatting + "-" + state.formatting[i]);
  
          if (state.formatting[i] === "header") {
            styles.push(formatting + "-" + state.formatting[i] + "-" + state.header);
          }
  
          // Add `formatting-quote` and `formatting-quote-#` for blockquotes
          // Add `error` instead if the maximum blockquote nesting depth is passed
          if (state.formatting[i] === "quote") {
            if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
              styles.push(formatting + "-" + state.formatting[i] + "-" + state.quote);
            } else {
              styles.push("error");
            }
          }
        }
      }
  
      if (state.taskOpen) {
        styles.push("meta");
        return styles.length ? styles.join(' ') : null;
      }
      if (state.taskClosed) {
        styles.push("property");
        return styles.length ? styles.join(' ') : null;
      }
  
      if (state.linkHref) {
        styles.push(linkhref, "url");
      } else { // Only apply inline styles to non-url text
        if (state.strong) { styles.push(strong); }
        if (state.em) { styles.push(em); }
        if (state.strikethrough) { styles.push(strikethrough); }
  
        if (state.linkText) { styles.push(linktext); }
  
        if (state.code) { styles.push(code); }
      }
  
      if (state.header) { styles.push(header); styles.push(header + "-" + state.header); }
  
      if (state.quote) {
        styles.push(quote);
  
        // Add `quote-#` where the maximum for `#` is modeCfg.maxBlockquoteDepth
        if (!modeCfg.maxBlockquoteDepth || modeCfg.maxBlockquoteDepth >= state.quote) {
          styles.push(quote + "-" + state.quote);
        } else {
          styles.push(quote + "-" + modeCfg.maxBlockquoteDepth);
        }
      }
  
      if (state.list !== false) {
        var listMod = (state.listDepth - 1) % 3;
        if (!listMod) {
          styles.push(list1);
        } else if (listMod === 1) {
          styles.push(list2);
        } else {
          styles.push(list3);
        }
      }
  
      if (state.trailingSpaceNewLine) {
        styles.push("trailing-space-new-line");
      } else if (state.trailingSpace) {
        styles.push("trailing-space-" + (state.trailingSpace % 2 ? "a" : "b"));
      }
  
      return styles.length ? styles.join(' ') : null;
    }
  
    function handleText(stream, state) {
      if (stream.match(textRE, true)) {
        return getType(state);
      }
      return undefined;
    }
  
    function inlineNormal(stream, state) {
      var style = state.text(stream, state);
      if (typeof style !== 'undefined')
        return style;
  
      if (state.list) { // List marker (*, +, -, 1., etc)
        state.list = null;
        return getType(state);
      }
  
      if (state.taskList) {
        var taskOpen = stream.match(taskListRE, true)[1] !== "x";
        if (taskOpen) state.taskOpen = true;
        else state.taskClosed = true;
        if (modeCfg.highlightFormatting) state.formatting = "task";
        state.taskList = false;
        return getType(state);
      }
  
      state.taskOpen = false;
      state.taskClosed = false;
  
      if (state.header && stream.match(/^#+$/, true)) {
        if (modeCfg.highlightFormatting) state.formatting = "header";
        return getType(state);
      }
  
      // Get sol() value now, before character is consumed
      var sol = stream.sol();
  
      var ch = stream.next();
  
      if (ch === '\\') {
        stream.next();
        if (modeCfg.highlightFormatting) {
          var type = getType(state);
          return type ? type + " formatting-escape" : "formatting-escape";
        }
      }
  
      // Matches link titles present on next line
      if (state.linkTitle) {
        state.linkTitle = false;
        var matchCh = ch;
        if (ch === '(') {
          matchCh = ')';
        }
        matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
        var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
        if (stream.match(new RegExp(regex), true)) {
          return linkhref;
        }
      }
  
      // If this block is changed, it may need to be updated in GFM mode
      if (ch === '`') {
        var previousFormatting = state.formatting;
        if (modeCfg.highlightFormatting) state.formatting = "code";
        var t = getType(state);
        var before = stream.pos;
        stream.eatWhile('`');
        var difference = 1 + stream.pos - before;
        if (!state.code) {
          codeDepth = difference;
          state.code = true;
          return getType(state);
        } else {
          if (difference === codeDepth) { // Must be exact
            state.code = false;
            return t;
          }
          state.formatting = previousFormatting;
          return getType(state);
        }
      } else if (state.code) {
        return getType(state);
      }
  
      if (ch === '!' && stream.match(/\[[^\]]*\] ?(?:\(|\[)/, false)) {
        stream.match(/\[[^\]]*\]/);
        state.inline = state.f = linkHref;
        return image;
      }
  
      if (ch === '[' && stream.match(/.*\](\(.*\)| ?\[.*\])/, false)) {
        state.linkText = true;
        if (modeCfg.highlightFormatting) state.formatting = "link";
        return getType(state);
      }
  
      if (ch === ']' && state.linkText && stream.match(/\(.*\)| ?\[.*\]/, false)) {
        if (modeCfg.highlightFormatting) state.formatting = "link";
        var type = getType(state);
        state.linkText = false;
        state.inline = state.f = linkHref;
        return type;
      }
  
      if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, false)) {
        state.f = state.inline = linkInline;
        if (modeCfg.highlightFormatting) state.formatting = "link";
        var type = getType(state);
        if (type){
          type += " ";
        } else {
          type = "";
        }
        return type + linkinline;
      }
  
      if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, false)) {
        state.f = state.inline = linkInline;
        if (modeCfg.highlightFormatting) state.formatting = "link";
        var type = getType(state);
        if (type){
          type += " ";
        } else {
          type = "";
        }
        return type + linkemail;
      }
  
      if (ch === '<' && stream.match(/^(!--|\w)/, false)) {
        var end = stream.string.indexOf(">", stream.pos);
        if (end != -1) {
          var atts = stream.string.substring(stream.start, end);
          if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) state.md_inside = true;
        }
        stream.backUp(1);
        state.htmlState = CodeMirror.startState(htmlMode);
        return switchBlock(stream, state, htmlBlock);
      }
  
      if (ch === '<' && stream.match(/^\/\w*?>/)) {
        state.md_inside = false;
        return "tag";
      }
  
      var ignoreUnderscore = false;
      if (!modeCfg.underscoresBreakWords) {
        if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
          var prevPos = stream.pos - 2;
          if (prevPos >= 0) {
            var prevCh = stream.string.charAt(prevPos);
            if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
              ignoreUnderscore = true;
            }
          }
        }
      }
      if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
        if (sol && stream.peek() === ' ') {
          // Do nothing, surrounded by newline and space
        } else if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
          if (modeCfg.highlightFormatting) state.formatting = "strong";
          var t = getType(state);
          state.strong = false;
          return t;
        } else if (!state.strong && stream.eat(ch)) { // Add STRONG
          state.strong = ch;
          if (modeCfg.highlightFormatting) state.formatting = "strong";
          return getType(state);
        } else if (state.em === ch) { // Remove EM
          if (modeCfg.highlightFormatting) state.formatting = "em";
          var t = getType(state);
          state.em = false;
          return t;
        } else if (!state.em) { // Add EM
          state.em = ch;
          if (modeCfg.highlightFormatting) state.formatting = "em";
          return getType(state);
        }
      } else if (ch === ' ') {
        if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
          if (stream.peek() === ' ') { // Surrounded by spaces, ignore
            return getType(state);
          } else { // Not surrounded by spaces, back up pointer
            stream.backUp(1);
          }
        }
      }
  
      if (modeCfg.strikethrough) {
        if (ch === '~' && stream.eatWhile(ch)) {
          if (state.strikethrough) {// Remove strikethrough
            if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
            var t = getType(state);
            state.strikethrough = false;
            return t;
          } else if (stream.match(/^[^\s]/, false)) {// Add strikethrough
            state.strikethrough = true;
            if (modeCfg.highlightFormatting) state.formatting = "strikethrough";
            return getType(state);
          }
        } else if (ch === ' ') {
          if (stream.match(/^~~/, true)) { // Probably surrounded by space
            if (stream.peek() === ' ') { // Surrounded by spaces, ignore
              return getType(state);
            } else { // Not surrounded by spaces, back up pointer
              stream.backUp(2);
            }
          }
        }
      }
  
      if (ch === ' ') {
        if (stream.match(/ +$/, false)) {
          state.trailingSpace++;
        } else if (state.trailingSpace) {
          state.trailingSpaceNewLine = true;
        }
      }
  
      return getType(state);
    }
  
    function linkInline(stream, state) {
      var ch = stream.next();
  
      if (ch === ">") {
        state.f = state.inline = inlineNormal;
        if (modeCfg.highlightFormatting) state.formatting = "link";
        var type = getType(state);
        if (type){
          type += " ";
        } else {
          type = "";
        }
        return type + linkinline;
      }
  
      stream.match(/^[^>]+/, true);
  
      return linkinline;
    }
  
    function linkHref(stream, state) {
      // Check if space, and return NULL if so (to avoid marking the space)
      if(stream.eatSpace()){
        return null;
      }
      var ch = stream.next();
      if (ch === '(' || ch === '[') {
        state.f = state.inline = getLinkHrefInside(ch === "(" ? ")" : "]");
        if (modeCfg.highlightFormatting) state.formatting = "link-string";
        state.linkHref = true;
        return getType(state);
      }
      return 'error';
    }
  
    function getLinkHrefInside(endChar) {
      return function(stream, state) {
        var ch = stream.next();
  
        if (ch === endChar) {
          state.f = state.inline = inlineNormal;
          if (modeCfg.highlightFormatting) state.formatting = "link-string";
          var returnState = getType(state);
          state.linkHref = false;
          return returnState;
        }
  
        if (stream.match(inlineRE(endChar), true)) {
          stream.backUp(1);
        }
  
        state.linkHref = true;
        return getType(state);
      };
    }
  
    function footnoteLink(stream, state) {
      if (stream.match(/^[^\]]*\]:/, false)) {
        state.f = footnoteLinkInside;
        stream.next(); // Consume [
        if (modeCfg.highlightFormatting) state.formatting = "link";
        state.linkText = true;
        return getType(state);
      }
      return switchInline(stream, state, inlineNormal);
    }
  
    function footnoteLinkInside(stream, state) {
      if (stream.match(/^\]:/, true)) {
        state.f = state.inline = footnoteUrl;
        if (modeCfg.highlightFormatting) state.formatting = "link";
        var returnType = getType(state);
        state.linkText = false;
        return returnType;
      }
  
      stream.match(/^[^\]]+/, true);
  
      return linktext;
    }
  
    function footnoteUrl(stream, state) {
      // Check if space, and return NULL if so (to avoid marking the space)
      if(stream.eatSpace()){
        return null;
      }
      // Match URL
      stream.match(/^[^\s]+/, true);
      // Check for link title
      if (stream.peek() === undefined) { // End of line, set flag to check next line
        state.linkTitle = true;
      } else { // More type on line, check if link title
        stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
      }
      state.f = state.inline = inlineNormal;
      return linkhref + " url";
    }
  
    var savedInlineRE = [];
    function inlineRE(endChar) {
      if (!savedInlineRE[endChar]) {
        // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
        endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
        // Match any non-endChar, escaped character, as well as the closing
        // endChar.
        savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
      }
      return savedInlineRE[endChar];
    }
  
    var mode = {
      startState: function() {
        return {
          f: blockNormal,
  
          prevLineHasContent: false,
          thisLineHasContent: false,
  
          block: blockNormal,
          htmlState: null,
          indentation: 0,
  
          inline: inlineNormal,
          text: handleText,
  
          formatting: false,
          linkText: false,
          linkHref: false,
          linkTitle: false,
          em: false,
          strong: false,
          header: 0,
          hr: false,
          taskList: false,
          list: false,
          listDepth: 0,
          quote: 0,
          trailingSpace: 0,
          trailingSpaceNewLine: false,
          strikethrough: false
        };
      },
  
      copyState: function(s) {
        return {
          f: s.f,
  
          prevLineHasContent: s.prevLineHasContent,
          thisLineHasContent: s.thisLineHasContent,
  
          block: s.block,
          htmlState: s.htmlState && CodeMirror.copyState(htmlMode, s.htmlState),
          indentation: s.indentation,
  
          localMode: s.localMode,
          localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
  
          inline: s.inline,
          text: s.text,
          formatting: false,
          linkTitle: s.linkTitle,
          em: s.em,
          strong: s.strong,
          strikethrough: s.strikethrough,
          header: s.header,
          hr: s.hr,
          taskList: s.taskList,
          list: s.list,
          listDepth: s.listDepth,
          quote: s.quote,
          indentedCode: s.indentedCode,
          trailingSpace: s.trailingSpace,
          trailingSpaceNewLine: s.trailingSpaceNewLine,
          md_inside: s.md_inside
        };
      },
  
      token: function(stream, state) {
  
        // Reset state.formatting
        state.formatting = false;
  
        if (stream.sol()) {
          var forceBlankLine = !!state.header || state.hr;
  
          // Reset state.header and state.hr
          state.header = 0;
          state.hr = false;
  
          if (stream.match(/^\s*$/, true) || forceBlankLine) {
            state.prevLineHasContent = false;
            blankLine(state);
            return forceBlankLine ? this.token(stream, state) : null;
          } else {
            state.prevLineHasContent = state.thisLineHasContent;
            state.thisLineHasContent = true;
          }
  
          // Reset state.taskList
          state.taskList = false;
  
          // Reset state.code
          state.code = false;
  
          // Reset state.trailingSpace
          state.trailingSpace = 0;
          state.trailingSpaceNewLine = false;
  
          state.f = state.block;
          var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
          var difference = Math.floor((indentation - state.indentation) / 4) * 4;
          if (difference > 4) difference = 4;
          var adjustedIndentation = state.indentation + difference;
          state.indentationDiff = adjustedIndentation - state.indentation;
          state.indentation = adjustedIndentation;
          if (indentation > 0) return null;
        }
        return state.f(stream, state);
      },
  
      innerMode: function(state) {
        if (state.block == htmlBlock) return {state: state.htmlState, mode: htmlMode};
        if (state.localState) return {state: state.localState, mode: state.localMode};
        return {state: state, mode: mode};
      },
  
      blankLine: blankLine,
  
      getType: getType,
  
      fold: "markdown"
    };
    return mode;
  }, "xml");
  
  CodeMirror.defineMIME("text/x-markdown", "markdown");
  
  });
  