| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- var escapeForXML = require('./escapeForXML');
- var Stream = require('stream').Stream;
-
- var DEFAULT_INDENT = ' ';
-
- function xml(input, options) {
-
- if (typeof options !== 'object') {
- options = {
- indent: options
- };
- }
-
- var stream = options.stream ? new Stream() : null,
- output = "",
- interrupted = false,
- indent = !options.indent ? ''
- : options.indent === true ? DEFAULT_INDENT
- : options.indent,
- instant = true;
-
-
- function delay (func) {
- if (!instant) {
- func();
- } else {
- process.nextTick(func);
- }
- }
-
- function append (interrupt, out) {
- if (out !== undefined) {
- output += out;
- }
- if (interrupt && !interrupted) {
- stream = stream || new Stream();
- interrupted = true;
- }
- if (interrupt && interrupted) {
- var data = output;
- delay(function () { stream.emit('data', data) });
- output = "";
- }
- }
-
- function add (value, last) {
- format(append, resolve(value, indent, indent ? 1 : 0), last);
- }
-
- function end() {
- if (stream) {
- var data = output;
- delay(function () {
- stream.emit('data', data);
- stream.emit('end');
- stream.readable = false;
- stream.emit('close');
- });
- }
- }
-
- function addXmlDeclaration(declaration) {
- var encoding = declaration.encoding || 'UTF-8',
- attr = { version: '1.0', encoding: encoding };
-
- if (declaration.standalone) {
- attr.standalone = declaration.standalone
- }
-
- add({'?xml': { _attr: attr } });
- output = output.replace('/>', '?>');
- }
-
- // disable delay delayed
- delay(function () { instant = false });
-
- if (options.declaration) {
- addXmlDeclaration(options.declaration);
- }
-
- if (input && input.forEach) {
- input.forEach(function (value, i) {
- var last;
- if (i + 1 === input.length)
- last = end;
- add(value, last);
- });
- } else {
- add(input, end);
- }
-
- if (stream) {
- stream.readable = true;
- return stream;
- }
- return output;
- }
-
- function element (/*input, …*/) {
- var input = Array.prototype.slice.call(arguments),
- self = {
- _elem: resolve(input)
- };
-
- self.push = function (input) {
- if (!this.append) {
- throw new Error("not assigned to a parent!");
- }
- var that = this;
- var indent = this._elem.indent;
- format(this.append, resolve(
- input, indent, this._elem.icount + (indent ? 1 : 0)),
- function () { that.append(true) });
- };
-
- self.close = function (input) {
- if (input !== undefined) {
- this.push(input);
- }
- if (this.end) {
- this.end();
- }
- };
-
- return self;
- }
-
- function create_indent(character, count) {
- return (new Array(count || 0).join(character || ''))
- }
-
- function resolve(data, indent, indent_count) {
- indent_count = indent_count || 0;
- var indent_spaces = create_indent(indent, indent_count);
- var name;
- var values = data;
- var interrupt = false;
-
- if (typeof data === 'object') {
- var keys = Object.keys(data);
- name = keys[0];
- values = data[name];
-
- if (values && values._elem) {
- values._elem.name = name;
- values._elem.icount = indent_count;
- values._elem.indent = indent;
- values._elem.indents = indent_spaces;
- values._elem.interrupt = values;
- return values._elem;
- }
- }
-
- var attributes = [],
- content = [];
-
- var isStringContent;
-
- function get_attributes(obj){
- var keys = Object.keys(obj);
- keys.forEach(function(key){
- attributes.push(attribute(key, obj[key]));
- });
- }
-
- switch(typeof values) {
- case 'object':
- if (values === null) break;
-
- if (values._attr) {
- get_attributes(values._attr);
- }
-
- if (values._cdata) {
- content.push(
- ('<![CDATA[' + values._cdata).replace(/\]\]>/g, ']]]]><![CDATA[>') + ']]>'
- );
- }
-
- if (values.forEach) {
- isStringContent = false;
- content.push('');
- values.forEach(function(value) {
- if (typeof value == 'object') {
- var _name = Object.keys(value)[0];
-
- if (_name == '_attr') {
- get_attributes(value._attr);
- } else {
- content.push(resolve(
- value, indent, indent_count + 1));
- }
- } else {
- //string
- content.pop();
- isStringContent=true;
- content.push(escapeForXML(value));
- }
-
- });
- if (!isStringContent) {
- content.push('');
- }
- }
- break;
-
- default:
- //string
- content.push(escapeForXML(values));
-
- }
-
- return {
- name: name,
- interrupt: interrupt,
- attributes: attributes,
- content: content,
- icount: indent_count,
- indents: indent_spaces,
- indent: indent
- };
- }
-
- function format(append, elem, end) {
-
- if (typeof elem != 'object') {
- return append(false, elem);
- }
-
- var len = elem.interrupt ? 1 : elem.content.length;
-
- function proceed () {
- while (elem.content.length) {
- var value = elem.content.shift();
-
- if (value === undefined) continue;
- if (interrupt(value)) return;
-
- format(append, value);
- }
-
- append(false, (len > 1 ? elem.indents : '')
- + (elem.name ? '</' + elem.name + '>' : '')
- + (elem.indent && !end ? '\n' : ''));
-
- if (end) {
- end();
- }
- }
-
- function interrupt(value) {
- if (value.interrupt) {
- value.interrupt.append = append;
- value.interrupt.end = proceed;
- value.interrupt = false;
- append(true);
- return true;
- }
- return false;
- }
-
- append(false, elem.indents
- + (elem.name ? '<' + elem.name : '')
- + (elem.attributes.length ? ' ' + elem.attributes.join(' ') : '')
- + (len ? (elem.name ? '>' : '') : (elem.name ? '/>' : ''))
- + (elem.indent && len > 1 ? '\n' : ''));
-
- if (!len) {
- return append(false, elem.indent ? '\n' : '');
- }
-
- if (!interrupt(elem)) {
- proceed();
- }
- }
-
- function attribute(key, value) {
- return key + '=' + '"' + escapeForXML(value) + '"';
- }
-
- module.exports = xml;
- module.exports.element = module.exports.Element = element;
|