1CodeMirror.defineMode("xml", function(config, parserConfig) { 2 var indentUnit = config.indentUnit; 3 var Kludges = parserConfig.htmlMode ? { 4 autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, 5 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, 6 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, 7 'track': true, 'wbr': true}, 8 implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, 9 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, 10 'th': true, 'tr': true}, 11 contextGrabbers: { 12 'dd': {'dd': true, 'dt': true}, 13 'dt': {'dd': true, 'dt': true}, 14 'li': {'li': true}, 15 'option': {'option': true, 'optgroup': true}, 16 'optgroup': {'optgroup': true}, 17 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, 18 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, 19 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, 20 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, 21 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, 22 'rp': {'rp': true, 'rt': true}, 23 'rt': {'rp': true, 'rt': true}, 24 'tbody': {'tbody': true, 'tfoot': true}, 25 'td': {'td': true, 'th': true}, 26 'tfoot': {'tbody': true}, 27 'th': {'td': true, 'th': true}, 28 'thead': {'tbody': true, 'tfoot': true}, 29 'tr': {'tr': true} 30 }, 31 doNotIndent: {"pre": true}, 32 allowUnquoted: true, 33 allowMissing: true 34 } : { 35 autoSelfClosers: {}, 36 implicitlyClosed: {}, 37 contextGrabbers: {}, 38 doNotIndent: {}, 39 allowUnquoted: false, 40 allowMissing: false 41 }; 42 var alignCDATA = parserConfig.alignCDATA; 43 44 // Return variables for tokenizers 45 var tagName, type; 46 47 function inText(stream, state) { 48 function chain(parser) { 49 state.tokenize = parser; 50 return parser(stream, state); 51 } 52 53 var ch = stream.next(); 54 if (ch == "<") { 55 if (stream.eat("!")) { 56 if (stream.eat("[")) { 57 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); 58 else return null; 59 } 60 else if (stream.match("--")) return chain(inBlock("comment", "-->")); 61 else if (stream.match("DOCTYPE", true, true)) { 62 stream.eatWhile(/[\w\._\-]/); 63 return chain(doctype(1)); 64 } 65 else return null; 66 } 67 else if (stream.eat("?")) { 68 stream.eatWhile(/[\w\._\-]/); 69 state.tokenize = inBlock("meta", "?>"); 70 return "meta"; 71 } 72 else { 73 var isClose = stream.eat("/"); 74 tagName = ""; 75 var c; 76 while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; 77 if (!tagName) return "error"; 78 type = isClose ? "closeTag" : "openTag"; 79 state.tokenize = inTag; 80 return "tag"; 81 } 82 } 83 else if (ch == "&") { 84 var ok; 85 if (stream.eat("#")) { 86 if (stream.eat("x")) { 87 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); 88 } else { 89 ok = stream.eatWhile(/[\d]/) && stream.eat(";"); 90 } 91 } else { 92 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); 93 } 94 return ok ? "atom" : "error"; 95 } 96 else { 97 stream.eatWhile(/[^&<]/); 98 return null; 99 } 100 } 101 102 function inTag(stream, state) { 103 var ch = stream.next(); 104 if (ch == ">" || (ch == "/" && stream.eat(">"))) { 105 state.tokenize = inText; 106 type = ch == ">" ? "endTag" : "selfcloseTag"; 107 return "tag"; 108 } 109 else if (ch == "=") { 110 type = "equals"; 111 return null; 112 } 113 else if (/[\'\"]/.test(ch)) { 114 state.tokenize = inAttribute(ch); 115 return state.tokenize(stream, state); 116 } 117 else { 118 stream.eatWhile(/[^\s\u00a0=<>\"\']/); 119 return "word"; 120 } 121 } 122 123 function inAttribute(quote) { 124 return function(stream, state) { 125 while (!stream.eol()) { 126 if (stream.next() == quote) { 127 state.tokenize = inTag; 128 break; 129 } 130 } 131 return "string"; 132 }; 133 } 134 135 function inBlock(style, terminator) { 136 return function(stream, state) { 137 while (!stream.eol()) { 138 if (stream.match(terminator)) { 139 state.tokenize = inText; 140 break; 141 } 142 stream.next(); 143 } 144 return style; 145 }; 146 } 147 function doctype(depth) { 148 return function(stream, state) { 149 var ch; 150 while ((ch = stream.next()) != null) { 151 if (ch == "<") { 152 state.tokenize = doctype(depth + 1); 153 return state.tokenize(stream, state); 154 } else if (ch == ">") { 155 if (depth == 1) { 156 state.tokenize = inText; 157 break; 158 } else { 159 state.tokenize = doctype(depth - 1); 160 return state.tokenize(stream, state); 161 } 162 } 163 } 164 return "meta"; 165 }; 166 } 167 168 var curState, setStyle; 169 function pass() { 170 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); 171 } 172 function cont() { 173 pass.apply(null, arguments); 174 return true; 175 } 176 177 function pushContext(tagName, startOfLine) { 178 var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); 179 curState.context = { 180 prev: curState.context, 181 tagName: tagName, 182 indent: curState.indented, 183 startOfLine: startOfLine, 184 noIndent: noIndent 185 }; 186 } 187 function popContext() { 188 if (curState.context) curState.context = curState.context.prev; 189 } 190 191 function element(type) { 192 if (type == "openTag") { 193 curState.tagName = tagName; 194 return cont(attributes, endtag(curState.startOfLine)); 195 } else if (type == "closeTag") { 196 var err = false; 197 if (curState.context) { 198 if (curState.context.tagName != tagName) { 199 if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { 200 popContext(); 201 } 202 err = !curState.context || curState.context.tagName != tagName; 203 } 204 } else { 205 err = true; 206 } 207 if (err) setStyle = "error"; 208 return cont(endclosetag(err)); 209 } 210 return cont(); 211 } 212 function endtag(startOfLine) { 213 return function(type) { 214 var tagName = curState.tagName; 215 curState.tagName = null; 216 if (type == "selfcloseTag" || 217 (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { 218 maybePopContext(tagName.toLowerCase()); 219 return cont(); 220 } 221 if (type == "endTag") { 222 maybePopContext(tagName.toLowerCase()); 223 pushContext(tagName, startOfLine); 224 return cont(); 225 } 226 return cont(); 227 }; 228 } 229 function endclosetag(err) { 230 return function(type) { 231 if (err) setStyle = "error"; 232 if (type == "endTag") { popContext(); return cont(); } 233 setStyle = "error"; 234 return cont(arguments.callee); 235 }; 236 } 237 function maybePopContext(nextTagName) { 238 var parentTagName; 239 while (true) { 240 if (!curState.context) { 241 return; 242 } 243 parentTagName = curState.context.tagName.toLowerCase(); 244 if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || 245 !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { 246 return; 247 } 248 popContext(); 249 } 250 } 251 252 function attributes(type) { 253 if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} 254 if (type == "endTag" || type == "selfcloseTag") return pass(); 255 setStyle = "error"; 256 return cont(attributes); 257 } 258 function attribute(type) { 259 if (type == "equals") return cont(attvalue, attributes); 260 if (!Kludges.allowMissing) setStyle = "error"; 261 else if (type == "word") setStyle = "attribute"; 262 return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); 263 } 264 function attvalue(type) { 265 if (type == "string") return cont(attvaluemaybe); 266 if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} 267 setStyle = "error"; 268 return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); 269 } 270 function attvaluemaybe(type) { 271 if (type == "string") return cont(attvaluemaybe); 272 else return pass(); 273 } 274 275 return { 276 startState: function() { 277 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null}; 278 }, 279 280 token: function(stream, state) { 281 if (stream.sol()) { 282 state.startOfLine = true; 283 state.indented = stream.indentation(); 284 } 285 if (stream.eatSpace()) return null; 286 287 setStyle = type = tagName = null; 288 var style = state.tokenize(stream, state); 289 state.type = type; 290 if ((style || type) && style != "comment") { 291 curState = state; 292 while (true) { 293 var comb = state.cc.pop() || element; 294 if (comb(type || style)) break; 295 } 296 } 297 state.startOfLine = false; 298 return setStyle || style; 299 }, 300 301 indent: function(state, textAfter, fullLine) { 302 var context = state.context; 303 if ((state.tokenize != inTag && state.tokenize != inText) || 304 context && context.noIndent) 305 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; 306 if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; 307 if (context && /^<\//.test(textAfter)) 308 context = context.prev; 309 while (context && !context.startOfLine) 310 context = context.prev; 311 if (context) return context.indent + indentUnit; 312 else return 0; 313 }, 314 315 electricChars: "/", 316 317 configuration: parserConfig.htmlMode ? "html" : "xml" 318 }; 319}); 320 321CodeMirror.defineMIME("text/xml", "xml"); 322CodeMirror.defineMIME("application/xml", "xml"); 323if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 324 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true}); 325