电速宝智配引擎
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. var escapeForXML = require('./escapeForXML');
  2. var Stream = require('stream').Stream;
  3. var DEFAULT_INDENT = ' ';
  4. function xml(input, options) {
  5. if (typeof options !== 'object') {
  6. options = {
  7. indent: options
  8. };
  9. }
  10. var stream = options.stream ? new Stream() : null,
  11. output = "",
  12. interrupted = false,
  13. indent = !options.indent ? ''
  14. : options.indent === true ? DEFAULT_INDENT
  15. : options.indent,
  16. instant = true;
  17. function delay (func) {
  18. if (!instant) {
  19. func();
  20. } else {
  21. process.nextTick(func);
  22. }
  23. }
  24. function append (interrupt, out) {
  25. if (out !== undefined) {
  26. output += out;
  27. }
  28. if (interrupt && !interrupted) {
  29. stream = stream || new Stream();
  30. interrupted = true;
  31. }
  32. if (interrupt && interrupted) {
  33. var data = output;
  34. delay(function () { stream.emit('data', data) });
  35. output = "";
  36. }
  37. }
  38. function add (value, last) {
  39. format(append, resolve(value, indent, indent ? 1 : 0), last);
  40. }
  41. function end() {
  42. if (stream) {
  43. var data = output;
  44. delay(function () {
  45. stream.emit('data', data);
  46. stream.emit('end');
  47. stream.readable = false;
  48. stream.emit('close');
  49. });
  50. }
  51. }
  52. function addXmlDeclaration(declaration) {
  53. var encoding = declaration.encoding || 'UTF-8',
  54. attr = { version: '1.0', encoding: encoding };
  55. if (declaration.standalone) {
  56. attr.standalone = declaration.standalone
  57. }
  58. add({'?xml': { _attr: attr } });
  59. output = output.replace('/>', '?>');
  60. }
  61. // disable delay delayed
  62. delay(function () { instant = false });
  63. if (options.declaration) {
  64. addXmlDeclaration(options.declaration);
  65. }
  66. if (input && input.forEach) {
  67. input.forEach(function (value, i) {
  68. var last;
  69. if (i + 1 === input.length)
  70. last = end;
  71. add(value, last);
  72. });
  73. } else {
  74. add(input, end);
  75. }
  76. if (stream) {
  77. stream.readable = true;
  78. return stream;
  79. }
  80. return output;
  81. }
  82. function element (/*input, …*/) {
  83. var input = Array.prototype.slice.call(arguments),
  84. self = {
  85. _elem: resolve(input)
  86. };
  87. self.push = function (input) {
  88. if (!this.append) {
  89. throw new Error("not assigned to a parent!");
  90. }
  91. var that = this;
  92. var indent = this._elem.indent;
  93. format(this.append, resolve(
  94. input, indent, this._elem.icount + (indent ? 1 : 0)),
  95. function () { that.append(true) });
  96. };
  97. self.close = function (input) {
  98. if (input !== undefined) {
  99. this.push(input);
  100. }
  101. if (this.end) {
  102. this.end();
  103. }
  104. };
  105. return self;
  106. }
  107. function create_indent(character, count) {
  108. return (new Array(count || 0).join(character || ''))
  109. }
  110. function resolve(data, indent, indent_count) {
  111. indent_count = indent_count || 0;
  112. var indent_spaces = create_indent(indent, indent_count);
  113. var name;
  114. var values = data;
  115. var interrupt = false;
  116. if (typeof data === 'object') {
  117. var keys = Object.keys(data);
  118. name = keys[0];
  119. values = data[name];
  120. if (values && values._elem) {
  121. values._elem.name = name;
  122. values._elem.icount = indent_count;
  123. values._elem.indent = indent;
  124. values._elem.indents = indent_spaces;
  125. values._elem.interrupt = values;
  126. return values._elem;
  127. }
  128. }
  129. var attributes = [],
  130. content = [];
  131. var isStringContent;
  132. function get_attributes(obj){
  133. var keys = Object.keys(obj);
  134. keys.forEach(function(key){
  135. attributes.push(attribute(key, obj[key]));
  136. });
  137. }
  138. switch(typeof values) {
  139. case 'object':
  140. if (values === null) break;
  141. if (values._attr) {
  142. get_attributes(values._attr);
  143. }
  144. if (values._cdata) {
  145. content.push(
  146. ('<![CDATA[' + values._cdata).replace(/\]\]>/g, ']]]]><![CDATA[>') + ']]>'
  147. );
  148. }
  149. if (values.forEach) {
  150. isStringContent = false;
  151. content.push('');
  152. values.forEach(function(value) {
  153. if (typeof value == 'object') {
  154. var _name = Object.keys(value)[0];
  155. if (_name == '_attr') {
  156. get_attributes(value._attr);
  157. } else {
  158. content.push(resolve(
  159. value, indent, indent_count + 1));
  160. }
  161. } else {
  162. //string
  163. content.pop();
  164. isStringContent=true;
  165. content.push(escapeForXML(value));
  166. }
  167. });
  168. if (!isStringContent) {
  169. content.push('');
  170. }
  171. }
  172. break;
  173. default:
  174. //string
  175. content.push(escapeForXML(values));
  176. }
  177. return {
  178. name: name,
  179. interrupt: interrupt,
  180. attributes: attributes,
  181. content: content,
  182. icount: indent_count,
  183. indents: indent_spaces,
  184. indent: indent
  185. };
  186. }
  187. function format(append, elem, end) {
  188. if (typeof elem != 'object') {
  189. return append(false, elem);
  190. }
  191. var len = elem.interrupt ? 1 : elem.content.length;
  192. function proceed () {
  193. while (elem.content.length) {
  194. var value = elem.content.shift();
  195. if (value === undefined) continue;
  196. if (interrupt(value)) return;
  197. format(append, value);
  198. }
  199. append(false, (len > 1 ? elem.indents : '')
  200. + (elem.name ? '</' + elem.name + '>' : '')
  201. + (elem.indent && !end ? '\n' : ''));
  202. if (end) {
  203. end();
  204. }
  205. }
  206. function interrupt(value) {
  207. if (value.interrupt) {
  208. value.interrupt.append = append;
  209. value.interrupt.end = proceed;
  210. value.interrupt = false;
  211. append(true);
  212. return true;
  213. }
  214. return false;
  215. }
  216. append(false, elem.indents
  217. + (elem.name ? '<' + elem.name : '')
  218. + (elem.attributes.length ? ' ' + elem.attributes.join(' ') : '')
  219. + (len ? (elem.name ? '>' : '') : (elem.name ? '/>' : ''))
  220. + (elem.indent && len > 1 ? '\n' : ''));
  221. if (!len) {
  222. return append(false, elem.indent ? '\n' : '');
  223. }
  224. if (!interrupt(elem)) {
  225. proceed();
  226. }
  227. }
  228. function attribute(key, value) {
  229. return key + '=' + '"' + escapeForXML(value) + '"';
  230. }
  231. module.exports = xml;
  232. module.exports.element = module.exports.Element = element;