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 = /^[*-=_]/ , ulRE = /^[*-+]\s+/ , olRE = /^[0-9]\.\s+/ , headerRE = /^(?:\={3,}|-{3,})$/ , codeRE = /^(k:\t|\s{4,})/ , 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 blockNormal(stream, state) { if (stream.match(codeRE)) { stream.skipToEnd(); return code; } if (stream.eatSpace()) { return null; } if (stream.peek() === '#' || stream.match(headerRE)) { stream.skipToEnd(); return header; } if (stream.eat('>')) { state.indentation++; return quote; } if (stream.peek() === '[') { return switchInline(stream, state, footnoteLink); } if (hrRE.test(stream.peek())) { var re = new RegExp('(?:\s*['+stream.peek()+']){3,}$'); if (stream.match(re, true)) { return hr; } } var match; 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) { return state.strong ? (state.em ? emstrong : strong) : (state.em ? em : 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 }; }, 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 }; }, token: function(stream, state) { if (stream.sol()) { state.f = state.block; var previousIndentation = state.indentation , currentIndentation = 0; while (previousIndentation > 0) { if (stream.eat(' ')) { previousIndentation--; currentIndentation++; } else if (previousIndentation >= 4 && stream.eat('\t')) { previousIndentation -= 4; currentIndentation += 4; } else { break; } } state.indentation = currentIndentation; if (currentIndentation > 0) return null; } return state.f(stream, state); }, getType: getType }; }); CodeMirror.defineMIME("text/x-markdown", "markdown"); CodeMirror.defineMIME("application/x-markdown", "markdown");