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; |