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