xml-writer
Version:
Native and full Javascript implementation of the classic XMLWriter class
400 lines (353 loc) • 11.5 kB
JavaScript
function isFalse(s) {
return typeof s !== 'number' && !s;
}
function strval(s) {
if (typeof s == 'string') {
return s;
}
else if (typeof s == 'number') {
return s+'';
}
else if (typeof s == 'function') {
return s();
}
else if (s instanceof XMLWriter) {
return s.toString();
}
else throw Error('Bad Parameter');
}
function XMLWriter(indent, callback) {
if (!(this instanceof XMLWriter)) {
return new XMLWriter();
}
this.name_regex = /[_:A-Za-z][-._:A-Za-z0-9]*/;
this.indent = indent ? true : false;
this.indentString = this.indent && typeof indent === 'string' ? indent : ' ';
this.output = '';
this.stack = [];
this.tags = 0;
this.attributes = 0;
this.attribute = 0;
this.texts = 0;
this.comment = 0;
this.dtd = 0;
this.root = '';
this.pi = 0;
this.cdata = 0;
this.started_write = false;
this.writer;
this.writer_encoding = 'UTF-8';
if (typeof callback == 'function') {
this.writer = callback;
} else {
this.writer = function (s, e) {
this.output += s;
}
}
}
XMLWriter.prototype = {
toString : function () {
this.flush();
return this.output;
},
indenter : function () {
if (this.indent) {
this.write('\n');
for (var i = 1; i < this.tags; i++) {
this.write(this.indentString);
}
}
},
write : function () {
for (var i = 0; i < arguments.length; i++) {
this.writer(arguments[i], this.writer_encoding);
}
},
flush : function () {
for (var i = this.tags; i > 0; i--) {
this.endElement();
}
this.tags = 0;
},
startDocument : function (version, encoding, standalone) {
if (this.tags || this.attributes) return this;
this.startPI('xml');
this.startAttribute('version');
this.text(typeof version == "string" ? version : "1.0");
this.endAttribute();
if (typeof encoding == "string") {
this.startAttribute('encoding');
this.text(encoding);
this.endAttribute();
this.writer_encoding = encoding;
}
if (standalone) {
this.startAttribute('standalone');
this.text("yes");
this.endAttribute();
}
this.endPI();
if (!this.indent) {
this.write('\n');
}
return this;
},
endDocument : function () {
if (this.attributes) this.endAttributes();
return this;
},
writeElement : function (name, content) {
return this.startElement(name).text(content).endElement();
},
writeElementNS : function (prefix, name, uri, content) {
if (!content) {
content = uri;
}
return this.startElementNS(prefix, name, uri).text(content).endElement();
},
startElement : function (name) {
name = strval(name);
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (this.tags === 0 && this.root && this.root !== name) throw Error('Invalid Parameter');
if (this.attributes) this.endAttributes();
++this.tags;
this.texts = 0;
if (this.stack.length > 0)
this.stack[this.stack.length-1].containsTag = true;
this.stack.push({
name: name,
tags: this.tags
});
if (this.started_write) this.indenter();
this.write('<', name);
this.startAttributes();
this.started_write = true;
return this;
},
startElementNS : function (prefix, name, uri) {
prefix = strval(prefix);
name = strval(name);
if (!prefix.match(this.name_regex)) throw Error('Invalid Parameter');
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (this.attributes) this.endAttributes();
++this.tags;
this.texts = 0;
if (this.stack.length > 0)
this.stack[this.stack.length-1].containsTag = true;
this.stack.push({
name: prefix + ':' + name,
tags: this.tags
});
if (this.started_write) this.indenter();
this.write('<', prefix + ':' + name);
this.startAttributes();
this.started_write = true;
return this;
},
endElement : function () {
if (!this.tags) return this;
var t = this.stack.pop();
if (this.attributes > 0) {
if (this.attribute) {
if (this.texts) this.endAttribute();
this.endAttribute();
}
this.write('/');
this.endAttributes();
} else {
if (t.containsTag) this.indenter();
this.write('</', t.name, '>');
}
--this.tags;
this.texts = 0;
return this;
},
writeAttribute : function (name, content) {
if (typeof content == 'function') {
content = content();
}
if (isFalse(content)) {
return this;
}
return this.startAttribute(name).text(content).endAttribute();
},
writeAttributeNS : function (prefix, name, uri, content) {
if (!content) {
content = uri;
}
if (typeof content == 'function') {
content = content();
}
if (isFalse(content)) {
return this;
}
return this.startAttributeNS(prefix, name, uri).text(content).endAttribute();
},
startAttributes : function () {
this.attributes = 1;
return this;
},
endAttributes : function () {
if (!this.attributes) return this;
if (this.attribute) this.endAttribute();
this.attributes = 0;
this.attribute = 0;
this.texts = 0;
this.write('>');
return this;
},
startAttribute : function (name) {
name = strval(name);
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (!this.attributes && !this.pi) return this;
if (this.attribute) return this;
this.attribute = 1;
this.write(' ', name, '="');
return this;
},
startAttributeNS : function (prefix, name, uri) {
prefix = strval(prefix);
name = strval(name);
if (!prefix.match(this.name_regex)) throw Error('Invalid Parameter');
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (!this.attributes && !this.pi) return this;
if (this.attribute) return this;
this.attribute = 1;
this.write(' ', prefix + ':' + name, '="');
return this;
},
endAttribute : function () {
if (!this.attribute) return this;
this.attribute = 0;
this.texts = 0;
this.write('"');
return this;
},
text : function (content) {
content = strval(content);
if (!this.tags && !this.comment && !this.pi && !this.cdata) return this;
if (this.attributes && this.attribute) {
++this.texts;
this.write(content
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/"/g, '"')
.replace(/\t/g, '	')
.replace(/\n/g, '
')
.replace(/\r/g, '
')
);
return this;
} else if (this.attributes && !this.attribute) {
this.endAttributes();
}
if (this.comment || this.cdata) {
this.write(content);
}
else {
this.write(content.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'));
}
++this.texts;
this.started_write = true;
return this;
},
writeComment : function (content) {
return this.startComment().text(content).endComment();
},
startComment : function () {
if (this.comment) return this;
if (this.attributes) this.endAttributes();
this.indenter();
this.write('<!--');
this.comment = 1;
this.started_write = true;
return this;
},
endComment : function () {
if (!this.comment) return this;
this.write('-->');
this.comment = 0;
return this;
},
writeDocType : function (name, pubid, sysid, subset) {
return this.startDocType(name, pubid, sysid, subset).endDocType()
},
startDocType : function (name, pubid, sysid, subset) {
if (this.dtd || this.tags) return this;
name = strval(name);
pubid = pubid ? strval(pubid) : pubid;
sysid = sysid ? strval(sysid) : sysid;
subset = subset ? strval(subset) : subset;
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (pubid && !pubid.match(/^[\w\-][\w\s\-\/\+\:\.]*/)) throw Error('Invalid Parameter');
if (sysid && !sysid.match(/^[\w\.][\w\-\/\\\:\.]*/)) throw Error('Invalid Parameter');
if (subset && !subset.match(/[\w\s\<\>\+\.\!\#\-\?\*\,\(\)\|]*/)) throw Error('Invalid Parameter');
pubid = pubid ? ' PUBLIC "' + pubid + '"' : (sysid) ? ' SYSTEM' : '';
sysid = sysid ? ' "' + sysid + '"' : '';
subset = subset ? ' [' + subset + ']': '';
if (this.started_write) this.indenter();
this.write('<!DOCTYPE ', name, pubid, sysid, subset);
this.root = name;
this.dtd = 1;
this.started_write = true;
return this;
},
endDocType : function () {
if (!this.dtd) return this;
this.write('>');
return this;
},
writePI : function (name, content) {
return this.startPI(name).text(content).endPI()
},
startPI : function (name) {
name = strval(name);
if (!name.match(this.name_regex)) throw Error('Invalid Parameter');
if (this.pi) return this;
if (this.attributes) this.endAttributes();
if (this.started_write) this.indenter();
this.write('<?', name);
this.pi = 1;
this.started_write = true;
return this;
},
endPI : function () {
if (!this.pi) return this;
this.write('?>');
this.pi = 0;
return this;
},
writeCData : function (content) {
return this.startCData().text(content).endCData();
},
startCData : function () {
if (this.cdata) return this;
if (this.attributes) this.endAttributes();
this.indenter();
this.write('<![CDATA[');
this.cdata = 1;
this.started_write = true;
return this;
},
endCData : function () {
if (!this.cdata) return this;
this.write(']]>');
this.cdata = 0;
return this;
},
writeRaw : function(content) {
content = strval(content);
if (!this.tags && !this.comment && !this.pi && !this.cdata) return this;
if (this.attributes && this.attribute) {
++this.texts;
this.write(content.replace('&', '&').replace('"', '"'));
return this;
} else if (this.attributes && !this.attribute) {
this.endAttributes();
}
++this.texts;
this.write(content);
this.started_write = true;
return this;
}
}
module.exports = XMLWriter;