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