markdown.js
246 lines
| 5.9 KiB
| application/javascript
|
JavascriptLexer
Fernando Perez
|
r4933 | CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { | ||
var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true }); | ||||
var header = 'header' | ||||
Brian Granger
|
r5941 | , code = 'comment' | ||
Fernando Perez
|
r4933 | , quote = 'quote' | ||
Brian Granger
|
r5941 | , list = 'string' | ||
Fernando Perez
|
r4933 | , hr = 'hr' | ||
Brian Granger
|
r5941 | , linktext = 'link' | ||
, linkhref = 'string' | ||||
Fernando Perez
|
r4933 | , em = 'em' | ||
, strong = 'strong' | ||||
, emstrong = 'emstrong'; | ||||
var hrRE = /^[*-=_]/ | ||||
, ulRE = /^[*-+]\s+/ | ||||
Brian Granger
|
r6058 | , olRE = /^[0-9]+\.\s+/ | ||
Fernando Perez
|
r4933 | , 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; | ||||
} | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | if (stream.eatSpace()) { | ||
return null; | ||||
} | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | 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; | ||||
} | ||||
} | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | var match; | ||
if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { | ||||
state.indentation += match[0].length; | ||||
return list; | ||||
} | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | return switchInline(stream, state, state.inline); | ||
} | ||||
function htmlBlock(stream, state) { | ||||
Brian Granger
|
r5941 | var style = htmlMode.token(stream, state.htmlState); | ||
if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { | ||||
state.f = inlineNormal; | ||||
Fernando Perez
|
r4933 | state.block = blockNormal; | ||
} | ||||
Brian Granger
|
r5941 | return style; | ||
Fernando Perez
|
r4933 | } | ||
// Inline | ||||
Brian Granger
|
r5941 | function getType(state) { | ||
return state.strong ? (state.em ? emstrong : strong) | ||||
: (state.em ? em : null); | ||||
} | ||||
Fernando Perez
|
r4933 | |||
Brian Granger
|
r5941 | function handleText(stream, state) { | ||
Fernando Perez
|
r4933 | if (stream.match(textRE, true)) { | ||
Brian Granger
|
r5941 | return getType(state); | ||
Fernando Perez
|
r4933 | } | ||
Brian Granger
|
r5941 | return undefined; | ||
} | ||||
Fernando Perez
|
r4933 | |||
Brian Granger
|
r5941 | function inlineNormal(stream, state) { | ||
var style = state.text(stream, state) | ||||
if (typeof style !== 'undefined') | ||||
return style; | ||||
Fernando Perez
|
r4933 | var ch = stream.next(); | ||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | if (ch === '\\') { | ||
stream.next(); | ||||
Brian Granger
|
r5941 | return getType(state); | ||
Fernando Perez
|
r4933 | } | ||
if (ch === '`') { | ||||
return switchInline(stream, state, inlineElement(code, '`')); | ||||
} | ||||
if (ch === '[') { | ||||
return switchInline(stream, state, linkText); | ||||
} | ||||
Brian Granger
|
r5941 | if (ch === '<' && stream.match(/^\w/, false)) { | ||
stream.backUp(1); | ||||
return switchBlock(stream, state, htmlBlock); | ||||
} | ||||
Fernando Perez
|
r4933 | |||
Brian Granger
|
r5941 | var t = getType(state); | ||
Fernando Perez
|
r4933 | if (ch === '*' || ch === '_') { | ||
if (stream.eat(ch)) { | ||||
Brian Granger
|
r5941 | return (state.strong = !state.strong) ? getType(state) : t; | ||
Fernando Perez
|
r4933 | } | ||
Brian Granger
|
r5941 | return (state.em = !state.em) ? getType(state) : t; | ||
Fernando Perez
|
r4933 | } | ||
Brian Granger
|
r5941 | |||
return getType(state); | ||||
Fernando Perez
|
r4933 | } | ||
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; | ||||
} | ||||
Brian Granger
|
r5941 | 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]; | ||||
} | ||||
Fernando Perez
|
r4933 | function inlineElement(type, endChar, next) { | ||
next = next || inlineNormal; | ||||
return function(stream, state) { | ||||
Brian Granger
|
r5941 | stream.match(inlineRE(endChar)); | ||
state.inline = state.f = next; | ||||
Fernando Perez
|
r4933 | return type; | ||
}; | ||||
} | ||||
return { | ||||
startState: function() { | ||||
return { | ||||
f: blockNormal, | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | block: blockNormal, | ||
htmlState: htmlMode.startState(), | ||||
indentation: 0, | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | inline: inlineNormal, | ||
Brian Granger
|
r5941 | text: handleText, | ||
Fernando Perez
|
r4933 | em: false, | ||
strong: false | ||||
}; | ||||
}, | ||||
copyState: function(s) { | ||||
return { | ||||
f: s.f, | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | block: s.block, | ||
htmlState: CodeMirror.copyState(htmlMode, s.htmlState), | ||||
indentation: s.indentation, | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | inline: s.inline, | ||
Brian Granger
|
r5941 | text: s.text, | ||
Fernando Perez
|
r4933 | em: s.em, | ||
strong: s.strong | ||||
}; | ||||
}, | ||||
token: function(stream, state) { | ||||
if (stream.sol()) { | ||||
Brian Granger
|
r6058 | // Reset EM state | ||
state.em = false; | ||||
// Reset STRONG state | ||||
state.strong = false; | ||||
Fernando Perez
|
r4933 | 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; | ||||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | if (currentIndentation > 0) return null; | ||
} | ||||
return state.f(stream, state); | ||||
Brian Granger
|
r5941 | }, | ||
getType: getType | ||||
Fernando Perez
|
r4933 | }; | ||
}); | ||||
CodeMirror.defineMIME("text/x-markdown", "markdown"); | ||||