CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); var header = 'header' , code = 'comment' , quote = 'quote' , list = 'string' , hr = 'hr' , linktext = 'link' , linkhref = 'string' , em = 'em' , strong = 'strong' , emstrong = 'emstrong'; var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/ , ulRE = /^[*\-+]\s+/ , olRE = /^[0-9]+\.\s+/ , headerRE = /^(?:\={3,}|-{3,})$/ , 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 EM state state.em = false; // Reset STRONG state state.strong = false; return null; } function blockNormal(stream, state) { var match; if (state.indentationDiff >= 4) { state.indentation -= state.indentationDiff; stream.skipToEnd(); return code; } else if (stream.eatSpace()) { return null; } else if (stream.peek() === '#' || stream.match(headerRE)) { state.header = true; } else if (stream.eat('>')) { state.indentation++; state.quote = true; } else if (stream.peek() === '[') { return switchInline(stream, state, footnoteLink); } else if (stream.match(hrRE, true)) { return hr; } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { state.indentation += match[0].length; return list; } return switchInline(stream, state, state.inline); } function htmlBlock(stream, state) { var style = htmlMode.token(stream, state.htmlState); if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { state.f = inlineNormal; state.block = blockNormal; } return style; } // Inline function getType(state) { var styles = []; if (state.strong) { styles.push(state.em ? emstrong : strong); } else if (state.em) { styles.push(em); } if (state.header) { styles.push(header); } if (state.quote) { styles.push(quote); } 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; var ch = stream.next(); if (ch === '\\') { stream.next(); return getType(state); } if (ch === '`') { return switchInline(stream, state, inlineElement(code, '`')); } if (ch === '[') { return switchInline(stream, state, linkText); } if (ch === '<' && stream.match(/^\w/, false)) { stream.backUp(1); return switchBlock(stream, state, htmlBlock); } var t = getType(state); if (ch === '*' || ch === '_') { if (stream.eat(ch)) { return (state.strong = !state.strong) ? getType(state) : t; } return (state.em = !state.em) ? getType(state) : t; } return getType(state); } function linkText(stream, state) { while (!stream.eol()) { var ch = stream.next(); if (ch === '\\') stream.next(); if (ch === ']') { state.inline = state.f = linkHref; return linktext; } } return linktext; } function linkHref(stream, state) { stream.eatSpace(); var ch = stream.next(); if (ch === '(' || ch === '[') { return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']')); } return 'error'; } function footnoteLink(stream, state) { if (stream.match(/^[^\]]*\]:/, true)) { state.f = footnoteUrl; return linktext; } return switchInline(stream, state, inlineNormal); } function footnoteUrl(stream, state) { stream.eatSpace(); stream.match(/^[^\s]+/, true); state.f = state.inline = inlineNormal; return linkhref; } function inlineRE(endChar) { if (!inlineRE[endChar]) { // match any not-escaped-non-endChar and any escaped char // then match endChar or eol inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)'); } return inlineRE[endChar]; } function inlineElement(type, endChar, next) { next = next || inlineNormal; return function(stream, state) { stream.match(inlineRE(endChar)); state.inline = state.f = next; return type; }; } return { startState: function() { return { f: blockNormal, block: blockNormal, htmlState: htmlMode.startState(), indentation: 0, inline: inlineNormal, text: handleText, em: false, strong: false, header: false, quote: false }; }, copyState: function(s) { return { f: s.f, block: s.block, htmlState: CodeMirror.copyState(htmlMode, s.htmlState), indentation: s.indentation, inline: s.inline, text: s.text, em: s.em, strong: s.strong, header: s.header, quote: s.quote }; }, token: function(stream, state) { if (stream.sol()) { if (stream.match(/^\s*$/, true)) { return blankLine(state); } // Reset state.header state.header = false; // Reset state.quote state.quote = false; state.f = state.block; var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; state.indentationDiff = indentation - state.indentation; state.indentation = indentation; if (indentation > 0) { return null; } } return state.f(stream, state); }, blankLine: blankLine, getType: getType }; }, "xml"); CodeMirror.defineMIME("text/x-markdown", "markdown");