1 CodeMirror
.defineMode("xml", function(config
, parserConfig
) {
2 var indentUnit
= config
.indentUnit
;
3 var multilineTagIndentFactor
= parserConfig
.multilineTagIndentFactor
|| 1;
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},
14 'dd': {'dd': true, 'dt': true},
15 'dt': {'dd': true, 'dt': 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},
33 doNotIndent
: {"pre": true},
44 var alignCDATA
= parserConfig
.alignCDATA
;
46 // Return variables for tokenizers
49 function inText(stream
, state
) {
50 function chain(parser
) {
51 state
.tokenize
= parser
;
52 return parser(stream
, state
);
55 var ch
= stream
.next();
57 if (stream
.eat("!")) {
58 if (stream
.eat("[")) {
59 if (stream
.match("CDATA[")) return chain(inBlock("atom", "]]>"));
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));
69 else if (stream
.eat("?")) {
70 stream
.eatWhile(/[\w\._\-]/);
71 state
.tokenize
= inBlock("meta", "?>");
75 var isClose
= stream
.eat("/");
78 while ((c
= stream
.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName
+= c
;
79 if (!tagName
) return "error";
80 type
= isClose
? "closeTag" : "openTag";
81 state
.tokenize
= inTag
;
87 if (stream
.eat("#")) {
88 if (stream
.eat("x")) {
89 ok
= stream
.eatWhile(/[a-fA-F\d]/) && stream
.eat(";");
91 ok
= stream
.eatWhile(/[\d]/) && stream
.eat(";");
94 ok
= stream
.eatWhile(/[\w\.\-:]/) && stream
.eat(";");
96 return ok
? "atom" : "error";
99 stream
.eatWhile(/[^&<]/);
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";
111 else if (ch
== "=") {
115 else if (/[\'\"]/.test(ch
)) {
116 state
.tokenize
= inAttribute(ch
);
117 return state
.tokenize(stream
, state
);
120 stream
.eatWhile(/[^\s\u00a0=<>\"\']/);
125 function inAttribute(quote
) {
126 return function(stream
, state
) {
127 while (!stream
.eol()) {
128 if (stream
.next() == quote
) {
129 state
.tokenize
= inTag
;
137 function inBlock(style
, terminator
) {
138 return function(stream
, state
) {
139 while (!stream
.eol()) {
140 if (stream
.match(terminator
)) {
141 state
.tokenize
= inText
;
149 function doctype(depth
) {
150 return function(stream
, state
) {
152 while ((ch
= stream
.next()) != null) {
154 state
.tokenize
= doctype(depth
+ 1);
155 return state
.tokenize(stream
, state
);
156 } else if (ch
== ">") {
158 state
.tokenize
= inText
;
161 state
.tokenize
= doctype(depth
- 1);
162 return state
.tokenize(stream
, state
);
170 var curState
, curStream
, setStyle
;
172 for (var i
= arguments
.length
- 1; i
>= 0; i
--) curState
.cc
.push(arguments
[i
]);
175 pass
.apply(null, arguments
);
179 function pushContext(tagName
, startOfLine
) {
180 var noIndent
= Kludges
.doNotIndent
.hasOwnProperty(tagName
) || (curState
.context
&& curState
.context
.noIndent
);
182 prev
: curState
.context
,
184 indent
: curState
.indented
,
185 startOfLine
: startOfLine
,
189 function popContext() {
190 if (curState
.context
) curState
.context
= curState
.context
.prev
;
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") {
200 if (curState
.context
) {
201 if (curState
.context
.tagName
!= tagName
) {
202 if (Kludges
.implicitlyClosed
.hasOwnProperty(curState
.context
.tagName
.toLowerCase())) {
205 err
= !curState
.context
|| curState
.context
.tagName
!= tagName
;
210 if (err
) setStyle
= "error";
211 return cont(endclosetag(err
));
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());
224 if (type
== "endTag") {
225 maybePopContext(tagName
.toLowerCase());
226 pushContext(tagName
, startOfLine
);
232 function endclosetag(err
) {
233 return function(type
) {
234 if (err
) setStyle
= "error";
235 if (type
== "endTag") { popContext(); return cont(); }
237 return cont(arguments
.callee
);
240 function maybePopContext(nextTagName
) {
243 if (!curState
.context
) {
246 parentTagName
= curState
.context
.tagName
.toLowerCase();
247 if (!Kludges
.contextGrabbers
.hasOwnProperty(parentTagName
) ||
248 !Kludges
.contextGrabbers
[parentTagName
].hasOwnProperty(nextTagName
)) {
255 function attributes(type
) {
256 if (type
== "word") {setStyle
= "attribute"; return cont(attribute
, attributes
);}
257 if (type
== "endTag" || type
== "selfcloseTag") return pass();
259 return cont(attributes
);
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();
267 function attvalue(type
) {
268 if (type
== "string") return cont(attvaluemaybe
);
269 if (type
== "word" && Kludges
.allowUnquoted
) {setStyle
= "string"; return cont();}
271 return (type
== "endTag" || type
== "selfCloseTag") ? pass() : cont();
273 function attvaluemaybe(type
) {
274 if (type
== "string") return cont(attvaluemaybe
);
279 startState: function() {
280 return {tokenize
: inText
, cc
: [], indented
: 0, startOfLine
: true, tagName
: null, tagStart
: null, context
: null};
283 token: function(stream
, state
) {
284 if (!state
.tagName
&& stream
.sol()) {
285 state
.startOfLine
= true;
286 state
.indented
= stream
.indentation();
288 if (stream
.eatSpace()) return null;
290 setStyle
= type
= tagName
= null;
291 var style
= state
.tokenize(stream
, state
);
293 if ((style
|| type
) && style
!= "comment") {
294 curState
= state
; curStream
= stream
;
296 var comb
= state
.cc
.pop() || element
;
297 if (comb(type
|| style
)) break;
300 state
.startOfLine
= false;
301 return setStyle
|| style
;
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
;
320 blockCommentStart
: "<!--",
321 blockCommentEnd
: "-->",
323 configuration
: parserConfig
.htmlMode
? "html" : "xml"
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});