1 |
|
2 | (function() {
|
3 | "use strict";
|
4 | var bom, builder, escapeCDATA, events, isEmpty, processName, processors, requiresCDATA, sax, wrapCDATA,
|
5 | extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
|
6 | hasProp = {}.hasOwnProperty,
|
7 | bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
|
8 |
|
9 | sax = require('sax');
|
10 |
|
11 | events = require('events');
|
12 |
|
13 | builder = require('xmlbuilder');
|
14 |
|
15 | bom = require('./bom');
|
16 |
|
17 | processors = require('./processors');
|
18 |
|
19 | isEmpty = function(thing) {
|
20 | return typeof thing === "object" && (thing != null) && Object.keys(thing).length === 0;
|
21 | };
|
22 |
|
23 | processName = function(processors, processedName) {
|
24 | var i, len, process;
|
25 | for (i = 0, len = processors.length; i < len; i++) {
|
26 | process = processors[i];
|
27 | processedName = process(processedName);
|
28 | }
|
29 | return processedName;
|
30 | };
|
31 |
|
32 | requiresCDATA = function(entry) {
|
33 | return entry.indexOf('&') >= 0 || entry.indexOf('>') >= 0 || entry.indexOf('<') >= 0;
|
34 | };
|
35 |
|
36 | wrapCDATA = function(entry) {
|
37 | return "<![CDATA[" + (escapeCDATA(entry)) + "]]>";
|
38 | };
|
39 |
|
40 | escapeCDATA = function(entry) {
|
41 | return entry.replace(']]>', ']]]]><![CDATA[>');
|
42 | };
|
43 |
|
44 | exports.processors = processors;
|
45 |
|
46 | exports.defaults = {
|
47 | "0.1": {
|
48 | explicitCharkey: false,
|
49 | trim: true,
|
50 | normalize: true,
|
51 | normalizeTags: false,
|
52 | attrkey: "@",
|
53 | charkey: "#",
|
54 | explicitArray: false,
|
55 | ignoreAttrs: false,
|
56 | mergeAttrs: false,
|
57 | explicitRoot: false,
|
58 | validator: null,
|
59 | xmlns: false,
|
60 | explicitChildren: false,
|
61 | childkey: '@@',
|
62 | charsAsChildren: false,
|
63 | async: false,
|
64 | strict: true,
|
65 | attrNameProcessors: null,
|
66 | tagNameProcessors: null,
|
67 | valueProcessors: null,
|
68 | emptyTag: ''
|
69 | },
|
70 | "0.2": {
|
71 | explicitCharkey: false,
|
72 | trim: false,
|
73 | normalize: false,
|
74 | normalizeTags: false,
|
75 | attrkey: "$",
|
76 | charkey: "_",
|
77 | explicitArray: true,
|
78 | ignoreAttrs: false,
|
79 | mergeAttrs: false,
|
80 | explicitRoot: true,
|
81 | validator: null,
|
82 | xmlns: false,
|
83 | explicitChildren: false,
|
84 | preserveChildrenOrder: false,
|
85 | childkey: '$$',
|
86 | charsAsChildren: false,
|
87 | async: false,
|
88 | strict: true,
|
89 | attrNameProcessors: null,
|
90 | tagNameProcessors: null,
|
91 | valueProcessors: null,
|
92 | rootName: 'root',
|
93 | xmldec: {
|
94 | 'version': '1.0',
|
95 | 'encoding': 'UTF-8',
|
96 | 'standalone': true
|
97 | },
|
98 | doctype: null,
|
99 | renderOpts: {
|
100 | 'pretty': true,
|
101 | 'indent': ' ',
|
102 | 'newline': '\n'
|
103 | },
|
104 | headless: false,
|
105 | chunkSize: 10000,
|
106 | emptyTag: '',
|
107 | cdata: false
|
108 | }
|
109 | };
|
110 |
|
111 | exports.ValidationError = (function(superClass) {
|
112 | extend(ValidationError, superClass);
|
113 |
|
114 | function ValidationError(message) {
|
115 | this.message = message;
|
116 | }
|
117 |
|
118 | return ValidationError;
|
119 |
|
120 | })(Error);
|
121 |
|
122 | exports.Builder = (function() {
|
123 | function Builder(opts) {
|
124 | var key, ref, value;
|
125 | this.options = {};
|
126 | ref = exports.defaults["0.2"];
|
127 | for (key in ref) {
|
128 | if (!hasProp.call(ref, key)) continue;
|
129 | value = ref[key];
|
130 | this.options[key] = value;
|
131 | }
|
132 | for (key in opts) {
|
133 | if (!hasProp.call(opts, key)) continue;
|
134 | value = opts[key];
|
135 | this.options[key] = value;
|
136 | }
|
137 | }
|
138 |
|
139 | Builder.prototype.buildObject = function(rootObj) {
|
140 | var attrkey, charkey, render, rootElement, rootName;
|
141 | attrkey = this.options.attrkey;
|
142 | charkey = this.options.charkey;
|
143 | if ((Object.keys(rootObj).length === 1) && (this.options.rootName === exports.defaults['0.2'].rootName)) {
|
144 | rootName = Object.keys(rootObj)[0];
|
145 | rootObj = rootObj[rootName];
|
146 | } else {
|
147 | rootName = this.options.rootName;
|
148 | }
|
149 | render = (function(_this) {
|
150 | return function(element, obj) {
|
151 | var attr, child, entry, index, key, value;
|
152 | if (typeof obj !== 'object') {
|
153 | if (_this.options.cdata && requiresCDATA(obj)) {
|
154 | element.raw(wrapCDATA(obj));
|
155 | } else {
|
156 | element.txt(obj);
|
157 | }
|
158 | } else {
|
159 | for (key in obj) {
|
160 | if (!hasProp.call(obj, key)) continue;
|
161 | child = obj[key];
|
162 | if (key === attrkey) {
|
163 | if (typeof child === "object") {
|
164 | for (attr in child) {
|
165 | value = child[attr];
|
166 | element = element.att(attr, value);
|
167 | }
|
168 | }
|
169 | } else if (key === charkey) {
|
170 | if (_this.options.cdata && requiresCDATA(child)) {
|
171 | element = element.raw(wrapCDATA(child));
|
172 | } else {
|
173 | element = element.txt(child);
|
174 | }
|
175 | } else if (Array.isArray(child)) {
|
176 | for (index in child) {
|
177 | if (!hasProp.call(child, index)) continue;
|
178 | entry = child[index];
|
179 | if (typeof entry === 'string') {
|
180 | if (_this.options.cdata && requiresCDATA(entry)) {
|
181 | element = element.ele(key).raw(wrapCDATA(entry)).up();
|
182 | } else {
|
183 | element = element.ele(key, entry).up();
|
184 | }
|
185 | } else {
|
186 | element = render(element.ele(key), entry).up();
|
187 | }
|
188 | }
|
189 | } else if (typeof child === "object") {
|
190 | element = render(element.ele(key), child).up();
|
191 | } else {
|
192 | if (typeof child === 'string' && _this.options.cdata && requiresCDATA(child)) {
|
193 | element = element.ele(key).raw(wrapCDATA(child)).up();
|
194 | } else {
|
195 | element = element.ele(key, child.toString()).up();
|
196 | }
|
197 | }
|
198 | }
|
199 | }
|
200 | return element;
|
201 | };
|
202 | })(this);
|
203 | rootElement = builder.create(rootName, this.options.xmldec, this.options.doctype, {
|
204 | headless: this.options.headless
|
205 | });
|
206 | return render(rootElement, rootObj).end(this.options.renderOpts);
|
207 | };
|
208 |
|
209 | return Builder;
|
210 |
|
211 | })();
|
212 |
|
213 | exports.Parser = (function(superClass) {
|
214 | extend(Parser, superClass);
|
215 |
|
216 | function Parser(opts) {
|
217 | this.parseString = bind(this.parseString, this);
|
218 | this.reset = bind(this.reset, this);
|
219 | this.assignOrPush = bind(this.assignOrPush, this);
|
220 | this.processAsync = bind(this.processAsync, this);
|
221 | var key, ref, value;
|
222 | if (!(this instanceof exports.Parser)) {
|
223 | return new exports.Parser(opts);
|
224 | }
|
225 | this.options = {};
|
226 | ref = exports.defaults["0.2"];
|
227 | for (key in ref) {
|
228 | if (!hasProp.call(ref, key)) continue;
|
229 | value = ref[key];
|
230 | this.options[key] = value;
|
231 | }
|
232 | for (key in opts) {
|
233 | if (!hasProp.call(opts, key)) continue;
|
234 | value = opts[key];
|
235 | this.options[key] = value;
|
236 | }
|
237 | if (this.options.xmlns) {
|
238 | this.options.xmlnskey = this.options.attrkey + "ns";
|
239 | }
|
240 | if (this.options.normalizeTags) {
|
241 | if (!this.options.tagNameProcessors) {
|
242 | this.options.tagNameProcessors = [];
|
243 | }
|
244 | this.options.tagNameProcessors.unshift(processors.normalize);
|
245 | }
|
246 | this.reset();
|
247 | }
|
248 |
|
249 | Parser.prototype.processAsync = function() {
|
250 | var chunk;
|
251 | if (this.remaining.length <= this.options.chunkSize) {
|
252 | chunk = this.remaining;
|
253 | this.remaining = '';
|
254 | this.saxParser = this.saxParser.write(chunk);
|
255 | return this.saxParser.close();
|
256 | } else {
|
257 | chunk = this.remaining.substr(0, this.options.chunkSize);
|
258 | this.remaining = this.remaining.substr(this.options.chunkSize, this.remaining.length);
|
259 | this.saxParser = this.saxParser.write(chunk);
|
260 | return setImmediate(this.processAsync);
|
261 | }
|
262 | };
|
263 |
|
264 | Parser.prototype.assignOrPush = function(obj, key, newValue) {
|
265 | if (!(key in obj)) {
|
266 | if (!this.options.explicitArray) {
|
267 | return obj[key] = newValue;
|
268 | } else {
|
269 | return obj[key] = [newValue];
|
270 | }
|
271 | } else {
|
272 | if (!(obj[key] instanceof Array)) {
|
273 | obj[key] = [obj[key]];
|
274 | }
|
275 | return obj[key].push(newValue);
|
276 | }
|
277 | };
|
278 |
|
279 | Parser.prototype.reset = function() {
|
280 | var attrkey, charkey, ontext, stack;
|
281 | this.removeAllListeners();
|
282 | this.saxParser = sax.parser(this.options.strict, {
|
283 | trim: false,
|
284 | normalize: false,
|
285 | xmlns: this.options.xmlns
|
286 | });
|
287 | this.saxParser.errThrown = false;
|
288 | this.saxParser.onerror = (function(_this) {
|
289 | return function(error) {
|
290 | _this.saxParser.resume();
|
291 | if (!_this.saxParser.errThrown) {
|
292 | _this.saxParser.errThrown = true;
|
293 | return _this.emit("error", error);
|
294 | }
|
295 | };
|
296 | })(this);
|
297 | this.saxParser.ended = false;
|
298 | this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
|
299 | this.resultObject = null;
|
300 | stack = [];
|
301 | attrkey = this.options.attrkey;
|
302 | charkey = this.options.charkey;
|
303 | this.saxParser.onopentag = (function(_this) {
|
304 | return function(node) {
|
305 | var key, newValue, obj, processedKey, ref;
|
306 | obj = {};
|
307 | obj[charkey] = "";
|
308 | if (!_this.options.ignoreAttrs) {
|
309 | ref = node.attributes;
|
310 | for (key in ref) {
|
311 | if (!hasProp.call(ref, key)) continue;
|
312 | if (!(attrkey in obj) && !_this.options.mergeAttrs) {
|
313 | obj[attrkey] = {};
|
314 | }
|
315 | newValue = node.attributes[key];
|
316 | processedKey = _this.options.attrNameProcessors ? processName(_this.options.attrNameProcessors, key) : key;
|
317 | if (_this.options.mergeAttrs) {
|
318 | _this.assignOrPush(obj, processedKey, newValue);
|
319 | } else {
|
320 | obj[attrkey][processedKey] = newValue;
|
321 | }
|
322 | }
|
323 | }
|
324 | obj["#name"] = _this.options.tagNameProcessors ? processName(_this.options.tagNameProcessors, node.name) : node.name;
|
325 | if (_this.options.xmlns) {
|
326 | obj[_this.options.xmlnskey] = {
|
327 | uri: node.uri,
|
328 | local: node.local
|
329 | };
|
330 | }
|
331 | return stack.push(obj);
|
332 | };
|
333 | })(this);
|
334 | this.saxParser.onclosetag = (function(_this) {
|
335 | return function() {
|
336 | var cdata, emptyStr, err, key, node, nodeName, obj, objClone, old, s, xpath;
|
337 | obj = stack.pop();
|
338 | nodeName = obj["#name"];
|
339 | if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) {
|
340 | delete obj["#name"];
|
341 | }
|
342 | if (obj.cdata === true) {
|
343 | cdata = obj.cdata;
|
344 | delete obj.cdata;
|
345 | }
|
346 | s = stack[stack.length - 1];
|
347 | if (obj[charkey].match(/^\s*$/) && !cdata) {
|
348 | emptyStr = obj[charkey];
|
349 | delete obj[charkey];
|
350 | } else {
|
351 | if (_this.options.trim) {
|
352 | obj[charkey] = obj[charkey].trim();
|
353 | }
|
354 | if (_this.options.normalize) {
|
355 | obj[charkey] = obj[charkey].replace(/\s{2,}/g, " ").trim();
|
356 | }
|
357 | obj[charkey] = _this.options.valueProcessors ? processName(_this.options.valueProcessors, obj[charkey]) : obj[charkey];
|
358 | if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
|
359 | obj = obj[charkey];
|
360 | }
|
361 | }
|
362 | if (isEmpty(obj)) {
|
363 | obj = _this.options.emptyTag !== '' ? _this.options.emptyTag : emptyStr;
|
364 | }
|
365 | if (_this.options.validator != null) {
|
366 | xpath = "/" + ((function() {
|
367 | var i, len, results;
|
368 | results = [];
|
369 | for (i = 0, len = stack.length; i < len; i++) {
|
370 | node = stack[i];
|
371 | results.push(node["#name"]);
|
372 | }
|
373 | return results;
|
374 | })()).concat(nodeName).join("/");
|
375 | try {
|
376 | obj = _this.options.validator(xpath, s && s[nodeName], obj);
|
377 | } catch (_error) {
|
378 | err = _error;
|
379 | _this.emit("error", err);
|
380 | }
|
381 | }
|
382 | if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj === 'object') {
|
383 | if (!_this.options.preserveChildrenOrder) {
|
384 | node = {};
|
385 | if (_this.options.attrkey in obj) {
|
386 | node[_this.options.attrkey] = obj[_this.options.attrkey];
|
387 | delete obj[_this.options.attrkey];
|
388 | }
|
389 | if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
|
390 | node[_this.options.charkey] = obj[_this.options.charkey];
|
391 | delete obj[_this.options.charkey];
|
392 | }
|
393 | if (Object.getOwnPropertyNames(obj).length > 0) {
|
394 | node[_this.options.childkey] = obj;
|
395 | }
|
396 | obj = node;
|
397 | } else if (s) {
|
398 | s[_this.options.childkey] = s[_this.options.childkey] || [];
|
399 | objClone = {};
|
400 | for (key in obj) {
|
401 | if (!hasProp.call(obj, key)) continue;
|
402 | objClone[key] = obj[key];
|
403 | }
|
404 | s[_this.options.childkey].push(objClone);
|
405 | delete obj["#name"];
|
406 | if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
|
407 | obj = obj[charkey];
|
408 | }
|
409 | }
|
410 | }
|
411 | if (stack.length > 0) {
|
412 | return _this.assignOrPush(s, nodeName, obj);
|
413 | } else {
|
414 | if (_this.options.explicitRoot) {
|
415 | old = obj;
|
416 | obj = {};
|
417 | obj[nodeName] = old;
|
418 | }
|
419 | _this.resultObject = obj;
|
420 | _this.saxParser.ended = true;
|
421 | return _this.emit("end", _this.resultObject);
|
422 | }
|
423 | };
|
424 | })(this);
|
425 | ontext = (function(_this) {
|
426 | return function(text) {
|
427 | var charChild, s;
|
428 | s = stack[stack.length - 1];
|
429 | if (s) {
|
430 | s[charkey] += text;
|
431 | if (_this.options.explicitChildren && _this.options.preserveChildrenOrder && _this.options.charsAsChildren && text.replace(/\\n/g, '').trim() !== '') {
|
432 | s[_this.options.childkey] = s[_this.options.childkey] || [];
|
433 | charChild = {
|
434 | '#name': '__text__'
|
435 | };
|
436 | charChild[charkey] = text;
|
437 | s[_this.options.childkey].push(charChild);
|
438 | }
|
439 | return s;
|
440 | }
|
441 | };
|
442 | })(this);
|
443 | this.saxParser.ontext = ontext;
|
444 | return this.saxParser.oncdata = (function(_this) {
|
445 | return function(text) {
|
446 | var s;
|
447 | s = ontext(text);
|
448 | if (s) {
|
449 | return s.cdata = true;
|
450 | }
|
451 | };
|
452 | })(this);
|
453 | };
|
454 |
|
455 | Parser.prototype.parseString = function(str, cb) {
|
456 | var err;
|
457 | if ((cb != null) && typeof cb === "function") {
|
458 | this.on("end", function(result) {
|
459 | this.reset();
|
460 | return cb(null, result);
|
461 | });
|
462 | this.on("error", function(err) {
|
463 | this.reset();
|
464 | return cb(err);
|
465 | });
|
466 | }
|
467 | str = str.toString();
|
468 | if (str.trim() === '') {
|
469 | this.emit("end", null);
|
470 | return true;
|
471 | }
|
472 | try {
|
473 | str = bom.stripBOM(str);
|
474 | if (this.options.async) {
|
475 | this.remaining = str;
|
476 | setImmediate(this.processAsync);
|
477 | return this.saxParser;
|
478 | }
|
479 | return this.saxParser.write(str).close();
|
480 | } catch (_error) {
|
481 | err = _error;
|
482 | if (!(this.saxParser.errThrown || this.saxParser.ended)) {
|
483 | this.emit('error', err);
|
484 | return this.saxParser.errThrown = true;
|
485 | } else if (this.saxParser.ended) {
|
486 | throw err;
|
487 | }
|
488 | }
|
489 | };
|
490 |
|
491 | return Parser;
|
492 |
|
493 | })(events.EventEmitter);
|
494 |
|
495 | exports.parseString = function(str, a, b) {
|
496 | var cb, options, parser;
|
497 | if (b != null) {
|
498 | if (typeof b === 'function') {
|
499 | cb = b;
|
500 | }
|
501 | if (typeof a === 'object') {
|
502 | options = a;
|
503 | }
|
504 | } else {
|
505 | if (typeof a === 'function') {
|
506 | cb = a;
|
507 | }
|
508 | options = {};
|
509 | }
|
510 | parser = new exports.Parser(options);
|
511 | return parser.parseString(str, cb);
|
512 | };
|
513 |
|
514 | }).call(this);
|