audiobookshelf/server/libs/xml/index.js

286 lines
6.1 KiB
JavaScript

// node-xml
// SOURCE: https://github.com/dylang/node-xml
// LICENSE: https://github.com/dylang/node-xml/blob/master/LICENSE
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;