Mercurial Hosting > nabble
comparison src/nabble/view/web/util/codemirror/js/parsexml.js @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:7ecd1a4ef557 |
---|---|
1 /* This file defines an XML parser, with a few kludges to make it | |
2 * useable for HTML. autoSelfClosers defines a set of tag names that | |
3 * are expected to not have a closing tag, and doNotIndent specifies | |
4 * the tags inside of which no indentation should happen (see Config | |
5 * object). These can be disabled by passing the editor an object like | |
6 * {useHTMLKludges: false} as parserConfig option. | |
7 */ | |
8 | |
9 var XMLParser = Editor.Parser = (function() { | |
10 var Kludges = { | |
11 autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true, | |
12 "meta": true, "col": true, "frame": true, "base": true, "area": true}, | |
13 doNotIndent: {"pre": true, "!cdata": true} | |
14 }; | |
15 var NoKludges = {autoSelfClosers: {}, doNotIndent: {"!cdata": true}}; | |
16 var UseKludges = Kludges; | |
17 var alignCDATA = false; | |
18 | |
19 // Simple stateful tokenizer for XML documents. Returns a | |
20 // MochiKit-style iterator, with a state property that contains a | |
21 // function encapsulating the current state. See tokenize.js. | |
22 var tokenizeXML = (function() { | |
23 function inText(source, setState) { | |
24 var ch = source.next(); | |
25 if (ch == "<") { | |
26 if (source.equals("!")) { | |
27 source.next(); | |
28 if (source.equals("[")) { | |
29 if (source.lookAhead("[CDATA[", true)) { | |
30 setState(inBlock("xml-cdata", "]]>")); | |
31 return null; | |
32 } | |
33 else { | |
34 return "xml-text"; | |
35 } | |
36 } | |
37 else if (source.lookAhead("--", true)) { | |
38 setState(inBlock("xml-comment", "-->")); | |
39 return null; | |
40 } | |
41 else if (source.lookAhead("DOCTYPE", true)) { | |
42 source.nextWhileMatches(/[\w\._\-]/); | |
43 setState(inBlock("xml-doctype", ">")); | |
44 return "xml-doctype"; | |
45 } | |
46 else { | |
47 return "xml-text"; | |
48 } | |
49 } | |
50 else if (source.equals("?")) { | |
51 source.next(); | |
52 source.nextWhileMatches(/[\w\._\-]/); | |
53 setState(inBlock("xml-processing", "?>")); | |
54 return "xml-processing"; | |
55 } | |
56 else { | |
57 if (source.equals("/")) source.next(); | |
58 setState(inTag); | |
59 return "xml-punctuation"; | |
60 } | |
61 } | |
62 else if (ch == "&") { | |
63 while (!source.endOfLine()) { | |
64 if (source.next() == ";") | |
65 break; | |
66 } | |
67 return "xml-entity"; | |
68 } | |
69 else { | |
70 source.nextWhileMatches(/[^&<\n]/); | |
71 return "xml-text"; | |
72 } | |
73 } | |
74 | |
75 function inTag(source, setState) { | |
76 var ch = source.next(); | |
77 if (ch == ">") { | |
78 setState(inText); | |
79 return "xml-punctuation"; | |
80 } | |
81 else if (/[?\/]/.test(ch) && source.equals(">")) { | |
82 source.next(); | |
83 setState(inText); | |
84 return "xml-punctuation"; | |
85 } | |
86 else if (ch == "=") { | |
87 return "xml-punctuation"; | |
88 } | |
89 else if (/[\'\"]/.test(ch)) { | |
90 setState(inAttribute(ch)); | |
91 return null; | |
92 } | |
93 else { | |
94 source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/); | |
95 return "xml-name"; | |
96 } | |
97 } | |
98 | |
99 function inAttribute(quote) { | |
100 return function(source, setState) { | |
101 while (!source.endOfLine()) { | |
102 if (source.next() == quote) { | |
103 setState(inTag); | |
104 break; | |
105 } | |
106 } | |
107 return "xml-attribute"; | |
108 }; | |
109 } | |
110 | |
111 function inBlock(style, terminator) { | |
112 return function(source, setState) { | |
113 while (!source.endOfLine()) { | |
114 if (source.lookAhead(terminator, true)) { | |
115 setState(inText); | |
116 break; | |
117 } | |
118 source.next(); | |
119 } | |
120 return style; | |
121 }; | |
122 } | |
123 | |
124 return function(source, startState) { | |
125 return tokenizer(source, startState || inText); | |
126 }; | |
127 })(); | |
128 | |
129 // The parser. The structure of this function largely follows that of | |
130 // parseJavaScript in parsejavascript.js (there is actually a bit more | |
131 // shared code than I'd like), but it is quite a bit simpler. | |
132 function parseXML(source) { | |
133 var tokens = tokenizeXML(source), token; | |
134 var cc = [base]; | |
135 var tokenNr = 0, indented = 0; | |
136 var currentTag = null, context = null; | |
137 var consume; | |
138 | |
139 function push(fs) { | |
140 for (var i = fs.length - 1; i >= 0; i--) | |
141 cc.push(fs[i]); | |
142 } | |
143 function cont() { | |
144 push(arguments); | |
145 consume = true; | |
146 } | |
147 function pass() { | |
148 push(arguments); | |
149 consume = false; | |
150 } | |
151 | |
152 function markErr() { | |
153 token.style += " xml-error"; | |
154 } | |
155 function expect(text) { | |
156 return function(style, content) { | |
157 if (content == text) cont(); | |
158 else {markErr(); cont(arguments.callee);} | |
159 }; | |
160 } | |
161 | |
162 function pushContext(tagname, startOfLine) { | |
163 var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent); | |
164 context = {prev: context, name: tagname, indent: indented, startOfLine: startOfLine, noIndent: noIndent}; | |
165 } | |
166 function popContext() { | |
167 context = context.prev; | |
168 } | |
169 function computeIndentation(baseContext) { | |
170 return function(nextChars, current) { | |
171 var context = baseContext; | |
172 if (context && context.noIndent) | |
173 return current; | |
174 if (alignCDATA && /<!\[CDATA\[/.test(nextChars)) | |
175 return 0; | |
176 if (context && /^<\//.test(nextChars)) | |
177 context = context.prev; | |
178 while (context && !context.startOfLine) | |
179 context = context.prev; | |
180 if (context) | |
181 return context.indent + indentUnit; | |
182 else | |
183 return 0; | |
184 }; | |
185 } | |
186 | |
187 function base() { | |
188 return pass(element, base); | |
189 } | |
190 var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true, "xml-doctype": true}; | |
191 function element(style, content) { | |
192 if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1)); | |
193 else if (content == "</") cont(closetagname, expect(">")); | |
194 else if (style == "xml-cdata") { | |
195 if (!context || context.name != "!cdata") pushContext("!cdata"); | |
196 if (/\]\]>$/.test(content)) popContext(); | |
197 cont(); | |
198 } | |
199 else if (harmlessTokens.hasOwnProperty(style)) cont(); | |
200 else {markErr(); cont();} | |
201 } | |
202 function tagname(style, content) { | |
203 if (style == "xml-name") { | |
204 currentTag = content.toLowerCase(); | |
205 token.style = "xml-tagname"; | |
206 cont(); | |
207 } | |
208 else { | |
209 currentTag = null; | |
210 pass(); | |
211 } | |
212 } | |
213 function closetagname(style, content) { | |
214 if (style == "xml-name") { | |
215 token.style = "xml-tagname"; | |
216 if (context && content.toLowerCase() == context.name) popContext(); | |
217 else markErr(); | |
218 } | |
219 cont(); | |
220 } | |
221 function endtag(startOfLine) { | |
222 return function(style, content) { | |
223 if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont(); | |
224 else if (content == ">") {pushContext(currentTag, startOfLine); cont();} | |
225 else {markErr(); cont(arguments.callee);} | |
226 }; | |
227 } | |
228 function attributes(style) { | |
229 if (style == "xml-name") {token.style = "xml-attname"; cont(attribute, attributes);} | |
230 else pass(); | |
231 } | |
232 function attribute(style, content) { | |
233 if (content == "=") cont(value); | |
234 else if (content == ">" || content == "/>") pass(endtag); | |
235 else pass(); | |
236 } | |
237 function value(style) { | |
238 if (style == "xml-attribute") cont(value); | |
239 else pass(); | |
240 } | |
241 | |
242 return { | |
243 indentation: function() {return indented;}, | |
244 | |
245 next: function(){ | |
246 token = tokens.next(); | |
247 if (token.style == "whitespace" && tokenNr == 0) | |
248 indented = token.value.length; | |
249 else | |
250 tokenNr++; | |
251 if (token.content == "\n") { | |
252 indented = tokenNr = 0; | |
253 token.indentation = computeIndentation(context); | |
254 } | |
255 | |
256 if (token.style == "whitespace" || token.type == "xml-comment") | |
257 return token; | |
258 | |
259 while(true){ | |
260 consume = false; | |
261 cc.pop()(token.style, token.content); | |
262 if (consume) return token; | |
263 } | |
264 }, | |
265 | |
266 copy: function(){ | |
267 var _cc = cc.concat([]), _tokenState = tokens.state, _context = context; | |
268 var parser = this; | |
269 | |
270 return function(input){ | |
271 cc = _cc.concat([]); | |
272 tokenNr = indented = 0; | |
273 context = _context; | |
274 tokens = tokenizeXML(input, _tokenState); | |
275 return parser; | |
276 }; | |
277 } | |
278 }; | |
279 } | |
280 | |
281 return { | |
282 make: parseXML, | |
283 electricChars: "/", | |
284 configure: function(config) { | |
285 if (config.useHTMLKludges != null) | |
286 UseKludges = config.useHTMLKludges ? Kludges : NoKludges; | |
287 if (config.alignCDATA) | |
288 alignCDATA = config.alignCDATA; | |
289 } | |
290 }; | |
291 })(); |