phrebejk@460: CodeMirror.defineMode("xml", function(config, parserConfig) { phrebejk@460: var indentUnit = config.indentUnit; phrebejk@460: var Kludges = parserConfig.htmlMode ? { phrebejk@460: autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, phrebejk@460: 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, phrebejk@460: 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, phrebejk@460: 'track': true, 'wbr': true}, phrebejk@460: implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, phrebejk@460: 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, phrebejk@460: 'th': true, 'tr': true}, phrebejk@460: contextGrabbers: { phrebejk@460: 'dd': {'dd': true, 'dt': true}, phrebejk@460: 'dt': {'dd': true, 'dt': true}, phrebejk@460: 'li': {'li': true}, phrebejk@460: 'option': {'option': true, 'optgroup': true}, phrebejk@460: 'optgroup': {'optgroup': true}, phrebejk@460: 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, phrebejk@460: 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, phrebejk@460: 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, phrebejk@460: 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, phrebejk@460: 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, phrebejk@460: 'rp': {'rp': true, 'rt': true}, phrebejk@460: 'rt': {'rp': true, 'rt': true}, phrebejk@460: 'tbody': {'tbody': true, 'tfoot': true}, phrebejk@460: 'td': {'td': true, 'th': true}, phrebejk@460: 'tfoot': {'tbody': true}, phrebejk@460: 'th': {'td': true, 'th': true}, phrebejk@460: 'thead': {'tbody': true, 'tfoot': true}, phrebejk@460: 'tr': {'tr': true} phrebejk@460: }, phrebejk@460: doNotIndent: {"pre": true}, phrebejk@460: allowUnquoted: true, phrebejk@460: allowMissing: true phrebejk@460: } : { phrebejk@460: autoSelfClosers: {}, phrebejk@460: implicitlyClosed: {}, phrebejk@460: contextGrabbers: {}, phrebejk@460: doNotIndent: {}, phrebejk@460: allowUnquoted: false, phrebejk@460: allowMissing: false phrebejk@460: }; phrebejk@460: var alignCDATA = parserConfig.alignCDATA; phrebejk@460: phrebejk@460: // Return variables for tokenizers phrebejk@460: var tagName, type; phrebejk@460: phrebejk@460: function inText(stream, state) { phrebejk@460: function chain(parser) { phrebejk@460: state.tokenize = parser; phrebejk@460: return parser(stream, state); phrebejk@460: } phrebejk@460: phrebejk@460: var ch = stream.next(); phrebejk@460: if (ch == "<") { phrebejk@460: if (stream.eat("!")) { phrebejk@460: if (stream.eat("[")) { phrebejk@460: if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); phrebejk@460: else return null; phrebejk@460: } phrebejk@460: else if (stream.match("--")) return chain(inBlock("comment", "-->")); phrebejk@460: else if (stream.match("DOCTYPE", true, true)) { phrebejk@460: stream.eatWhile(/[\w\._\-]/); phrebejk@460: return chain(doctype(1)); phrebejk@460: } phrebejk@460: else return null; phrebejk@460: } phrebejk@460: else if (stream.eat("?")) { phrebejk@460: stream.eatWhile(/[\w\._\-]/); phrebejk@460: state.tokenize = inBlock("meta", "?>"); phrebejk@460: return "meta"; phrebejk@460: } phrebejk@460: else { phrebejk@460: var isClose = stream.eat("/"); phrebejk@460: tagName = ""; phrebejk@460: var c; phrebejk@460: while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; phrebejk@460: if (!tagName) return "error"; phrebejk@460: type = isClose ? "closeTag" : "openTag"; phrebejk@460: state.tokenize = inTag; phrebejk@460: return "tag"; phrebejk@460: } phrebejk@460: } phrebejk@460: else if (ch == "&") { phrebejk@460: var ok; phrebejk@460: if (stream.eat("#")) { phrebejk@460: if (stream.eat("x")) { phrebejk@460: ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); phrebejk@460: } else { phrebejk@460: ok = stream.eatWhile(/[\d]/) && stream.eat(";"); phrebejk@460: } phrebejk@460: } else { phrebejk@460: ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); phrebejk@460: } phrebejk@460: return ok ? "atom" : "error"; phrebejk@460: } phrebejk@460: else { phrebejk@460: stream.eatWhile(/[^&<]/); phrebejk@460: return null; phrebejk@460: } phrebejk@460: } phrebejk@460: phrebejk@460: function inTag(stream, state) { phrebejk@460: var ch = stream.next(); phrebejk@460: if (ch == ">" || (ch == "/" && stream.eat(">"))) { phrebejk@460: state.tokenize = inText; phrebejk@460: type = ch == ">" ? "endTag" : "selfcloseTag"; phrebejk@460: return "tag"; phrebejk@460: } phrebejk@460: else if (ch == "=") { phrebejk@460: type = "equals"; phrebejk@460: return null; phrebejk@460: } phrebejk@460: else if (/[\'\"]/.test(ch)) { phrebejk@460: state.tokenize = inAttribute(ch); phrebejk@460: return state.tokenize(stream, state); phrebejk@460: } phrebejk@460: else { phrebejk@460: stream.eatWhile(/[^\s\u00a0=<>\"\']/); phrebejk@460: return "word"; phrebejk@460: } phrebejk@460: } phrebejk@460: phrebejk@460: function inAttribute(quote) { phrebejk@460: return function(stream, state) { phrebejk@460: while (!stream.eol()) { phrebejk@460: if (stream.next() == quote) { phrebejk@460: state.tokenize = inTag; phrebejk@460: break; phrebejk@460: } phrebejk@460: } phrebejk@460: return "string"; phrebejk@460: }; phrebejk@460: } phrebejk@460: phrebejk@460: function inBlock(style, terminator) { phrebejk@460: return function(stream, state) { phrebejk@460: while (!stream.eol()) { phrebejk@460: if (stream.match(terminator)) { phrebejk@460: state.tokenize = inText; phrebejk@460: break; phrebejk@460: } phrebejk@460: stream.next(); phrebejk@460: } phrebejk@460: return style; phrebejk@460: }; phrebejk@460: } phrebejk@460: function doctype(depth) { phrebejk@460: return function(stream, state) { phrebejk@460: var ch; phrebejk@460: while ((ch = stream.next()) != null) { phrebejk@460: if (ch == "<") { phrebejk@460: state.tokenize = doctype(depth + 1); phrebejk@460: return state.tokenize(stream, state); phrebejk@460: } else if (ch == ">") { phrebejk@460: if (depth == 1) { phrebejk@460: state.tokenize = inText; phrebejk@460: break; phrebejk@460: } else { phrebejk@460: state.tokenize = doctype(depth - 1); phrebejk@460: return state.tokenize(stream, state); phrebejk@460: } phrebejk@460: } phrebejk@460: } phrebejk@460: return "meta"; phrebejk@460: }; phrebejk@460: } phrebejk@460: phrebejk@460: var curState, setStyle; phrebejk@460: function pass() { phrebejk@460: for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); phrebejk@460: } phrebejk@460: function cont() { phrebejk@460: pass.apply(null, arguments); phrebejk@460: return true; phrebejk@460: } phrebejk@460: phrebejk@460: function pushContext(tagName, startOfLine) { phrebejk@460: var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); phrebejk@460: curState.context = { phrebejk@460: prev: curState.context, phrebejk@460: tagName: tagName, phrebejk@460: indent: curState.indented, phrebejk@460: startOfLine: startOfLine, phrebejk@460: noIndent: noIndent phrebejk@460: }; phrebejk@460: } phrebejk@460: function popContext() { phrebejk@460: if (curState.context) curState.context = curState.context.prev; phrebejk@460: } phrebejk@460: phrebejk@460: function element(type) { phrebejk@460: if (type == "openTag") { phrebejk@460: curState.tagName = tagName; phrebejk@460: return cont(attributes, endtag(curState.startOfLine)); phrebejk@460: } else if (type == "closeTag") { phrebejk@460: var err = false; phrebejk@460: if (curState.context) { phrebejk@460: if (curState.context.tagName != tagName) { phrebejk@460: if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { phrebejk@460: popContext(); phrebejk@460: } phrebejk@460: err = !curState.context || curState.context.tagName != tagName; phrebejk@460: } phrebejk@460: } else { phrebejk@460: err = true; phrebejk@460: } phrebejk@460: if (err) setStyle = "error"; phrebejk@460: return cont(endclosetag(err)); phrebejk@460: } phrebejk@460: return cont(); phrebejk@460: } phrebejk@460: function endtag(startOfLine) { phrebejk@460: return function(type) { phrebejk@460: var tagName = curState.tagName; phrebejk@460: curState.tagName = null; phrebejk@460: if (type == "selfcloseTag" || phrebejk@460: (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { phrebejk@460: maybePopContext(tagName.toLowerCase()); phrebejk@460: return cont(); phrebejk@460: } phrebejk@460: if (type == "endTag") { phrebejk@460: maybePopContext(tagName.toLowerCase()); phrebejk@460: pushContext(tagName, startOfLine); phrebejk@460: return cont(); phrebejk@460: } phrebejk@460: return cont(); phrebejk@460: }; phrebejk@460: } phrebejk@460: function endclosetag(err) { phrebejk@460: return function(type) { phrebejk@460: if (err) setStyle = "error"; phrebejk@460: if (type == "endTag") { popContext(); return cont(); } phrebejk@460: setStyle = "error"; phrebejk@460: return cont(arguments.callee); phrebejk@460: }; phrebejk@460: } phrebejk@460: function maybePopContext(nextTagName) { phrebejk@460: var parentTagName; phrebejk@460: while (true) { phrebejk@460: if (!curState.context) { phrebejk@460: return; phrebejk@460: } phrebejk@460: parentTagName = curState.context.tagName.toLowerCase(); phrebejk@460: if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || phrebejk@460: !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { phrebejk@460: return; phrebejk@460: } phrebejk@460: popContext(); phrebejk@460: } phrebejk@460: } phrebejk@460: phrebejk@460: function attributes(type) { phrebejk@460: if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} phrebejk@460: if (type == "endTag" || type == "selfcloseTag") return pass(); phrebejk@460: setStyle = "error"; phrebejk@460: return cont(attributes); phrebejk@460: } phrebejk@460: function attribute(type) { phrebejk@460: if (type == "equals") return cont(attvalue, attributes); phrebejk@460: if (!Kludges.allowMissing) setStyle = "error"; phrebejk@460: else if (type == "word") setStyle = "attribute"; phrebejk@460: return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); phrebejk@460: } phrebejk@460: function attvalue(type) { phrebejk@460: if (type == "string") return cont(attvaluemaybe); phrebejk@460: if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} phrebejk@460: setStyle = "error"; phrebejk@460: return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); phrebejk@460: } phrebejk@460: function attvaluemaybe(type) { phrebejk@460: if (type == "string") return cont(attvaluemaybe); phrebejk@460: else return pass(); phrebejk@460: } phrebejk@460: phrebejk@460: return { phrebejk@460: startState: function() { phrebejk@460: return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; phrebejk@460: }, phrebejk@460: phrebejk@460: token: function(stream, state) { phrebejk@460: if (stream.sol()) { phrebejk@460: state.startOfLine = true; phrebejk@460: state.indented = stream.indentation(); phrebejk@460: } phrebejk@460: if (stream.eatSpace()) return null; phrebejk@460: phrebejk@460: setStyle = type = tagName = null; phrebejk@460: var style = state.tokenize(stream, state); phrebejk@460: state.type = type; phrebejk@460: if ((style || type) && style != "comment") { phrebejk@460: curState = state; phrebejk@460: while (true) { phrebejk@460: var comb = state.cc.pop() || element; phrebejk@460: if (comb(type || style)) break; phrebejk@460: } phrebejk@460: } phrebejk@460: state.startOfLine = false; phrebejk@460: return setStyle || style; phrebejk@460: }, phrebejk@460: phrebejk@460: indent: function(state, textAfter, fullLine) { phrebejk@460: var context = state.context; phrebejk@460: if ((state.tokenize != inTag && state.tokenize != inText) || phrebejk@460: context && context.noIndent) phrebejk@460: return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; phrebejk@460: if (alignCDATA && /