markdown.js
268 lines
| 6.8 KiB
| application/javascript
|
JavascriptLexer
Fernando Perez
|
r4933 | CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) { | ||
Matthias BUSSONNIER
|
r8053 | var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html"); | ||
var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain"); | ||||
Fernando Perez
|
r4933 | |||
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'; | ||||
Matthias BUSSONNIER
|
r8053 | var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/ | ||
, ulRE = /^[*\-+]\s+/ | ||||
Brian Granger
|
r6058 | , olRE = /^[0-9]+\.\s+/ | ||
Fernando Perez
|
r4933 | , 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 | ||||
Matthias BUSSONNIER
|
r8053 | function blankLine(state) { | ||
// Reset EM state | ||||
state.em = false; | ||||
// Reset STRONG state | ||||
state.strong = false; | ||||
if (!htmlFound && state.f == htmlBlock) { | ||||
state.f = inlineNormal; | ||||
state.block = blockNormal; | ||||
} | ||||
return null; | ||||
} | ||||
Fernando Perez
|
r4933 | function blockNormal(stream, state) { | ||
Matthias BUSSONNIER
|
r8053 | var match; | ||
if (state.indentationDiff >= 4) { | ||||
state.indentation -= state.indentationDiff; | ||||
Fernando Perez
|
r4933 | stream.skipToEnd(); | ||
return code; | ||||
Matthias BUSSONNIER
|
r8053 | } else if (stream.eatSpace()) { | ||
Fernando Perez
|
r4933 | return null; | ||
Matthias BUSSONNIER
|
r8053 | } else if (stream.peek() === '#' || stream.match(headerRE)) { | ||
state.header = true; | ||||
} else if (stream.eat('>')) { | ||||
Fernando Perez
|
r4933 | state.indentation++; | ||
Matthias BUSSONNIER
|
r8053 | state.quote = true; | ||
} else if (stream.peek() === '[') { | ||||
Fernando Perez
|
r4933 | return switchInline(stream, state, footnoteLink); | ||
Matthias BUSSONNIER
|
r8053 | } else if (stream.match(hrRE, true)) { | ||
return hr; | ||||
} else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) { | ||||
Fernando Perez
|
r4933 | 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); | ||
Matthias BUSSONNIER
|
r8053 | if (htmlFound && style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) { | ||
Brian Granger
|
r5941 | state.f = inlineNormal; | ||
Fernando Perez
|
r4933 | state.block = blockNormal; | ||
} | ||||
Matthias BUSSONNIER
|
r8053 | if (state.md_inside && stream.current().indexOf(">")!=-1) { | ||
state.f = inlineNormal; | ||||
state.block = blockNormal; | ||||
state.htmlState.context = undefined; | ||||
} | ||||
Brian Granger
|
r5941 | return style; | ||
Fernando Perez
|
r4933 | } | ||
// Inline | ||||
Brian Granger
|
r5941 | function getType(state) { | ||
Matthias BUSSONNIER
|
r8053 | 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; | ||||
Brian Granger
|
r5941 | } | ||
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)) { | ||
Matthias BUSSONNIER
|
r8053 | var md_inside = false; | ||
if (stream.string.indexOf(">")!=-1) { | ||||
var atts = stream.string.substring(1,stream.string.indexOf(">")); | ||||
if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) { | ||||
state.md_inside = true; | ||||
} | ||||
} | ||||
Brian Granger
|
r5941 | stream.backUp(1); | ||
return switchBlock(stream, state, htmlBlock); | ||||
} | ||||
Matthias BUSSONNIER
|
r8053 | |||
if (ch === '<' && stream.match(/^\/\w*?>/)) { | ||||
state.md_inside = false; | ||||
return "tag"; | ||||
} | ||||
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, | ||
Matthias BUSSONNIER
|
r8053 | htmlState: CodeMirror.startState(htmlMode), | ||
Fernando Perez
|
r4933 | indentation: 0, | ||
Brian Granger
|
r5941 | |||
Fernando Perez
|
r4933 | inline: inlineNormal, | ||
Brian Granger
|
r5941 | text: handleText, | ||
Fernando Perez
|
r4933 | em: false, | ||
Matthias BUSSONNIER
|
r8053 | strong: false, | ||
header: false, | ||||
quote: false | ||||
Fernando Perez
|
r4933 | }; | ||
}, | ||||
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, | ||||
Matthias BUSSONNIER
|
r8053 | |||
Fernando Perez
|
r4933 | inline: s.inline, | ||
Brian Granger
|
r5941 | text: s.text, | ||
Fernando Perez
|
r4933 | em: s.em, | ||
Matthias BUSSONNIER
|
r8053 | strong: s.strong, | ||
header: s.header, | ||||
quote: s.quote, | ||||
md_inside: s.md_inside | ||||
Fernando Perez
|
r4933 | }; | ||
}, | ||||
token: function(stream, state) { | ||||
if (stream.sol()) { | ||||
Matthias BUSSONNIER
|
r8053 | if (stream.match(/^\s*$/, true)) { return blankLine(state); } | ||
// Reset state.header | ||||
state.header = false; | ||||
// Reset state.quote | ||||
state.quote = false; | ||||
Fernando Perez
|
r4933 | state.f = state.block; | ||
Matthias BUSSONNIER
|
r8053 | var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, ' ').length; | ||
state.indentationDiff = indentation - state.indentation; | ||||
state.indentation = indentation; | ||||
if (indentation > 0) { return null; } | ||||
Fernando Perez
|
r4933 | } | ||
return state.f(stream, state); | ||||
Brian Granger
|
r5941 | }, | ||
Matthias BUSSONNIER
|
r8053 | blankLine: blankLine, | ||
Brian Granger
|
r5941 | getType: getType | ||
Fernando Perez
|
r4933 | }; | ||
Matthias BUSSONNIER
|
r8053 | }, "xml"); | ||
Fernando Perez
|
r4933 | |||
CodeMirror.defineMIME("text/x-markdown", "markdown"); | ||||