xml.js (11607B)
1 CodeMirror.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 "tag 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 state.tokenize = inText; 113 var next = state.tokenize(stream, state); 114 return next ? next + " error" : "error"; 115 } else if (/[\'\"]/.test(ch)) { 116 state.tokenize = inAttribute(ch); 117 state.stringStartCol = stream.column(); 118 return state.tokenize(stream, state); 119 } else { 120 stream.eatWhile(/[^\s\u00a0=<>\"\']/); 121 return "word"; 122 } 123 } 124 125 function inAttribute(quote) { 126 var closure = function(stream, state) { 127 while (!stream.eol()) { 128 if (stream.next() == quote) { 129 state.tokenize = inTag; 130 break; 131 } 132 } 133 return "string"; 134 }; 135 closure.isInAttribute = true; 136 return closure; 137 } 138 139 function inBlock(style, terminator) { 140 return function(stream, state) { 141 while (!stream.eol()) { 142 if (stream.match(terminator)) { 143 state.tokenize = inText; 144 break; 145 } 146 stream.next(); 147 } 148 return style; 149 }; 150 } 151 function doctype(depth) { 152 return function(stream, state) { 153 var ch; 154 while ((ch = stream.next()) != null) { 155 if (ch == "<") { 156 state.tokenize = doctype(depth + 1); 157 return state.tokenize(stream, state); 158 } else if (ch == ">") { 159 if (depth == 1) { 160 state.tokenize = inText; 161 break; 162 } else { 163 state.tokenize = doctype(depth - 1); 164 return state.tokenize(stream, state); 165 } 166 } 167 } 168 return "meta"; 169 }; 170 } 171 172 var curState, curStream, setStyle; 173 function pass() { 174 for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); 175 } 176 function cont() { 177 pass.apply(null, arguments); 178 return true; 179 } 180 181 function pushContext(tagName, startOfLine) { 182 var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); 183 curState.context = { 184 prev: curState.context, 185 tagName: tagName, 186 indent: curState.indented, 187 startOfLine: startOfLine, 188 noIndent: noIndent 189 }; 190 } 191 function popContext() { 192 if (curState.context) curState.context = curState.context.prev; 193 } 194 195 function element(type) { 196 if (type == "openTag") { 197 curState.tagName = tagName; 198 curState.tagStart = curStream.column(); 199 return cont(attributes, endtag(curState.startOfLine)); 200 } else if (type == "closeTag") { 201 var err = false; 202 if (curState.context) { 203 if (curState.context.tagName != tagName) { 204 if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { 205 popContext(); 206 } 207 err = !curState.context || curState.context.tagName != tagName; 208 } 209 } else { 210 err = true; 211 } 212 if (err) setStyle = "error"; 213 return cont(endclosetag(err)); 214 } 215 return cont(); 216 } 217 function endtag(startOfLine) { 218 return function(type) { 219 var tagName = curState.tagName; 220 curState.tagName = curState.tagStart = null; 221 if (type == "selfcloseTag" || 222 (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { 223 maybePopContext(tagName.toLowerCase()); 224 return cont(); 225 } 226 if (type == "endTag") { 227 maybePopContext(tagName.toLowerCase()); 228 pushContext(tagName, startOfLine); 229 return cont(); 230 } 231 return cont(); 232 }; 233 } 234 function endclosetag(err) { 235 return function(type) { 236 if (err) setStyle = "error"; 237 if (type == "endTag") { popContext(); return cont(); } 238 setStyle = "error"; 239 return cont(arguments.callee); 240 }; 241 } 242 function maybePopContext(nextTagName) { 243 var parentTagName; 244 while (true) { 245 if (!curState.context) { 246 return; 247 } 248 parentTagName = curState.context.tagName.toLowerCase(); 249 if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || 250 !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { 251 return; 252 } 253 popContext(); 254 } 255 } 256 257 function attributes(type) { 258 if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} 259 if (type == "endTag" || type == "selfcloseTag") return pass(); 260 setStyle = "error"; 261 return cont(attributes); 262 } 263 function attribute(type) { 264 if (type == "equals") return cont(attvalue, attributes); 265 if (!Kludges.allowMissing) setStyle = "error"; 266 else if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} 267 return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); 268 } 269 function attvalue(type) { 270 if (type == "string") return cont(attvaluemaybe); 271 if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} 272 setStyle = "error"; 273 return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); 274 } 275 function attvaluemaybe(type) { 276 if (type == "string") return cont(attvaluemaybe); 277 else return pass(); 278 } 279 280 return { 281 startState: function() { 282 return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null}; 283 }, 284 285 token: function(stream, state) { 286 if (!state.tagName && stream.sol()) { 287 state.startOfLine = true; 288 state.indented = stream.indentation(); 289 } 290 if (stream.eatSpace()) return null; 291 292 setStyle = type = tagName = null; 293 var style = state.tokenize(stream, state); 294 state.type = type; 295 if ((style || type) && style != "comment") { 296 curState = state; curStream = stream; 297 while (true) { 298 var comb = state.cc.pop() || element; 299 if (comb(type || style)) break; 300 } 301 } 302 state.startOfLine = false; 303 if (setStyle) 304 style = setStyle == "error" ? style + " error" : setStyle; 305 return style; 306 }, 307 308 indent: function(state, textAfter, fullLine) { 309 var context = state.context; 310 // Indent multi-line strings (e.g. css). 311 if (state.tokenize.isInAttribute) { 312 return state.stringStartCol + 1; 313 } 314 if ((state.tokenize != inTag && state.tokenize != inText) || 315 context && context.noIndent) 316 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; 317 // Indent the starts of attribute names. 318 if (state.tagName) { 319 if (multilineTagIndentPastTag) 320 return state.tagStart + state.tagName.length + 2; 321 else 322 return state.tagStart + indentUnit * multilineTagIndentFactor; 323 } 324 if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; 325 if (context && /^<\//.test(textAfter)) 326 context = context.prev; 327 while (context && !context.startOfLine) 328 context = context.prev; 329 if (context) return context.indent + indentUnit; 330 else return 0; 331 }, 332 333 electricChars: "/", 334 blockCommentStart: "<!--", 335 blockCommentEnd: "-->", 336 337 configuration: parserConfig.htmlMode ? "html" : "xml", 338 helperType: parserConfig.htmlMode ? "html" : "xml" 339 }; 340 }); 341 342 CodeMirror.defineMIME("text/xml", "xml"); 343 CodeMirror.defineMIME("application/xml", "xml"); 344 if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 345 CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});