source: branches/2.2/plugins/LocalFilesEditor/codemirror/mode/xml/xml.js @ 10310

Last change on this file since 10310 was 10310, checked in by patdenice, 13 years ago

merge r10307 from trunk to branch 2.2
feature:2262
Replace editarea by Codemirror:
http://codemirror.net

File size: 6.0 KB
Line 
1CodeMirror.defineMode("xml", function(config, parserConfig) {
2  var indentUnit = config.indentUnit;
3  var Kludges = parserConfig.htmlMode ? {
4    autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
5                      "meta": true, "col": true, "frame": true, "base": true, "area": true},
6    doNotIndent: {"pre": true, "!cdata": true},
7    allowUnquoted: true
8  } : {autoSelfClosers: {}, doNotIndent: {"!cdata": true}, allowUnquoted: false};
9  var alignCDATA = parserConfig.alignCDATA;
10
11  // Return variables for tokenizers
12  var tagName, type;
13
14  function inText(stream, state) {
15    function chain(parser) {
16      state.tokenize = parser;
17      return parser(stream, state);
18    }
19
20    var ch = stream.next();
21    if (ch == "<") {
22      if (stream.eat("!")) {
23        if (stream.eat("[")) {
24          if (stream.match("[CDATA[")) return chain(inBlock("xml-cdata", "]]>"));
25          else return null;
26        }
27        else if (stream.match("--")) return chain(inBlock("xml-comment", "-->"));
28        else if (stream.match("DOCTYPE")) {
29          stream.eatWhile(/[\w\._\-]/);
30          return chain(inBlock("xml-doctype", ">"));
31        }
32        else return null;
33      }
34      else if (stream.eat("?")) {
35        stream.eatWhile(/[\w\._\-]/);
36        state.tokenize = inBlock("xml-processing", "?>");
37        return "xml-processing";
38      }
39      else {
40        type = stream.eat("/") ? "closeTag" : "openTag";
41        stream.eatSpace();
42        tagName = "";
43        var c;
44        while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
45        state.tokenize = inTag;
46        return "xml-tag";
47      }
48    }
49    else if (ch == "&") {
50      stream.eatWhile(/[^;]/);
51      stream.eat(";");
52      return "xml-entity";
53    }
54    else {
55      stream.eatWhile(/[^&<]/);
56      return null;
57    }
58  }
59
60  function inTag(stream, state) {
61    var ch = stream.next();
62    if (ch == ">" || (ch == "/" && stream.eat(">"))) {
63      state.tokenize = inText;
64      type = ch == ">" ? "endTag" : "selfcloseTag";
65      return "xml-tag";
66    }
67    else if (ch == "=") {
68      type = "equals";
69      return null;
70    }
71    else if (/[\'\"]/.test(ch)) {
72      state.tokenize = inAttribute(ch);
73      return state.tokenize(stream, state);
74    }
75    else {
76      stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
77      return "xml-word";
78    }
79  }
80
81  function inAttribute(quote) {
82    return function(stream, state) {
83      while (!stream.eol()) {
84        if (stream.next() == quote) {
85          state.tokenize = inTag;
86          break;
87        }
88      }
89      return "xml-attribute";
90    };
91  }
92
93  function inBlock(style, terminator) {
94    return function(stream, state) {
95      while (!stream.eol()) {
96        if (stream.match(terminator)) {
97          state.tokenize = inText;
98          break;
99        }
100        stream.next();
101      }
102      return style;
103    };
104  }
105
106  var curState, setStyle;
107  function pass() {
108    for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
109  }
110  function cont() {
111    pass.apply(null, arguments);
112    return true;
113  }
114
115  function pushContext(tagName, startOfLine) {
116    var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
117    curState.context = {
118      prev: curState.context,
119      tagName: tagName,
120      indent: curState.indented,
121      startOfLine: startOfLine,
122      noIndent: noIndent
123    };
124  }
125  function popContext() {
126    if (curState.context) curState.context = curState.context.prev;
127  }
128
129  function element(type) {
130    if (type == "openTag") {curState.tagName = tagName; return cont(attributes, endtag(curState.startOfLine));}
131    else if (type == "closeTag") {popContext(); return cont(endclosetag);}
132    else if (type == "xml-cdata") {
133      if (!curState.context || curState.context.name != "!cdata") pushContext("!cdata");
134      if (curState.tokenize == inText) popContext();
135      return cont();
136    }
137    else return cont();
138  }
139  function endtag(startOfLine) {
140    return function(type) {
141      if (type == "selfcloseTag" ||
142          (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
143        return cont();
144      if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
145      return cont();
146    };
147  }
148  function endclosetag(type) {
149    if (type == "endTag") return cont();
150    return pass();
151  }
152
153  function attributes(type) {
154    if (type == "xml-word") {setStyle = "xml-attname"; return cont(attributes);}
155    if (type == "equals") return cont(attvalue, attributes);
156    return pass();
157  }
158  function attvalue(type) {
159    if (type == "xml-word" && Kludges.allowUnquoted) {setStyle = "xml-attribute"; return cont();}
160    if (type == "xml-attribute") return cont();
161    return pass();
162  }
163
164  return {
165    startState: function() {
166      return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
167    },
168
169    token: function(stream, state) {
170      if (stream.sol()) {
171        state.startOfLine = true;
172        state.indented = stream.indentation();
173      }
174      if (stream.eatSpace()) return null;
175
176      setStyle = type = tagName = null;
177      var style = state.tokenize(stream, state);
178      if ((style || type) && style != "xml-comment") {
179        curState = state;
180        while (true) {
181          var comb = state.cc.pop() || element;
182          if (comb(type || style)) break;
183        }
184      }
185      state.startOfLine = false;
186      return setStyle || style;
187    },
188
189    indent: function(state, textAfter) {
190      var context = state.context;
191      if (context && context.noIndent) return 0;
192      if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
193      if (context && /^<\//.test(textAfter))
194        context = context.prev;
195      while (context && !context.startOfLine)
196        context = context.prev;
197      if (context) return context.indent + indentUnit;
198      else return 0;
199    },
200
201    electricChars: "/"
202  };
203});
204
205CodeMirror.defineMIME("application/xml", "xml");
206CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
Note: See TracBrowser for help on using the repository browser.