1// CodeMirror, copyright (c) by Marijn Haverbeke and others 2// Distributed under an MIT license: https://codemirror.net/LICENSE 3 4(function(mod) { 5 if (typeof exports == "object" && typeof module == "object") 6 // CommonJS 7 mod(require("../../lib/codemirror")); 8 else if (typeof define == "function" && define.amd) 9 // AMD 10 define(["../../lib/codemirror"], mod); 11 // Plain browser env 12 else mod(CodeMirror); 13})(function(CodeMirror) { 14 "use strict"; 15 16 var htmlConfig = { 17 autoSelfClosers: { 18 area: true, 19 base: true, 20 br: true, 21 col: true, 22 command: true, 23 embed: true, 24 frame: true, 25 hr: true, 26 img: true, 27 input: true, 28 keygen: true, 29 link: true, 30 meta: true, 31 param: true, 32 source: true, 33 track: true, 34 wbr: true, 35 menuitem: true, 36 }, 37 implicitlyClosed: { 38 dd: true, 39 li: true, 40 optgroup: true, 41 option: true, 42 p: true, 43 rp: true, 44 rt: true, 45 tbody: true, 46 td: true, 47 tfoot: true, 48 th: true, 49 tr: true, 50 }, 51 contextGrabbers: { 52 dd: { dd: true, dt: true }, 53 dt: { dd: true, dt: true }, 54 li: { li: true }, 55 option: { option: true, optgroup: true }, 56 optgroup: { optgroup: true }, 57 p: { 58 address: true, 59 article: true, 60 aside: true, 61 blockquote: true, 62 dir: true, 63 div: true, 64 dl: true, 65 fieldset: true, 66 footer: true, 67 form: true, 68 h1: true, 69 h2: true, 70 h3: true, 71 h4: true, 72 h5: true, 73 h6: true, 74 header: true, 75 hgroup: true, 76 hr: true, 77 menu: true, 78 nav: true, 79 ol: true, 80 p: true, 81 pre: true, 82 section: true, 83 table: true, 84 ul: true, 85 }, 86 rp: { rp: true, rt: true }, 87 rt: { rp: true, rt: true }, 88 tbody: { tbody: true, tfoot: true }, 89 td: { td: true, th: true }, 90 tfoot: { tbody: true }, 91 th: { td: true, th: true }, 92 thead: { tbody: true, tfoot: true }, 93 tr: { tr: true }, 94 }, 95 doNotIndent: { pre: true }, 96 allowUnquoted: true, 97 allowMissing: true, 98 caseFold: true, 99 }; 100 101 var xmlConfig = { 102 autoSelfClosers: {}, 103 implicitlyClosed: {}, 104 contextGrabbers: {}, 105 doNotIndent: {}, 106 allowUnquoted: false, 107 allowMissing: false, 108 allowMissingTagName: false, 109 caseFold: false, 110 }; 111 112 CodeMirror.defineMode("xml", function(editorConf, config_) { 113 var indentUnit = editorConf.indentUnit; 114 var config = {}; 115 var defaults = config_.htmlMode ? htmlConfig : xmlConfig; 116 for (var prop in defaults) config[prop] = defaults[prop]; 117 for (var prop in config_) config[prop] = config_[prop]; 118 119 // Return variables for tokenizers 120 var type, setStyle; 121 122 function inText(stream, state) { 123 function chain(parser) { 124 state.tokenize = parser; 125 return parser(stream, state); 126 } 127 128 var ch = stream.next(); 129 if (ch == "<") { 130 if (stream.eat("!")) { 131 if (stream.eat("[")) { 132 if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); 133 else return null; 134 } else if (stream.match("--")) { 135 return chain(inBlock("comment", "-->")); 136 } else if (stream.match("DOCTYPE", true, true)) { 137 stream.eatWhile(/[\w\._\-]/); 138 return chain(doctype(1)); 139 } else { 140 return null; 141 } 142 } else if (stream.eat("?")) { 143 stream.eatWhile(/[\w\._\-]/); 144 state.tokenize = inBlock("meta", "?>"); 145 return "meta"; 146 } else { 147 type = stream.eat("/") ? "closeTag" : "openTag"; 148 state.tokenize = inTag; 149 return "tag bracket"; 150 } 151 } else if (ch == "&") { 152 var ok; 153 if (stream.eat("#")) { 154 if (stream.eat("x")) { 155 ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); 156 } else { 157 ok = stream.eatWhile(/[\d]/) && stream.eat(";"); 158 } 159 } else { 160 ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); 161 } 162 return ok ? "atom" : "error"; 163 } else { 164 stream.eatWhile(/[^&<]/); 165 return null; 166 } 167 } 168 inText.isInText = true; 169 170 function inTag(stream, state) { 171 var ch = stream.next(); 172 if (ch == ">" || (ch == "/" && stream.eat(">"))) { 173 state.tokenize = inText; 174 type = ch == ">" ? "endTag" : "selfcloseTag"; 175 return "tag bracket"; 176 } else if (ch == "=") { 177 type = "equals"; 178 return null; 179 } else if (ch == "<") { 180 state.tokenize = inText; 181 state.state = baseState; 182 state.tagName = state.tagStart = null; 183 var next = state.tokenize(stream, state); 184 return next ? next + " tag error" : "tag error"; 185 } else if (/[\'\"]/.test(ch)) { 186 state.tokenize = inAttribute(ch); 187 state.stringStartCol = stream.column(); 188 return state.tokenize(stream, state); 189 } else { 190 stream.match(/^[^\s\u00a0=<>\"\']*[^\s\u00a0=<>\"\'\/]/); 191 return "word"; 192 } 193 } 194 195 function inAttribute(quote) { 196 var closure = function(stream, state) { 197 while (!stream.eol()) { 198 if (stream.next() == quote) { 199 state.tokenize = inTag; 200 break; 201 } 202 } 203 return "string"; 204 }; 205 closure.isInAttribute = true; 206 return closure; 207 } 208 209 function inBlock(style, terminator) { 210 return function(stream, state) { 211 while (!stream.eol()) { 212 if (stream.match(terminator)) { 213 state.tokenize = inText; 214 break; 215 } 216 stream.next(); 217 } 218 return style; 219 }; 220 } 221 222 function doctype(depth) { 223 return function(stream, state) { 224 var ch; 225 while ((ch = stream.next()) != null) { 226 if (ch == "<") { 227 state.tokenize = doctype(depth + 1); 228 return state.tokenize(stream, state); 229 } else if (ch == ">") { 230 if (depth == 1) { 231 state.tokenize = inText; 232 break; 233 } else { 234 state.tokenize = doctype(depth - 1); 235 return state.tokenize(stream, state); 236 } 237 } 238 } 239 return "meta"; 240 }; 241 } 242 243 function Context(state, tagName, startOfLine) { 244 this.prev = state.context; 245 this.tagName = tagName; 246 this.indent = state.indented; 247 this.startOfLine = startOfLine; 248 if (config.doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.noIndent)) this.noIndent = true; 249 } 250 function popContext(state) { 251 if (state.context) state.context = state.context.prev; 252 } 253 function maybePopContext(state, nextTagName) { 254 var parentTagName; 255 while (true) { 256 if (!state.context) { 257 return; 258 } 259 parentTagName = state.context.tagName; 260 if ( 261 !config.contextGrabbers.hasOwnProperty(parentTagName) || 262 !config.contextGrabbers[parentTagName].hasOwnProperty(nextTagName) 263 ) { 264 return; 265 } 266 popContext(state); 267 } 268 } 269 270 function baseState(type, stream, state) { 271 if (type == "openTag") { 272 state.tagStart = stream.column(); 273 return tagNameState; 274 } else if (type == "closeTag") { 275 return closeTagNameState; 276 } else { 277 return baseState; 278 } 279 } 280 function tagNameState(type, stream, state) { 281 if (type == "word") { 282 state.tagName = stream.current(); 283 setStyle = "tag"; 284 return attrState; 285 } else if (config.allowMissingTagName && type == "endTag") { 286 setStyle = "tag bracket"; 287 return attrState(type, stream, state); 288 } else { 289 setStyle = "error"; 290 return tagNameState; 291 } 292 } 293 function closeTagNameState(type, stream, state) { 294 if (type == "word") { 295 var tagName = stream.current(); 296 if ( 297 state.context && 298 state.context.tagName != tagName && 299 config.implicitlyClosed.hasOwnProperty(state.context.tagName) 300 ) 301 popContext(state); 302 if ((state.context && state.context.tagName == tagName) || config.matchClosing === false) { 303 setStyle = "tag"; 304 return closeState; 305 } else { 306 setStyle = "tag error"; 307 return closeStateErr; 308 } 309 } else if (config.allowMissingTagName && type == "endTag") { 310 setStyle = "tag bracket"; 311 return closeState(type, stream, state); 312 } else { 313 setStyle = "error"; 314 return closeStateErr; 315 } 316 } 317 318 function closeState(type, _stream, state) { 319 if (type != "endTag") { 320 setStyle = "error"; 321 return closeState; 322 } 323 popContext(state); 324 return baseState; 325 } 326 function closeStateErr(type, stream, state) { 327 setStyle = "error"; 328 return closeState(type, stream, state); 329 } 330
331 function attrState(type, _stream, state) { 332 if (type == "word") { 333 setStyle = "attribute"; 334 return attrEqState; 335 } else if (type == "endTag" || type == "selfcloseTag") { 336 var tagName = state.tagName, 337 tagStart = state.tagStart; 338 state.tagName = state.tagStart = null; 339 if (type == "selfcloseTag" || config.autoSelfClosers.hasOwnProperty(tagName)) { 340 maybePopContext(state, tagName); 341 } else { 342 maybePopContext(state, tagName); 343 state.context = new Context(state, tagName, tagStart == state.indented); 344 } 345 return baseState; 346 } 347 setStyle = "error"; 348 return attrState; 349 } 350 function attrEqState(type, stream, state) { 351 if (type == "equals") return attrValueState; 352 if (!config.allowMissing) setStyle = "error"; 353 return attrState(type, stream, state); 354 } 355 function attrValueState(type, stream, state) { 356 if (type == "string") return attrContinuedState; 357 if (type == "word" && config.allowUnquoted) { 358 setStyle = "string"; 359 return attrState; 360 } 361 setStyle = "error"; 362 return attrState(type, stream, state); 363 } 364 function attrContinuedState(type, stream, state) { 365 if (type == "string") return attrContinuedState; 366 return attrState(type, stream, state); 367 } 368 369 return { 370 startState: function(baseIndent) { 371 var state = { 372 tokenize: inText, 373 state: baseState, 374 indented: baseIndent || 0, 375 tagName: null, 376 tagStart: null, 377 context: null, 378 }; 379 if (baseIndent != null) state.baseIndent = baseIndent; 380 return state; 381 }, 382 383 token: function(stream, state) { 384 if (!state.tagName && stream.sol()) state.indented = stream.indentation(); 385 386 if (stream.eatSpace()) return null; 387 type = null; 388 var style = state.tokenize(stream, state); 389 if ((style || type) && style != "comment") { 390 setStyle = null; 391 state.state = state.state(type || style, stream, state); 392 if (setStyle) style = setStyle == "error" ? style + " error" : setStyle; 393 } 394 return style; 395 }, 396 397 indent: function(state, textAfter, fullLine) { 398 var context = state.context; 399 // Indent multi-line strings (e.g. css). 400 if (state.tokenize.isInAttribute) { 401 if (state.tagStart == state.indented) return state.stringStartCol + 1; 402 else return state.indented + indentUnit; 403 } 404 if (context && context.noIndent) return CodeMirror.Pass; 405 if (state.tokenize != inTag && state.tokenize != inText) 406 return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; 407 // Indent the starts of attribute names. 408 if (state.tagName) { 409 if (config.multilineTagIndentPastTag !== false) return state.tagStart + state.tagName.length + 2; 410 else return state.tagStart + indentUnit * (config.multilineTagIndentFactor || 1); 411 } 412 if (config.alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; 413 var tagAfter = textAfter && /^<(\/)?([\w_:\.-]*)/.exec(textAfter); 414 if (tagAfter && tagAfter[1]) { 415 // Closing tag spotted 416 while (context) { 417 if (context.tagName == tagAfter[2]) { 418 context = context.prev; 419 break; 420 } else if (config.implicitlyClosed.hasOwnProperty(context.tagName)) { 421 context = context.prev; 422 } else { 423 break; 424 } 425 } 426 } else if (tagAfter) { 427 // Opening tag spotted 428 while (context) { 429 var grabbers = config.contextGrabbers[context.tagName]; 430 if (grabbers && grabbers.hasOwnProperty(tagAfter[2])) context = context.prev; 431 else break; 432 } 433 } 434 while (context && context.prev && !context.startOfLine) context = context.prev; 435 if (context) return context.indent + indentUnit; 436 else return state.baseIndent || 0; 437 }, 438 439 electricInput: /<\/[\s\w:]+>$/, 440 blockCommentStart: "<!--", 441 blockCommentEnd: "-->", 442 443 configuration: config.htmlMode ? "html" : "xml", 444 helperType: config.htmlMode ? "html" : "xml", 445 446 skipAttribute: function(state) { 447 if (state.state == attrValueState) state.state = attrState; 448 }, 449 }; 450 }); 451 452 CodeMirror.defineMIME("text/xml", "xml"); 453 CodeMirror.defineMIME("application/xml", "xml"); 454 if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) 455 CodeMirror.defineMIME("text/html", { name: "xml", htmlMode: true }); 456});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.