const ARRAY = "array";
							 | 
						|
								const BOOLEAN = "boolean";
							 | 
						|
								const DATE = "date";
							 | 
						|
								const NULL = "null";
							 | 
						|
								const NUMBER = "number";
							 | 
						|
								const OBJECT = "object";
							 | 
						|
								const SPECIAL_OBJECT = "special-object";
							 | 
						|
								const STRING = "string";
							 | 
						|
								
							 | 
						|
								const PRIVATE_VARS = ["_selfCloseTag", "_attrs"];
							 | 
						|
								const PRIVATE_VARS_REGEXP = new RegExp(PRIVATE_VARS.join("|"), "g");
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Determines the indent string based on current tree depth.
							 | 
						|
								 */
							 | 
						|
								const getIndentStr = (indent = "", depth = 0) => indent.repeat(depth);
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Sugar function supplementing JS's quirky typeof operator, plus some extra help to detect
							 | 
						|
								 * "special objects" expected by jstoxml.
							 | 
						|
								 * Example:
							 | 
						|
								 * getType(new Date());
							 | 
						|
								 * -> 'date'
							 | 
						|
								 */
							 | 
						|
								const getType = (val) =>
							 | 
						|
								  (Array.isArray(val) && ARRAY) ||
							 | 
						|
								  (typeof val === OBJECT && val !== null && val._name && SPECIAL_OBJECT) ||
							 | 
						|
								  (val instanceof Date && DATE) ||
							 | 
						|
								  (val === null && NULL) ||
							 | 
						|
								  typeof val;
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Replaces matching values in a string with a new value.
							 | 
						|
								 * Example:
							 | 
						|
								 * filterStr('foo&bar', { '&': '&' });
							 | 
						|
								 * -> 'foo&bar'
							 | 
						|
								 */
							 | 
						|
								const filterStr = (inputStr = "", filter = {}) => {
							 | 
						|
								  // Passthrough/no-op for nonstrings (e.g. number, boolean).
							 | 
						|
								  if (typeof inputStr !== "string") {
							 | 
						|
								    return inputStr;
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  const regexp = new RegExp(
							 | 
						|
								    `(${Object.keys(filter).join("|")})(?!(\\w|#)*;)`,
							 | 
						|
								    "g"
							 | 
						|
								  );
							 | 
						|
								
							 | 
						|
								  return String(inputStr).replace(
							 | 
						|
								    regexp,
							 | 
						|
								    (str, entity) => filter[entity] || ""
							 | 
						|
								  );
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Maps an object or array of arribute keyval pairs to a string.
							 | 
						|
								 * Examples:
							 | 
						|
								 * { foo: 'bar', baz: 'g' } -> 'foo="bar" baz="g"'
							 | 
						|
								 * [ { ⚡: true }, { foo: 'bar' } ] -> '⚡ foo="bar"'
							 | 
						|
								 */
							 | 
						|
								const getAttributeKeyVals = (attributes = {}, filter) => {
							 | 
						|
								  let keyVals = [];
							 | 
						|
								  if (Array.isArray(attributes)) {
							 | 
						|
								    // Array containing complex objects and potentially duplicate attributes.
							 | 
						|
								    keyVals = attributes.map((attr) => {
							 | 
						|
								      const key = Object.keys(attr)[0];
							 | 
						|
								      const val = attr[key];
							 | 
						|
								
							 | 
						|
								      const filteredVal = filter ? filterStr(val, filter) : val;
							 | 
						|
								      const valStr = filteredVal === true ? "" : `="${filteredVal}"`;
							 | 
						|
								      return `${key}${valStr}`;
							 | 
						|
								    });
							 | 
						|
								  } else {
							 | 
						|
								    const keys = Object.keys(attributes);
							 | 
						|
								    keyVals = keys.map((key) => {
							 | 
						|
								      // Simple object - keyval pairs.
							 | 
						|
								
							 | 
						|
								      // For boolean true, simply output the key.
							 | 
						|
								      const filteredVal = filter
							 | 
						|
								        ? filterStr(attributes[key], filter)
							 | 
						|
								        : attributes[key];
							 | 
						|
								      const valStr = attributes[key] === true ? "" : `="${filteredVal}"`;
							 | 
						|
								
							 | 
						|
								      return `${key}${valStr}`;
							 | 
						|
								    });
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return keyVals;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Converts an attributes object/array to a string of keyval pairs.
							 | 
						|
								 * Example:
							 | 
						|
								 * formatAttributes({ a: 1, b: 2 })
							 | 
						|
								 * -> 'a="1" b="2"'
							 | 
						|
								 */
							 | 
						|
								const formatAttributes = (attributes = {}, filter) => {
							 | 
						|
								  const keyVals = getAttributeKeyVals(attributes, filter);
							 | 
						|
								  if (keyVals.length === 0) return "";
							 | 
						|
								
							 | 
						|
								  const keysValsJoined = keyVals.join(" ");
							 | 
						|
								  return ` ${keysValsJoined}`;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Converts an object to a jstoxml array.
							 | 
						|
								 * Example:
							 | 
						|
								 * objToArray({ foo: 'bar', baz: 2 });
							 | 
						|
								 * ->
							 | 
						|
								 * [
							 | 
						|
								 *   {
							 | 
						|
								 *     _name: 'foo',
							 | 
						|
								 *     _content: 'bar'
							 | 
						|
								 *   },
							 | 
						|
								 *   {
							 | 
						|
								 *     _name: 'baz',
							 | 
						|
								 *     _content: 2
							 | 
						|
								 *   }
							 | 
						|
								 * ]
							 | 
						|
								 */
							 | 
						|
								const objToArray = (obj = {}) =>
							 | 
						|
								  Object.keys(obj).map((key) => {
							 | 
						|
								    return {
							 | 
						|
								      _name: key,
							 | 
						|
								      _content: obj[key],
							 | 
						|
								    };
							 | 
						|
								  });
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Determines if a value is a primitive JavaScript value (not including Symbol).
							 | 
						|
								 * Example:
							 | 
						|
								 * isPrimitive(4);
							 | 
						|
								 * -> true
							 | 
						|
								 */
							 | 
						|
								const PRIMITIVE_TYPES = [STRING, NUMBER, BOOLEAN];
							 | 
						|
								const isPrimitive = (val) => PRIMITIVE_TYPES.includes(getType(val));
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Determines if a value is a simple primitive type that can fit onto one line.  Needed for
							 | 
						|
								 * determining any needed indenting and line breaks.
							 | 
						|
								 * Example:
							 | 
						|
								 * isSimpleType(new Date());
							 | 
						|
								 * -> true
							 | 
						|
								 */
							 | 
						|
								const SIMPLE_TYPES = [...PRIMITIVE_TYPES, DATE, SPECIAL_OBJECT];
							 | 
						|
								const isSimpleType = (val) => SIMPLE_TYPES.includes(getType(val));
							 | 
						|
								/**
							 | 
						|
								 * Determines if an XML string is a simple primitive, or contains nested data.
							 | 
						|
								 * Example:
							 | 
						|
								 * isSimpleXML('<foo />');
							 | 
						|
								 * -> false
							 | 
						|
								 */
							 | 
						|
								const isSimpleXML = (xmlStr) => !xmlStr.match("<");
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Assembles an XML header as defined by the config.
							 | 
						|
								 */
							 | 
						|
								const DEFAULT_XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>';
							 | 
						|
								const getHeaderString = ({ header, indent, isOutputStart /*, depth */ }) => {
							 | 
						|
								  const shouldOutputHeader = header && isOutputStart;
							 | 
						|
								  if (!shouldOutputHeader) return "";
							 | 
						|
								
							 | 
						|
								  const shouldUseDefaultHeader = typeof header === BOOLEAN;
							 | 
						|
								  // return `${shouldUseDefaultHeader ? DEFAULT_XML_HEADER : header}${indent ? "\n" : ""
							 | 
						|
								  //   }`;
							 | 
						|
								  return shouldUseDefaultHeader ? DEFAULT_XML_HEADER : header;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Recursively traverses an object tree and converts the output to an XML string.
							 | 
						|
								 * Example:
							 | 
						|
								 * toXML({ foo: 'bar' });
							 | 
						|
								 * -> <foo>bar</foo>
							 | 
						|
								 */
							 | 
						|
								const defaultEntityFilter = {
							 | 
						|
								  "<": "<",
							 | 
						|
								  ">": ">",
							 | 
						|
								  "&": "&",
							 | 
						|
								};
							 | 
						|
								export const toXML = (obj = {}, config = {}) => {
							 | 
						|
								  const {
							 | 
						|
								    // Tree depth
							 | 
						|
								    depth = 0,
							 | 
						|
								    indent,
							 | 
						|
								    _isFirstItem,
							 | 
						|
								    // _isLastItem,
							 | 
						|
								    _isOutputStart = true,
							 | 
						|
								    header,
							 | 
						|
								    attributesFilter: rawAttributesFilter = {},
							 | 
						|
								    filter: rawFilter = {},
							 | 
						|
								  } = config;
							 | 
						|
								
							 | 
						|
								  const shouldTurnOffAttributesFilter = typeof rawAttributesFilter === 'boolean' && !rawAttributesFilter;
							 | 
						|
								  const attributesFilter = shouldTurnOffAttributesFilter ? {} : {
							 | 
						|
								    ...defaultEntityFilter,
							 | 
						|
								    ...{ '"': """ },
							 | 
						|
								    ...rawAttributesFilter,
							 | 
						|
								  };
							 | 
						|
								
							 | 
						|
								  const shouldTurnOffFilter = typeof rawFilter === 'boolean' && !rawFilter;
							 | 
						|
								  const filter = shouldTurnOffFilter ? {} : { ...defaultEntityFilter, ...rawFilter };
							 | 
						|
								
							 | 
						|
								  // Determine indent string based on depth.
							 | 
						|
								  const indentStr = getIndentStr(indent, depth);
							 | 
						|
								
							 | 
						|
								  // For branching based on value type.
							 | 
						|
								  const valType = getType(obj);
							 | 
						|
								
							 | 
						|
								  const headerStr = getHeaderString({ header, indent, depth, isOutputStart: _isOutputStart });
							 | 
						|
								
							 | 
						|
								  const isOutputStart = _isOutputStart && !headerStr && _isFirstItem && depth === 0;
							 | 
						|
								
							 | 
						|
								  let outputStr = "";
							 | 
						|
								  switch (valType) {
							 | 
						|
								    case "special-object": {
							 | 
						|
								      // Processes a specially-formatted object used by jstoxml.
							 | 
						|
								
							 | 
						|
								      const { _name, _content } = obj;
							 | 
						|
								
							 | 
						|
								      // Output text content without a tag wrapper.
							 | 
						|
								      if (_content === null) {
							 | 
						|
								        outputStr = _name;
							 | 
						|
								        break;
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      // Handles arrays of primitive values. (#33)
							 | 
						|
								      const isArrayOfPrimitives =
							 | 
						|
								        Array.isArray(_content) && _content.every(isPrimitive);
							 | 
						|
								      if (isArrayOfPrimitives) {
							 | 
						|
								        const primitives = _content
							 | 
						|
								          .map((a) => {
							 | 
						|
								            return toXML(
							 | 
						|
								              {
							 | 
						|
								                _name,
							 | 
						|
								                _content: a,
							 | 
						|
								              },
							 | 
						|
								              {
							 | 
						|
								                ...config,
							 | 
						|
								                depth,
							 | 
						|
								                _isOutputStart: false
							 | 
						|
								              }
							 | 
						|
								            );
							 | 
						|
								          });
							 | 
						|
								        return primitives.join('');
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      // Don't output private vars (such as _attrs).
							 | 
						|
								      if (_name.match(PRIVATE_VARS_REGEXP)) break;
							 | 
						|
								
							 | 
						|
								      // Process the nested new value and create new config.
							 | 
						|
								      const newVal = toXML(_content, { ...config, depth: depth + 1, _isOutputStart: isOutputStart });
							 | 
						|
								      const newValType = getType(newVal);
							 | 
						|
								      const isNewValSimple = isSimpleXML(newVal);
							 | 
						|
								
							 | 
						|
								      // Pre-tag output (indent and line breaks).
							 | 
						|
								      const preIndentStr = (indent && !isOutputStart) ? "\n" : "";
							 | 
						|
								      const preTag = `${preIndentStr}${indentStr}`;
							 | 
						|
								
							 | 
						|
								      // Special handling for comments, preserving preceding line breaks/indents.
							 | 
						|
								      if (_name === '_comment') {
							 | 
						|
								        outputStr += `${preTag}<!-- ${_content} -->`;
							 | 
						|
								        break;
							 | 
						|
								      }
							 | 
						|
								
							 | 
						|
								      // Tag output.
							 | 
						|
								      const valIsEmpty = newValType === "undefined" || newVal === "";
							 | 
						|
								      const shouldSelfClose =
							 | 
						|
								        typeof obj._selfCloseTag === BOOLEAN
							 | 
						|
								          ? valIsEmpty && obj._selfCloseTag
							 | 
						|
								          : valIsEmpty;
							 | 
						|
								      const selfCloseStr = shouldSelfClose ? "/" : "";
							 | 
						|
								      const attributesString = formatAttributes(obj._attrs, attributesFilter);
							 | 
						|
								      const tag = `<${_name}${attributesString}${selfCloseStr}>`;
							 | 
						|
								
							 | 
						|
								      // Post-tag output (closing tag, indent, line breaks).
							 | 
						|
								      const preTagCloseStr = indent && !isNewValSimple ? `\n${indentStr}` : "";
							 | 
						|
								      const postTag = !shouldSelfClose
							 | 
						|
								        ? `${newVal}${preTagCloseStr}</${_name}>`
							 | 
						|
								        : "";
							 | 
						|
								      outputStr += `${preTag}${tag}${postTag}`;
							 | 
						|
								      break;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    case "object": {
							 | 
						|
								      // Iterates over keyval pairs in an object, converting each item to a special-object.
							 | 
						|
								
							 | 
						|
								      const keys = Object.keys(obj);
							 | 
						|
								      const outputArr = keys.map((key, index) => {
							 | 
						|
								        const newConfig = {
							 | 
						|
								          ...config,
							 | 
						|
								          _isFirstItem: index === 0,
							 | 
						|
								          _isLastItem: index + 1 === keys.length,
							 | 
						|
								          _isOutputStart: isOutputStart
							 | 
						|
								        };
							 | 
						|
								
							 | 
						|
								        const outputObj = { _name: key };
							 | 
						|
								
							 | 
						|
								        if (getType(obj[key]) === "object") {
							 | 
						|
								          // Sub-object contains an object.
							 | 
						|
								
							 | 
						|
								          // Move private vars up as needed.  Needed to support certain types of objects
							 | 
						|
								          // E.g. { foo: { _attrs: { a: 1 } } } -> <foo a="1"/>
							 | 
						|
								          PRIVATE_VARS.forEach((privateVar) => {
							 | 
						|
								            const val = obj[key][privateVar];
							 | 
						|
								            if (typeof val !== "undefined") {
							 | 
						|
								              outputObj[privateVar] = val;
							 | 
						|
								              delete obj[key][privateVar];
							 | 
						|
								            }
							 | 
						|
								          });
							 | 
						|
								
							 | 
						|
								          const hasContent = typeof obj[key]._content !== "undefined";
							 | 
						|
								          if (hasContent) {
							 | 
						|
								            // _content has sibling keys, so pass as an array (edge case).
							 | 
						|
								            // E.g. { foo: 'bar', _content: { baz: 2 } } -> <foo>bar</foo><baz>2</baz>
							 | 
						|
								            if (Object.keys(obj[key]).length > 1) {
							 | 
						|
								              const newContentObj = Object.assign({}, obj[key]);
							 | 
						|
								              delete newContentObj._content;
							 | 
						|
								
							 | 
						|
								              outputObj._content = [
							 | 
						|
								                ...objToArray(newContentObj),
							 | 
						|
								                obj[key]._content,
							 | 
						|
								              ];
							 | 
						|
								            }
							 | 
						|
								          }
							 | 
						|
								        }
							 | 
						|
								
							 | 
						|
								        // Fallthrough: just pass the key as the content for the new special-object.
							 | 
						|
								        if (typeof outputObj._content === "undefined")
							 | 
						|
								          outputObj._content = obj[key];
							 | 
						|
								
							 | 
						|
								        const xml = toXML(outputObj, newConfig, key);
							 | 
						|
								
							 | 
						|
								        return xml;
							 | 
						|
								      }, config);
							 | 
						|
								
							 | 
						|
								      outputStr = outputArr.join('');
							 | 
						|
								      break;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    case "function": {
							 | 
						|
								      // Executes a user-defined function and returns output.
							 | 
						|
								
							 | 
						|
								      const fnResult = obj(config);
							 | 
						|
								
							 | 
						|
								      outputStr = toXML(fnResult, config);
							 | 
						|
								      break;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    case "array": {
							 | 
						|
								      // Iterates and converts each value in an array.
							 | 
						|
								      const outputArr = obj.map((singleVal, index) => {
							 | 
						|
								        const newConfig = {
							 | 
						|
								          ...config,
							 | 
						|
								          _isFirstItem: index === 0,
							 | 
						|
								          _isLastItem: index + 1 === obj.length,
							 | 
						|
								          _isOutputStart: isOutputStart
							 | 
						|
								        };
							 | 
						|
								        return toXML(singleVal, newConfig);
							 | 
						|
								      });
							 | 
						|
								
							 | 
						|
								      outputStr = outputArr.join('');
							 | 
						|
								
							 | 
						|
								      break;
							 | 
						|
								    }
							 | 
						|
								
							 | 
						|
								    // number, string, boolean, date, null, etc
							 | 
						|
								    default: {
							 | 
						|
								      outputStr = filterStr(obj, filter);
							 | 
						|
								      break;
							 | 
						|
								    }
							 | 
						|
								  }
							 | 
						|
								
							 | 
						|
								  return `${headerStr}${outputStr}`;
							 | 
						|
								};
							 | 
						|
								
							 | 
						|
								export default {
							 | 
						|
								  toXML,
							 | 
						|
								};
							 |