4 var bom, builder, escapeCDATA, events, isEmpty, processName, processors, requiresCDATA, sax, setImmediate, 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); }; };
11 events = require(
'events');
13 builder = require(
'xmlbuilder');
15 bom = require(
'./bom');
17 processors = require(
'./processors');
19 setImmediate = require(
'timers').setImmediate;
21 isEmpty =
function(thing) {
22 return typeof thing ===
"object" && (thing != null) && Object.keys(thing).length === 0;
25 processName =
function(processors, processedName) {
27 for (i = 0, len = processors.length; i < len; i++) {
28 process = processors[i];
29 processedName = process(processedName);
34 requiresCDATA =
function(entry) {
35 return entry.indexOf(
'&') >= 0 || entry.indexOf(
'>') >= 0 || entry.indexOf(
'<') >= 0;
38 wrapCDATA =
function(entry) {
39 return "<![CDATA[" + (escapeCDATA(entry)) +
"]]>";
42 escapeCDATA =
function(entry) {
43 return entry.replace(
']]>',
']]]]><![CDATA[>');
46 exports.processors = processors;
50 explicitCharkey:
false,
62 explicitChildren:
false,
64 charsAsChildren:
false,
65 includeWhiteChars:
false,
68 attrNameProcessors: null,
69 attrValueProcessors: null,
70 tagNameProcessors: null,
71 valueProcessors: null,
75 explicitCharkey:
false,
87 explicitChildren:
false,
88 preserveChildrenOrder:
false,
90 charsAsChildren:
false,
91 includeWhiteChars:
false,
94 attrNameProcessors: null,
95 attrValueProcessors: null,
96 tagNameProcessors: null,
97 valueProcessors: null,
117 exports.ValidationError = (
function(superClass) {
118 extend(ValidationError, superClass);
120 function ValidationError(message) {
121 this.message = message;
124 return ValidationError;
128 exports.Builder = (
function() {
129 function Builder(opts) {
132 ref = exports.defaults[
"0.2"];
134 if (!hasProp.call(ref, key))
continue;
136 this.options[key] = value;
139 if (!hasProp.call(opts, key))
continue;
141 this.options[key] = value;
145 Builder.prototype.buildObject =
function(rootObj) {
146 var attrkey, charkey, render, rootElement, rootName;
147 attrkey = this.options.attrkey;
148 charkey = this.options.charkey;
149 if ((Object.keys(rootObj).length === 1) && (this.options.rootName === exports.defaults[
'0.2'].rootName)) {
150 rootName = Object.keys(rootObj)[0];
151 rootObj = rootObj[rootName];
153 rootName = this.options.rootName;
155 render = (
function(_this) {
156 return function(element, obj) {
157 var attr, child, entry, index, key, value;
158 if (typeof obj !==
'object') {
159 if (_this.options.cdata && requiresCDATA(obj)) {
160 element.raw(wrapCDATA(obj));
166 if (!hasProp.call(obj, key))
continue;
168 if (key === attrkey) {
169 if (typeof child ===
"object") {
170 for (attr in child) {
172 element = element.att(attr, value);
175 }
else if (key === charkey) {
176 if (_this.options.cdata && requiresCDATA(child)) {
177 element = element.raw(wrapCDATA(child));
179 element = element.txt(child);
181 }
else if (Array.isArray(child)) {
182 for (index in child) {
183 if (!hasProp.call(child, index))
continue;
184 entry = child[index];
185 if (typeof entry ===
'string') {
186 if (_this.options.cdata && requiresCDATA(entry)) {
187 element = element.ele(key).raw(wrapCDATA(entry)).up();
189 element = element.ele(key, entry).up();
192 element = render(element.ele(key), entry).up();
195 }
else if (typeof child ===
"object") {
196 element = render(element.ele(key), child).up();
198 if (typeof child ===
'string' && _this.options.cdata && requiresCDATA(child)) {
199 element = element.ele(key).raw(wrapCDATA(child)).up();
204 element = element.ele(key, child.toString()).up();
212 rootElement = builder.create(rootName, this.options.xmldec,
this.options.doctype, {
213 headless:
this.options.headless,
214 allowSurrogateChars:
this.options.allowSurrogateChars
216 return render(rootElement, rootObj).end(this.options.renderOpts);
223 exports.Parser = (
function(superClass) {
224 extend(Parser, superClass);
226 function Parser(opts) {
227 this.parseString = bind(this.parseString,
this);
228 this.reset = bind(this.reset,
this);
229 this.assignOrPush = bind(this.assignOrPush,
this);
230 this.processAsync = bind(this.processAsync,
this);
232 if (!(
this instanceof exports.Parser)) {
233 return new exports.Parser(opts);
236 ref = exports.defaults[
"0.2"];
238 if (!hasProp.call(ref, key))
continue;
240 this.options[key] = value;
243 if (!hasProp.call(opts, key))
continue;
245 this.options[key] = value;
247 if (this.options.xmlns) {
248 this.options.xmlnskey = this.options.attrkey +
"ns";
250 if (this.options.normalizeTags) {
251 if (!this.options.tagNameProcessors) {
252 this.options.tagNameProcessors = [];
254 this.options.tagNameProcessors.unshift(processors.normalize);
259 Parser.prototype.processAsync =
function() {
260 var chunk, err, error1;
262 if (this.remaining.length <=
this.options.chunkSize) {
263 chunk = this.remaining;
265 this.saxParser = this.saxParser.write(chunk);
266 return this.saxParser.close();
268 chunk = this.remaining.substr(0, this.options.chunkSize);
269 this.remaining = this.remaining.substr(this.options.chunkSize,
this.remaining.length);
270 this.saxParser = this.saxParser.write(chunk);
271 return setImmediate(this.processAsync);
275 if (!this.saxParser.errThrown) {
276 this.saxParser.errThrown =
true;
277 return this.emit(err);
282 Parser.prototype.assignOrPush =
function(obj, key, newValue) {
284 if (!this.options.explicitArray) {
285 return obj[key] = newValue;
287 return obj[key] = [newValue];
290 if (!(obj[key] instanceof Array)) {
291 obj[key] = [obj[key]];
293 return obj[key].push(newValue);
297 Parser.prototype.reset =
function() {
298 var attrkey, charkey, ontext, stack;
299 this.removeAllListeners();
300 this.saxParser = sax.parser(this.options.strict, {
303 xmlns:
this.options.xmlns
305 this.saxParser.errThrown =
false;
306 this.saxParser.onerror = (
function(_this) {
307 return function(error) {
308 _this.saxParser.resume();
309 if (!_this.saxParser.errThrown) {
310 _this.saxParser.errThrown =
true;
311 return _this.emit(
"error", error);
315 this.saxParser.onend = (
function(_this) {
317 if (!_this.saxParser.ended) {
318 _this.saxParser.ended =
true;
319 return _this.emit(
"end", _this.resultObject);
323 this.saxParser.ended =
false;
324 this.EXPLICIT_CHARKEY = this.options.explicitCharkey;
325 this.resultObject = null;
327 attrkey = this.options.attrkey;
328 charkey = this.options.charkey;
329 this.saxParser.onopentag = (
function(_this) {
330 return function(node) {
331 var key, newValue, obj, processedKey, ref;
334 if (!_this.options.ignoreAttrs) {
335 ref = node.attributes;
337 if (!hasProp.call(ref, key))
continue;
338 if (!(attrkey in obj) && !_this.options.mergeAttrs) {
341 newValue = _this.options.attrValueProcessors ? processName(_this.options.attrValueProcessors, node.attributes[key]) : node.attributes[key];
342 processedKey = _this.options.attrNameProcessors ? processName(_this.options.attrNameProcessors, key) : key;
343 if (_this.options.mergeAttrs) {
344 _this.assignOrPush(obj, processedKey, newValue);
346 obj[attrkey][processedKey] = newValue;
350 obj[
"#name"] = _this.options.tagNameProcessors ? processName(_this.options.tagNameProcessors, node.name) : node.name;
351 if (_this.options.xmlns) {
352 obj[_this.options.xmlnskey] = {
357 return stack.push(obj);
360 this.saxParser.onclosetag = (
function(_this) {
362 var cdata, emptyStr, err, error1, key, node, nodeName, obj, objClone, old, s, xpath;
364 nodeName = obj[
"#name"];
365 if (!_this.options.explicitChildren || !_this.options.preserveChildrenOrder) {
368 if (obj.cdata ===
true) {
372 s = stack[stack.length - 1];
373 if (obj[charkey].match(/^\s*$/) && !cdata) {
374 emptyStr = obj[charkey];
377 if (_this.options.trim) {
378 obj[charkey] = obj[charkey].trim();
380 if (_this.options.normalize) {
381 obj[charkey] = obj[charkey].replace(/\s{2,}/g,
" ").trim();
383 obj[charkey] = _this.options.valueProcessors ? processName(_this.options.valueProcessors, obj[charkey]) : obj[charkey];
384 if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
389 obj = _this.options.emptyTag !==
'' ? _this.options.emptyTag : emptyStr;
391 if (_this.options.validator != null) {
392 xpath =
"/" + ((
function() {
395 for (i = 0, len = stack.length; i < len; i++) {
397 results.push(node[
"#name"]);
400 })()).concat(nodeName).join(
"/");
402 obj = _this.options.validator(xpath, s && s[nodeName], obj);
405 _this.emit(
"error", err);
408 if (_this.options.explicitChildren && !_this.options.mergeAttrs && typeof obj ===
'object') {
409 if (!_this.options.preserveChildrenOrder) {
411 if (_this.options.attrkey in obj) {
412 node[_this.options.attrkey] = obj[_this.options.attrkey];
413 delete obj[_this.options.attrkey];
415 if (!_this.options.charsAsChildren && _this.options.charkey in obj) {
416 node[_this.options.charkey] = obj[_this.options.charkey];
417 delete obj[_this.options.charkey];
419 if (Object.getOwnPropertyNames(obj).length > 0) {
420 node[_this.options.childkey] = obj;
424 s[_this.options.childkey] = s[_this.options.childkey] || [];
427 if (!hasProp.call(obj, key))
continue;
428 objClone[key] = obj[key];
430 s[_this.options.childkey].push(objClone);
432 if (Object.keys(obj).length === 1 && charkey in obj && !_this.EXPLICIT_CHARKEY) {
437 if (stack.length > 0) {
438 return _this.assignOrPush(s, nodeName, obj);
440 if (_this.options.explicitRoot) {
445 _this.resultObject = obj;
446 _this.saxParser.ended =
true;
447 return _this.emit(
"end", _this.resultObject);
451 ontext = (
function(_this) {
452 return function(text) {
454 s = stack[stack.length - 1];
457 if (_this.options.explicitChildren && _this.options.preserveChildrenOrder && _this.options.charsAsChildren && (_this.options.includeWhiteChars || text.replace(/\\n/g,
'').trim() !==
'')) {
458 s[_this.options.childkey] = s[_this.options.childkey] || [];
462 charChild[charkey] = text;
463 if (_this.options.normalize) {
464 charChild[charkey] = charChild[charkey].replace(/\s{2,}/g,
" ").trim();
466 s[_this.options.childkey].push(charChild);
472 this.saxParser.ontext = ontext;
473 return this.saxParser.oncdata = (
function(_this) {
474 return function(text) {
478 return s.cdata =
true;
484 Parser.prototype.parseString =
function(str, cb) {
486 if ((cb != null) && typeof cb ===
"function") {
487 this.on(
"end",
function(result) {
489 return cb(null, result);
491 this.on(
"error",
function(err) {
497 str = str.toString();
498 if (str.trim() ===
'') {
499 this.emit(
"end", null);
502 str = bom.stripBOM(str);
503 if (this.options.async) {
504 this.remaining = str;
505 setImmediate(this.processAsync);
506 return this.saxParser;
508 return this.saxParser.write(str).close();
511 if (!(this.saxParser.errThrown ||
this.saxParser.ended)) {
512 this.emit(
'error', err);
513 return this.saxParser.errThrown =
true;
514 }
else if (this.saxParser.ended) {
522 })(events.EventEmitter);
524 exports.parseString =
function(str, a, b) {
525 var cb, options, parser;
527 if (typeof b ===
'function') {
530 if (typeof a ===
'object') {
534 if (typeof a ===
'function') {
539 parser =
new exports.Parser(options);
540 return parser.parseString(str, cb);