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