Unreviewed, rolling out r152598.
[WebKit-https.git] / Source / WebInspectorUI / UserInterface / External / CodeMirror / xml.js
1 CodeMirror.defineMode("xml", function(config, parserConfig) {
2   var indentUnit = config.indentUnit;
3   var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1;
4
5   var Kludges = parserConfig.htmlMode ? {
6     autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true,
7                       'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true,
8                       'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true,
9                       'track': true, 'wbr': true},
10     implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true,
11                        'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true,
12                        'th': true, 'tr': true},
13     contextGrabbers: {
14       'dd': {'dd': true, 'dt': true},
15       'dt': {'dd': true, 'dt': true},
16       'li': {'li': true},
17       'option': {'option': true, 'optgroup': true},
18       'optgroup': {'optgroup': true},
19       'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true,
20             'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true,
21             'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true,
22             'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true,
23             'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true},
24       'rp': {'rp': true, 'rt': true},
25       'rt': {'rp': true, 'rt': true},
26       'tbody': {'tbody': true, 'tfoot': true},
27       'td': {'td': true, 'th': true},
28       'tfoot': {'tbody': true},
29       'th': {'td': true, 'th': true},
30       'thead': {'tbody': true, 'tfoot': true},
31       'tr': {'tr': true}
32     },
33     doNotIndent: {"pre": true},
34     allowUnquoted: true,
35     allowMissing: true
36   } : {
37     autoSelfClosers: {},
38     implicitlyClosed: {},
39     contextGrabbers: {},
40     doNotIndent: {},
41     allowUnquoted: false,
42     allowMissing: false
43   };
44   var alignCDATA = parserConfig.alignCDATA;
45
46   // Return variables for tokenizers
47   var tagName, type;
48
49   function inText(stream, state) {
50     function chain(parser) {
51       state.tokenize = parser;
52       return parser(stream, state);
53     }
54
55     var ch = stream.next();
56     if (ch == "<") {
57       if (stream.eat("!")) {
58         if (stream.eat("[")) {
59           if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
60           else return null;
61         }
62         else if (stream.match("--")) return chain(inBlock("comment", "-->"));
63         else if (stream.match("DOCTYPE", true, true)) {
64           stream.eatWhile(/[\w\._\-]/);
65           return chain(doctype(1));
66         }
67         else return null;
68       }
69       else if (stream.eat("?")) {
70         stream.eatWhile(/[\w\._\-]/);
71         state.tokenize = inBlock("meta", "?>");
72         return "meta";
73       }
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     }
85     else if (ch == "&") {
86       var ok;
87       if (stream.eat("#")) {
88         if (stream.eat("x")) {
89           ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
90         } else {
91           ok = stream.eatWhile(/[\d]/) && stream.eat(";");
92         }
93       } else {
94         ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
95       }
96       return ok ? "atom" : "error";
97     }
98     else {
99       stream.eatWhile(/[^&<]/);
100       return null;
101     }
102   }
103
104   function inTag(stream, state) {
105     var ch = stream.next();
106     if (ch == ">" || (ch == "/" && stream.eat(">"))) {
107       state.tokenize = inText;
108       type = ch == ">" ? "endTag" : "selfcloseTag";
109       return "tag";
110     }
111     else if (ch == "=") {
112       type = "equals";
113       return null;
114     }
115     else if (/[\'\"]/.test(ch)) {
116       state.tokenize = inAttribute(ch);
117       return state.tokenize(stream, state);
118     }
119     else {
120       stream.eatWhile(/[^\s\u00a0=<>\"\']/);
121       return "word";
122     }
123   }
124
125   function inAttribute(quote) {
126     return 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   }
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";
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       if ((state.tokenize != inTag && state.tokenize != inText) ||
307           context && context.noIndent)
308         return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
309       if (state.tagName) return state.tagStart + indentUnit * multilineTagIndentFactor;
310       if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
311       if (context && /^<\//.test(textAfter))
312         context = context.prev;
313       while (context && !context.startOfLine)
314         context = context.prev;
315       if (context) return context.indent + indentUnit;
316       else return 0;
317     },
318
319     electricChars: "/",
320     blockCommentStart: "<!--",
321     blockCommentEnd: "-->",
322
323     configuration: parserConfig.htmlMode ? "html" : "xml"
324   };
325 });
326
327 CodeMirror.defineMIME("text/xml", "xml");
328 CodeMirror.defineMIME("application/xml", "xml");
329 if (!CodeMirror.mimeModes.hasOwnProperty("text/html"))
330   CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});