[ Index ] |
MailPress 7.2 |
[ Index ] [ Classes ] [ Functions ] [ Variables ] [ Constants ] [ Statistics ] |
[Summary view] [Print] [Text view]
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: { 12 "br": true, 13 "img": true, 14 "hr": true, 15 "link": true, 16 "input": true, 17 "meta": true, 18 "col": true, 19 "frame": true, 20 "base": true, 21 "area": true 22 }, 23 doNotIndent: { 24 "pre": true, 25 "!cdata": true 26 } 27 }; 28 var NoKludges = { 29 autoSelfClosers: {}, 30 doNotIndent: { 31 "!cdata": true 32 } 33 }; 34 var UseKludges = Kludges; 35 var alignCDATA = false; 36 37 // Simple stateful tokenizer for XML documents. Returns a 38 // MochiKit-style iterator, with a state property that contains a 39 // function encapsulating the current state. See tokenize.js. 40 var tokenizeXML = (function () { 41 function inText(source, setState) { 42 var ch = source.next(); 43 if (ch == "<") { 44 if (source.equals("!")) { 45 source.next(); 46 if (source.equals("[")) { 47 if (source.lookAhead("[CDATA[", true)) { 48 setState(inBlock("xml-cdata", "]]>")); 49 return null; 50 } 51 else { 52 return "xml-text"; 53 } 54 } 55 else if (source.lookAhead("--", true)) { 56 setState(inBlock("xml-comment", "-->")); 57 return null; 58 } 59 else { 60 return "xml-text"; 61 } 62 } 63 else if (source.equals("?")) { 64 source.next(); 65 source.nextWhileMatches(/[\w\._\-]/); 66 setState(inBlock("xml-processing", "?>")); 67 return "xml-processing"; 68 } 69 else { if (source.equals("/")) source.next(); 70 setState(inTag); 71 return "xml-punctuation"; 72 } 73 } 74 else if (ch == "&") { 75 while (!source.endOfLine()) { 76 if (source.next() == ";") break; 77 } 78 return "xml-entity"; 79 } 80 else { 81 source.nextWhileMatches(/[^&<\n]/); 82 return "xml-text"; 83 } 84 } 85 86 function inTag(source, setState) { 87 var ch = source.next(); 88 if (ch == ">") { 89 setState(inText); 90 return "xml-punctuation"; 91 } 92 else if (/[?\/]/.test(ch) && source.equals(">")) { 93 source.next(); 94 setState(inText); 95 return "xml-punctuation"; 96 } 97 else if (ch == "=") { 98 return "xml-punctuation"; 99 } 100 else if (/[\'\"]/.test(ch)) { 101 setState(inAttribute(ch)); 102 return null; 103 } 104 else { 105 source.nextWhileMatches(/[^\s\u00a0=<>\"\'\/?]/); 106 return "xml-name"; 107 } 108 } 109 110 function inAttribute(quote) { 111 return function (source, setState) { 112 while (!source.endOfLine()) { 113 if (source.next() == quote) { 114 setState(inTag); 115 break; 116 } 117 } 118 return "xml-attribute"; 119 }; 120 } 121 122 function inBlock(style, terminator) { 123 return function (source, setState) { 124 while (!source.endOfLine()) { 125 if (source.lookAhead(terminator, true)) { 126 setState(inText); 127 break; 128 } 129 source.next(); 130 } 131 return style; 132 }; 133 } 134 135 return function (source, startState) { 136 return tokenizer(source, startState || inText); 137 }; 138 })(); 139 140 // The parser. The structure of this function largely follows that of 141 // parseJavaScript in parsejavascript.js (there is actually a bit more 142 // shared code than I'd like), but it is quite a bit simpler. 143 function parseXML(source) { 144 var tokens = tokenizeXML(source); 145 var cc = [base]; 146 var tokenNr = 0, 147 indented = 0; 148 var currentTag = null, 149 context = null; 150 var consume, marked; 151 152 function push(fs) { 153 for (var i = fs.length - 1; i >= 0; i--) 154 cc.push(fs[i]); 155 } 156 157 function cont() { 158 push(arguments); 159 consume = true; 160 } 161 162 function pass() { 163 push(arguments); 164 consume = false; 165 } 166 167 function mark(style) { 168 marked = style; 169 } 170 171 function expect(text) { 172 return function (style, content) { 173 if (content == text) cont(); 174 else mark("xml-error") || cont(arguments.callee); 175 }; 176 } 177 178 function pushContext(tagname, startOfLine) { 179 var noIndent = UseKludges.doNotIndent.hasOwnProperty(tagname) || (context && context.noIndent); 180 context = { 181 prev: context, 182 name: tagname, 183 indent: indented, 184 startOfLine: startOfLine, 185 noIndent: noIndent 186 }; 187 } 188 189 function popContext() { 190 context = context.prev; 191 } 192 193 function computeIndentation(baseContext) { 194 return function (nextChars, current) { 195 var context = baseContext; 196 if (context && context.noIndent) return current; 197 if (alignCDATA && /<!\[CDATA\[/.test(nextChars)) return 0; 198 if (context && /^<\//.test(nextChars)) context = context.prev; 199 while (context && !context.startOfLine) 200 context = context.prev; 201 if (context) return context.indent + indentUnit; 202 else return 0; 203 }; 204 } 205 206 function base() { 207 return pass(element, base); 208 } 209 210 var harmlessTokens = { 211 "xml-text": true, 212 "xml-entity": true, 213 "xml-comment": true, 214 "xml-processing": true 215 }; 216 217 function element(style, content) { 218 if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1)); 219 else if (content == "</") cont(closetagname, expect(">")); 220 else if (style == "xml-cdata") { 221 if (!context || context.name != "!cdata") pushContext("!cdata"); 222 if (/\]\]>$/.test(content)) popContext(); 223 cont(); 224 } 225 else if (harmlessTokens.hasOwnProperty(style)) cont(); 226 else mark("xml-error") || cont(); 227 } 228 229 function tagname(style, content) { 230 if (style == "xml-name") { 231 currentTag = content.toLowerCase(); 232 mark("xml-tagname"); 233 cont(); 234 } 235 else { 236 currentTag = null; 237 pass(); 238 } 239 } 240 241 function closetagname(style, content) { 242 if (style == "xml-name" && context && content.toLowerCase() == context.name) { 243 popContext(); 244 mark("xml-tagname"); 245 } 246 else { 247 mark("xml-error"); 248 } 249 cont(); 250 } 251 252 function endtag(startOfLine) { 253 return function (style, content) { 254 if (content == "/>" || (content == ">" && UseKludges.autoSelfClosers.hasOwnProperty(currentTag))) cont(); 255 else if (content == ">") pushContext(currentTag, startOfLine) || cont(); 256 else mark("xml-error") || cont(arguments.callee); 257 }; 258 } 259 260 function attributes(style) { 261 if (style == "xml-name") mark("xml-attname") || cont(attribute, attributes); 262 else pass(); 263 } 264 265 function attribute(style, content) { 266 if (content == "=") cont(value); 267 else if (content == ">" || content == "/>") pass(endtag); 268 else pass(); 269 } 270 271 function value(style) { 272 if (style == "xml-attribute") cont(value); 273 else pass(); 274 } 275 276 return { 277 indentation: function () { 278 return indented; 279 }, 280 281 next: function () { 282 var token = tokens.next(); 283 if (token.style == "whitespace" && tokenNr == 0) indented = token.value.length; 284 else tokenNr++; 285 if (token.content == "\n") { 286 indented = tokenNr = 0; 287 token.indentation = computeIndentation(context); 288 } 289 290 if (token.style == "whitespace" || token.type == "xml-comment") return token; 291 292 while (true) { 293 consume = marked = false; 294 cc.pop()(token.style, token.content); 295 if (consume) { 296 if (marked) token.style = marked; 297 return token; 298 } 299 } 300 }, 301 302 copy: function () { 303 var _cc = cc.concat([]), 304 _tokenState = tokens.state, 305 _context = context; 306 var parser = this; 307 308 return function (input) { 309 cc = _cc.concat([]); 310 tokenNr = indented = 0; 311 context = _context; 312 tokens = tokenizeXML(input, _tokenState); 313 return parser; 314 }; 315 } 316 }; 317 } 318 319 return { 320 make: parseXML, 321 electricChars: "/", 322 configure: function (config) { 323 if (config.useHTMLKludges != null) UseKludges = config.useHTMLKludges ? Kludges : NoKludges; 324 if (config.alignCDATA) alignCDATA = config.alignCDATA; 325 } 326 }; 327 })();
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue May 19 15:55:14 2020 | Cross-referenced by PHPXref 0.7.1 |