UNPKG

16.7 kBJavaScriptView Raw
1// Generated by CoffeeScript 1.9.3
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);