mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			286 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			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; |