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