(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.fastxmlparser = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
  'use strict';
  
  const validator = require('./validator');
  const XMLParser = require('./xmlparser/XMLParser');
  const XMLBuilder = require('./xmlbuilder/json2xml');
  
  module.exports = {
    XMLParser: XMLParser,
    XMLValidator: validator,
    XMLBuilder: XMLBuilder
  }
  },{"./validator":3,"./xmlbuilder/json2xml":4,"./xmlparser/XMLParser":9}],2:[function(require,module,exports){
  'use strict';
  
  const nameStartChar = ':A-Za-z_\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD';
  const nameChar = nameStartChar + '\\-.\\d\\u00B7\\u0300-\\u036F\\u203F-\\u2040';
  const nameRegexp = '[' + nameStartChar + '][' + nameChar + ']*'
  const regexName = new RegExp('^' + nameRegexp + '$');
  
  const getAllMatches = function(string, regex) {
    const matches = [];
    let match = regex.exec(string);
    while (match) {
      const allmatches = [];
      allmatches.startIndex = regex.lastIndex - match[0].length;
      const len = match.length;
      for (let index = 0; index < len; index++) {
        allmatches.push(match[index]);
      }
      matches.push(allmatches);
      match = regex.exec(string);
    }
    return matches;
  };
  
  const isName = function(string) {
    const match = regexName.exec(string);
    return !(match === null || typeof match === 'undefined');
  };
  
  exports.isExist = function(v) {
    return typeof v !== 'undefined';
  };
  
  exports.isEmptyObject = function(obj) {
    return Object.keys(obj).length === 0;
  };
  
  /**
   * Copy all the properties of a into b.
   * @param {*} target
   * @param {*} a
   */
  exports.merge = function(target, a, arrayMode) {
    if (a) {
      const keys = Object.keys(a); // will return an array of own properties
      const len = keys.length; //don't make it inline
      for (let i = 0; i < len; i++) {
        if (arrayMode === 'strict') {
          target[keys[i]] = [ a[keys[i]] ];
        } else {
          target[keys[i]] = a[keys[i]];
        }
      }
    }
  };
  /* exports.merge =function (b,a){
    return Object.assign(b,a);
  } */
  
  exports.getValue = function(v) {
    if (exports.isExist(v)) {
      return v;
    } else {
      return '';
    }
  };
  
  // const fakeCall = function(a) {return a;};
  // const fakeCallNoReturn = function() {};
  
  exports.isName = isName;
  exports.getAllMatches = getAllMatches;
  exports.nameRegexp = nameRegexp;
  
  },{}],3:[function(require,module,exports){
  'use strict';
  
  const util = require('./util');
  
  const defaultOptions = {
    allowBooleanAttributes: false, //A tag can have attributes without any value
    unpairedTags: []
  };
  
  //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
  exports.validate = function (xmlData, options) {
    options = Object.assign({}, defaultOptions, options);
  
    //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
    //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
    //xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
    const tags = [];
    let tagFound = false;
  
    //indicates that the root tag has been closed (aka. depth 0 has been reached)
    let reachedRoot = false;
  
    if (xmlData[0] === '\ufeff') {
      // check for byte order mark (BOM)
      xmlData = xmlData.substr(1);
    }
    
    for (let i = 0; i < xmlData.length; i++) {
  
      if (xmlData[i] === '<' && xmlData[i+1] === '?') {
        i+=2;
        i = readPI(xmlData,i);
        if (i.err) return i;
      }else if (xmlData[i] === '<') {
        //starting of tag
        //read until you reach to '>' avoiding any '>' in attribute value
        let tagStartPos = i;
        i++;
        
        if (xmlData[i] === '!') {
          i = readCommentAndCDATA(xmlData, i);
          continue;
        } else {
          let closingTag = false;
          if (xmlData[i] === '/') {
            //closing tag
            closingTag = true;
            i++;
          }
          //read tagname
          let tagName = '';
          for (; i < xmlData.length &&
            xmlData[i] !== '>' &&
            xmlData[i] !== ' ' &&
            xmlData[i] !== '\t' &&
            xmlData[i] !== '\n' &&
            xmlData[i] !== '\r'; i++
          ) {
            tagName += xmlData[i];
          }
          tagName = tagName.trim();
          //console.log(tagName);
  
          if (tagName[tagName.length - 1] === '/') {
            //self closing tag without attributes
            tagName = tagName.substring(0, tagName.length - 1);
            //continue;
            i--;
          }
          if (!validateTagName(tagName)) {
            let msg;
            if (tagName.trim().length === 0) {
              msg = "Invalid space after '<'.";
            } else {
              msg = "Tag '"+tagName+"' is an invalid name.";
            }
            return getErrorObject('InvalidTag', msg, getLineNumberForPosition(xmlData, i));
          }
  
          const result = readAttributeStr(xmlData, i);
          if (result === false) {
            return getErrorObject('InvalidAttr', "Attributes for '"+tagName+"' have open quote.", getLineNumberForPosition(xmlData, i));
          }
          let attrStr = result.value;
          i = result.index;
  
          if (attrStr[attrStr.length - 1] === '/') {
            //self closing tag
            const attrStrStart = i - attrStr.length;
            attrStr = attrStr.substring(0, attrStr.length - 1);
            const isValid = validateAttributeString(attrStr, options);
            if (isValid === true) {
              tagFound = true;
              //continue; //text may presents after self closing tag
            } else {
              //the result from the nested function returns the position of the error within the attribute
              //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
              //this gives us the absolute index in the entire xml, which we can use to find the line at last
              return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, attrStrStart + isValid.err.line));
            }
          } else if (closingTag) {
            if (!result.tagClosed) {
              return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' doesn't have proper closing.", getLineNumberForPosition(xmlData, i));
            } else if (attrStr.trim().length > 0) {
              return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' can't have attributes or invalid starting.", getLineNumberForPosition(xmlData, tagStartPos));
            } else if (tags.length === 0) {
              return getErrorObject('InvalidTag', "Closing tag '"+tagName+"' has not been opened.", getLineNumberForPosition(xmlData, tagStartPos));
            } else {
              const otg = tags.pop();
              if (tagName !== otg.tagName) {
                let openPos = getLineNumberForPosition(xmlData, otg.tagStartPos);
                return getErrorObject('InvalidTag',
                  "Expected closing tag '"+otg.tagName+"' (opened in line "+openPos.line+", col "+openPos.col+") instead of closing tag '"+tagName+"'.",
                  getLineNumberForPosition(xmlData, tagStartPos));
              }
  
              //when there are no more tags, we reached the root level.
              if (tags.length == 0) {
                reachedRoot = true;
              }
            }
          } else {
            const isValid = validateAttributeString(attrStr, options);
            if (isValid !== true) {
              //the result from the nested function returns the position of the error within the attribute
              //in order to get the 'true' error line, we need to calculate the position where the attribute begins (i - attrStr.length) and then add the position within the attribute
              //this gives us the absolute index in the entire xml, which we can use to find the line at last
              return getErrorObject(isValid.err.code, isValid.err.msg, getLineNumberForPosition(xmlData, i - attrStr.length + isValid.err.line));
            }
  
            //if the root level has been reached before ...
            if (reachedRoot === true) {
              return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
            } else if(options.unpairedTags.indexOf(tagName) !== -1){
              //don't push into stack
            } else {
              tags.push({tagName, tagStartPos});
            }
            tagFound = true;
          }
  
          //skip tag text value
          //It may include comments and CDATA value
          for (i++; i < xmlData.length; i++) {
            if (xmlData[i] === '<') {
              if (xmlData[i + 1] === '!') {
                //comment or CADATA
                i++;
                i = readCommentAndCDATA(xmlData, i);
                continue;
              } else if (xmlData[i+1] === '?') {
                i = readPI(xmlData, ++i);
                if (i.err) return i;
              } else{
                break;
              }
            } else if (xmlData[i] === '&') {
              const afterAmp = validateAmpersand(xmlData, i);
              if (afterAmp == -1)
                return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
              i = afterAmp;
            }else{
              if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
                return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
              }
            }
          } //end of reading tag text value
          if (xmlData[i] === '<') {
            i--;
          }
        }
      } else {
        if ( isWhiteSpace(xmlData[i])) {
          continue;
        }
        return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
      }
    }
  
    if (!tagFound) {
      return getErrorObject('InvalidXml', 'Start tag expected.', 1);
    }else if (tags.length == 1) {
        return getErrorObject('InvalidTag', "Unclosed tag '"+tags[0].tagName+"'.", getLineNumberForPosition(xmlData, tags[0].tagStartPos));
    }else if (tags.length > 0) {
        return getErrorObject('InvalidXml', "Invalid '"+
            JSON.stringify(tags.map(t => t.tagName), null, 4).replace(/\r?\n/g, '')+
            "' found.", {line: 1, col: 1});
    }
  
    return true;
  };
  
  function isWhiteSpace(char){
    return char === ' ' || char === '\t' || char === '\n'  || char === '\r';
  }
  /**
   * Read Processing insstructions and skip
   * @param {*} xmlData
   * @param {*} i
   */
  function readPI(xmlData, i) {
    const start = i;
    for (; i < xmlData.length; i++) {
      if (xmlData[i] == '?' || xmlData[i] == ' ') {
        //tagname
        const tagname = xmlData.substr(start, i - start);
        if (i > 5 && tagname === 'xml') {
          return getErrorObject('InvalidXml', 'XML declaration allowed only at the start of the document.', getLineNumberForPosition(xmlData, i));
        } else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
          //check if valid attribut string
          i++;
          break;
        } else {
          continue;
        }
      }
    }
    return i;
  }
  
  function readCommentAndCDATA(xmlData, i) {
    if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
      //comment
      for (i += 3; i < xmlData.length; i++) {
        if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
          i += 2;
          break;
        }
      }
    } else if (
      xmlData.length > i + 8 &&
      xmlData[i + 1] === 'D' &&
      xmlData[i + 2] === 'O' &&
      xmlData[i + 3] === 'C' &&
      xmlData[i + 4] === 'T' &&
      xmlData[i + 5] === 'Y' &&
      xmlData[i + 6] === 'P' &&
      xmlData[i + 7] === 'E'
    ) {
      let angleBracketsCount = 1;
      for (i += 8; i < xmlData.length; i++) {
        if (xmlData[i] === '<') {
          angleBracketsCount++;
        } else if (xmlData[i] === '>') {
          angleBracketsCount--;
          if (angleBracketsCount === 0) {
            break;
          }
        }
      }
    } else if (
      xmlData.length > i + 9 &&
      xmlData[i + 1] === '[' &&
      xmlData[i + 2] === 'C' &&
      xmlData[i + 3] === 'D' &&
      xmlData[i + 4] === 'A' &&
      xmlData[i + 5] === 'T' &&
      xmlData[i + 6] === 'A' &&
      xmlData[i + 7] === '['
    ) {
      for (i += 8; i < xmlData.length; i++) {
        if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
          i += 2;
          break;
        }
      }
    }
  
    return i;
  }
  
  const doubleQuote = '"';
  const singleQuote = "'";
  
  /**
   * Keep reading xmlData until '<' is found outside the attribute value.
   * @param {string} xmlData
   * @param {number} i
   */
  function readAttributeStr(xmlData, i) {
    let attrStr = '';
    let startChar = '';
    let tagClosed = false;
    for (; i < xmlData.length; i++) {
      if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
        if (startChar === '') {
          startChar = xmlData[i];
        } else if (startChar !== xmlData[i]) {
          //if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
        } else {
          startChar = '';
        }
      } else if (xmlData[i] === '>') {
        if (startChar === '') {
          tagClosed = true;
          break;
        }
      }
      attrStr += xmlData[i];
    }
    if (startChar !== '') {
      return false;
    }
  
    return {
      value: attrStr,
      index: i,
      tagClosed: tagClosed
    };
  }
  
  /**
   * Select all the attributes whether valid or invalid.
   */
  const validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
  
  //attr, ="sd", a="amit's", a="sd"b="saf", ab  cd=""
  
  function validateAttributeString(attrStr, options) {
    //console.log("start:"+attrStr+":end");
  
    //if(attrStr.trim().length === 0) return true; //empty string
  
    const matches = util.getAllMatches(attrStr, validAttrStrRegxp);
    const attrNames = {};
  
    for (let i = 0; i < matches.length; i++) {
      if (matches[i][1].length === 0) {
        //nospace before attribute name: a="sd"b="saf"
        return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' has no space in starting.", getPositionFromMatch(matches[i]))
      } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
        return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' is without value.", getPositionFromMatch(matches[i]));
      } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
        //independent attribute: ab
        return getErrorObject('InvalidAttr', "boolean attribute '"+matches[i][2]+"' is not allowed.", getPositionFromMatch(matches[i]));
      }
      /* else if(matches[i][6] === undefined){//attribute without value: ab=
                      return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
                  } */
      const attrName = matches[i][2];
      if (!validateAttrName(attrName)) {
        return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is an invalid name.", getPositionFromMatch(matches[i]));
      }
      if (!attrNames.hasOwnProperty(attrName)) {
        //check for duplicate attribute.
        attrNames[attrName] = 1;
      } else {
        return getErrorObject('InvalidAttr', "Attribute '"+attrName+"' is repeated.", getPositionFromMatch(matches[i]));
      }
    }
  
    return true;
  }
  
  function validateNumberAmpersand(xmlData, i) {
    let re = /\d/;
    if (xmlData[i] === 'x') {
      i++;
      re = /[\da-fA-F]/;
    }
    for (; i < xmlData.length; i++) {
      if (xmlData[i] === ';')
        return i;
      if (!xmlData[i].match(re))
        break;
    }
    return -1;
  }
  
  function validateAmpersand(xmlData, i) {
    // https://www.w3.org/TR/xml/#dt-charref
    i++;
    if (xmlData[i] === ';')
      return -1;
    if (xmlData[i] === '#') {
      i++;
      return validateNumberAmpersand(xmlData, i);
    }
    let count = 0;
    for (; i < xmlData.length; i++, count++) {
      if (xmlData[i].match(/\w/) && count < 20)
        continue;
      if (xmlData[i] === ';')
        break;
      return -1;
    }
    return i;
  }
  
  function getErrorObject(code, message, lineNumber) {
    return {
      err: {
        code: code,
        msg: message,
        line: lineNumber.line || lineNumber,
        col: lineNumber.col,
      },
    };
  }
  
  function validateAttrName(attrName) {
    return util.isName(attrName);
  }
  
  // const startsWithXML = /^xml/i;
  
  function validateTagName(tagname) {
    return util.isName(tagname) /* && !tagname.match(startsWithXML) */;
  }
  
  //this function returns the line number for the character at the given index
  function getLineNumberForPosition(xmlData, index) {
    const lines = xmlData.substring(0, index).split(/\r?\n/);
    return {
      line: lines.length,
  
      // column number is last line's length + 1, because column numbering starts at 1:
      col: lines[lines.length - 1].length + 1
    };
  }
  
  //this function returns the position of the first character of match within attrStr
  function getPositionFromMatch(match) {
    return match.startIndex + match[1].length;
  }
  
  },{"./util":2}],4:[function(require,module,exports){
  'use strict';
  //parse Empty Node as self closing node
  const buildFromOrderedJs = require('./orderedJs2Xml');
  
  const defaultOptions = {
    attributeNamePrefix: '@_',
    attributesGroupName: false,
    textNodeName: '#text',
    ignoreAttributes: true,
    cdataPropName: false,
    format: false,
    indentBy: '  ',
    suppressEmptyNode: false,
    suppressUnpairedNode: true,
    suppressBooleanAttributes: true,
    tagValueProcessor: function(key, a) {
      return a;
    },
    attributeValueProcessor: function(attrName, a) {
      return a;
    },
    preserveOrder: false,
    commentPropName: false,
    unpairedTags: [],
    entities: [
      { regex: new RegExp("&", "g"), val: "&amp;" },//it must be on top
      { regex: new RegExp(">", "g"), val: "&gt;" },
      { regex: new RegExp("<", "g"), val: "&lt;" },
      { regex: new RegExp("\'", "g"), val: "&apos;" },
      { regex: new RegExp("\"", "g"), val: "&quot;" }
    ],
    processEntities: true,
    stopNodes: [],
    // transformTagName: false,
    // transformAttributeName: false,
    oneListGroup: false
  };
  
  function Builder(options) {
    this.options = Object.assign({}, defaultOptions, options);
    if (this.options.ignoreAttributes || this.options.attributesGroupName) {
      this.isAttribute = function(/*a*/) {
        return false;
      };
    } else {
      this.attrPrefixLen = this.options.attributeNamePrefix.length;
      this.isAttribute = isAttribute;
    }
  
    this.processTextOrObjNode = processTextOrObjNode
  
    if (this.options.format) {
      this.indentate = indentate;
      this.tagEndChar = '>\n';
      this.newLine = '\n';
    } else {
      this.indentate = function() {
        return '';
      };
      this.tagEndChar = '>';
      this.newLine = '';
    }
  }
  
  Builder.prototype.build = function(jObj) {
    if(this.options.preserveOrder){
      return buildFromOrderedJs(jObj, this.options);
    }else {
      if(Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1){
        jObj = {
          [this.options.arrayNodeName] : jObj
        }
      }
      return this.j2x(jObj, 0).val;
    }
  };
  
  Builder.prototype.j2x = function(jObj, level) {
    let attrStr = '';
    let val = '';
    for (let key in jObj) {
      if(!Object.prototype.hasOwnProperty.call(jObj, key)) continue;
      if (typeof jObj[key] === 'undefined') {
        // supress undefined node only if it is not an attribute
        if (this.isAttribute(key)) {
          val += '';
        }
      } else if (jObj[key] === null) {
        // null attribute should be ignored by the attribute list, but should not cause the tag closing
        if (this.isAttribute(key)) {
          val += '';
        } else if (key[0] === '?') {
          val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
        } else {
          val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
        }
        // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
      } else if (jObj[key] instanceof Date) {
        val += this.buildTextValNode(jObj[key], key, '', level);
      } else if (typeof jObj[key] !== 'object') {
        //premitive type
        const attr = this.isAttribute(key);
        if (attr) {
          attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
        }else {
          //tag value
          if (key === this.options.textNodeName) {
            let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
            val += this.replaceEntitiesValue(newval);
          } else {
            val += this.buildTextValNode(jObj[key], key, '', level);
          }
        }
      } else if (Array.isArray(jObj[key])) {
        //repeated nodes
        const arrLen = jObj[key].length;
        let listTagVal = "";
        for (let j = 0; j < arrLen; j++) {
          const item = jObj[key][j];
          if (typeof item === 'undefined') {
            // supress undefined node
          } else if (item === null) {
            if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
            else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
            // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
          } else if (typeof item === 'object') {
            if(this.options.oneListGroup ){
              listTagVal += this.j2x(item, level + 1).val;
            }else{
              listTagVal += this.processTextOrObjNode(item, key, level)
            }
          } else {
            listTagVal += this.buildTextValNode(item, key, '', level);
          }
        }
        if(this.options.oneListGroup){
          listTagVal = this.buildObjectNode(listTagVal, key, '', level);
        }
        val += listTagVal;
      } else {
        //nested node
        if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
          const Ks = Object.keys(jObj[key]);
          const L = Ks.length;
          for (let j = 0; j < L; j++) {
            attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
          }
        } else {
          val += this.processTextOrObjNode(jObj[key], key, level)
        }
      }
    }
    return {attrStr: attrStr, val: val};
  };
  
  Builder.prototype.buildAttrPairStr = function(attrName, val){
    val = this.options.attributeValueProcessor(attrName, '' + val);
    val = this.replaceEntitiesValue(val);
    if (this.options.suppressBooleanAttributes && val === "true") {
      return ' ' + attrName;
    } else return ' ' + attrName + '="' + val + '"';
  }
  
  function processTextOrObjNode (object, key, level) {
    const result = this.j2x(object, level + 1);
    if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
      return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
    } else {
      return this.buildObjectNode(result.val, key, result.attrStr, level);
    }
  }
  
  Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
    if(val === ""){
      if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
      else {
        return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
      }
    }else{
  
      let tagEndExp = '</' + key + this.tagEndChar;
      let piClosingChar = "";
      
      if(key[0] === "?") {
        piClosingChar = "?";
        tagEndExp = "";
      }
    
      // attrStr is an empty string in case the attribute came as undefined or null
      if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
        return ( this.indentate(level) + '<' +  key + attrStr + piClosingChar + '>' + val + tagEndExp );
      } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
        return this.indentate(level) + `<!--${val}-->` + this.newLine;
      }else {
        return (
          this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
          val +
          this.indentate(level) + tagEndExp    );
      }
    }
  }
  
  Builder.prototype.closeTag = function(key){
    let closeTag = "";
    if(this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
      if(!this.options.suppressUnpairedNode) closeTag = "/"
    }else if(this.options.suppressEmptyNode){ //empty
      closeTag = "/";
    }else{
      closeTag = `></${key}`
    }
    return closeTag;
  }
  
  function buildEmptyObjNode(val, key, attrStr, level) {
    if (val !== '') {
      return this.buildObjectNode(val, key, attrStr, level);
    } else {
      if(key[0] === "?") return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
      else {
        return  this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
        // return this.buildTagStr(level,key, attrStr);
      }
    }
  }
  
  Builder.prototype.buildTextValNode = function(val, key, attrStr, level) {
    if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
      return this.indentate(level) + `<![CDATA[${val}]]>` +  this.newLine;
    }else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
      return this.indentate(level) + `<!--${val}-->` +  this.newLine;
    }else if(key[0] === "?") {//PI tag
      return  this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar; 
    }else{
      let textValue = this.options.tagValueProcessor(key, val);
      textValue = this.replaceEntitiesValue(textValue);
    
      if( textValue === ''){
        return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
      }else{
        return this.indentate(level) + '<' + key + attrStr + '>' +
           textValue +
          '</' + key + this.tagEndChar;
      }
    }
  }
  
  Builder.prototype.replaceEntitiesValue = function(textValue){
    if(textValue && textValue.length > 0 && this.options.processEntities){
      for (let i=0; i<this.options.entities.length; i++) {
        const entity = this.options.entities[i];
        textValue = textValue.replace(entity.regex, entity.val);
      }
    }
    return textValue;
  }
  
  function indentate(level) {
    return this.options.indentBy.repeat(level);
  }
  
  function isAttribute(name /*, options*/) {
    if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) {
      return name.substr(this.attrPrefixLen);
    } else {
      return false;
    }
  }
  
  module.exports = Builder;
  
  },{"./orderedJs2Xml":5}],5:[function(require,module,exports){
  const EOL = "\n";
  
  /**
   * 
   * @param {array} jArray 
   * @param {any} options 
   * @returns 
   */
  function toXml(jArray, options) {
      let indentation = "";
      if (options.format && options.indentBy.length > 0) {
          indentation = EOL;
      }
      return arrToStr(jArray, options, "", indentation);
  }
  
  function arrToStr(arr, options, jPath, indentation) {
      let xmlStr = "";
      let isPreviousElementTag = false;
  
      for (let i = 0; i < arr.length; i++) {
          const tagObj = arr[i];
          const tagName = propName(tagObj);
          if(tagName === undefined) continue;
  
          let newJPath = "";
          if (jPath.length === 0) newJPath = tagName
          else newJPath = `${jPath}.${tagName}`;
  
          if (tagName === options.textNodeName) {
              let tagText = tagObj[tagName];
              if (!isStopNode(newJPath, options)) {
                  tagText = options.tagValueProcessor(tagName, tagText);
                  tagText = replaceEntitiesValue(tagText, options);
              }
              if (isPreviousElementTag) {
                  xmlStr += indentation;
              }
              xmlStr += tagText;
              isPreviousElementTag = false;
              continue;
          } else if (tagName === options.cdataPropName) {
              if (isPreviousElementTag) {
                  xmlStr += indentation;
              }
              xmlStr += `<![CDATA[${tagObj[tagName][0][options.textNodeName]}]]>`;
              isPreviousElementTag = false;
              continue;
          } else if (tagName === options.commentPropName) {
              xmlStr += indentation + `<!--${tagObj[tagName][0][options.textNodeName]}-->`;
              isPreviousElementTag = true;
              continue;
          } else if (tagName[0] === "?") {
              const attStr = attr_to_str(tagObj[":@"], options);
              const tempInd = tagName === "?xml" ? "" : indentation;
              let piTextNodeName = tagObj[tagName][0][options.textNodeName];
              piTextNodeName = piTextNodeName.length !== 0 ? " " + piTextNodeName : ""; //remove extra spacing
              xmlStr += tempInd + `<${tagName}${piTextNodeName}${attStr}?>`;
              isPreviousElementTag = true;
              continue;
          }
          let newIdentation = indentation;
          if (newIdentation !== "") {
              newIdentation += options.indentBy;
          }
          const attStr = attr_to_str(tagObj[":@"], options);
          const tagStart = indentation + `<${tagName}${attStr}`;
          const tagValue = arrToStr(tagObj[tagName], options, newJPath, newIdentation);
          if (options.unpairedTags.indexOf(tagName) !== -1) {
              if (options.suppressUnpairedNode) xmlStr += tagStart + ">";
              else xmlStr += tagStart + "/>";
          } else if ((!tagValue || tagValue.length === 0) && options.suppressEmptyNode) {
              xmlStr += tagStart + "/>";
          } else if (tagValue && tagValue.endsWith(">")) {
              xmlStr += tagStart + `>${tagValue}${indentation}</${tagName}>`;
          } else {
              xmlStr += tagStart + ">";
              if (tagValue && indentation !== "" && (tagValue.includes("/>") || tagValue.includes("</"))) {
                  xmlStr += indentation + options.indentBy + tagValue + indentation;
              } else {
                  xmlStr += tagValue;
              }
              xmlStr += `</${tagName}>`;
          }
          isPreviousElementTag = true;
      }
  
      return xmlStr;
  }
  
  function propName(obj) {
      const keys = Object.keys(obj);
      for (let i = 0; i < keys.length; i++) {
          const key = keys[i];
          if(!obj.hasOwnProperty(key)) continue;
          if (key !== ":@") return key;
      }
  }
  
  function attr_to_str(attrMap, options) {
      let attrStr = "";
      if (attrMap && !options.ignoreAttributes) {
          for (let attr in attrMap) {
              if(!attrMap.hasOwnProperty(attr)) continue;
              let attrVal = options.attributeValueProcessor(attr, attrMap[attr]);
              attrVal = replaceEntitiesValue(attrVal, options);
              if (attrVal === true && options.suppressBooleanAttributes) {
                  attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}`;
              } else {
                  attrStr += ` ${attr.substr(options.attributeNamePrefix.length)}="${attrVal}"`;
              }
          }
      }
      return attrStr;
  }
  
  function isStopNode(jPath, options) {
      jPath = jPath.substr(0, jPath.length - options.textNodeName.length - 1);
      let tagName = jPath.substr(jPath.lastIndexOf(".") + 1);
      for (let index in options.stopNodes) {
          if (options.stopNodes[index] === jPath || options.stopNodes[index] === "*." + tagName) return true;
      }
      return false;
  }
  
  function replaceEntitiesValue(textValue, options) {
      if (textValue && textValue.length > 0 && options.processEntities) {
          for (let i = 0; i < options.entities.length; i++) {
              const entity = options.entities[i];
              textValue = textValue.replace(entity.regex, entity.val);
          }
      }
      return textValue;
  }
  module.exports = toXml;
  
  },{}],6:[function(require,module,exports){
  const util = require('../util');
  
  //TODO: handle comments
  function readDocType(xmlData, i){
      
      const entities = {};
      if( xmlData[i + 3] === 'O' &&
           xmlData[i + 4] === 'C' &&
           xmlData[i + 5] === 'T' &&
           xmlData[i + 6] === 'Y' &&
           xmlData[i + 7] === 'P' &&
           xmlData[i + 8] === 'E')
      {    
          i = i+9;
          let angleBracketsCount = 1;
          let hasBody = false, comment = false;
          let exp = "";
          for(;i<xmlData.length;i++){
              if (xmlData[i] === '<' && !comment) { //Determine the tag type
                  if( hasBody && isEntity(xmlData, i)){
                      i += 7; 
                      [entityName, val,i] = readEntityExp(xmlData,i+1);
                      if(val.indexOf("&") === -1) //Parameter entities are not supported
                          entities[ validateEntityName(entityName) ] = {
                              regx : RegExp( `&${entityName};`,"g"),
                              val: val
                          };
                  }
                  else if( hasBody && isElement(xmlData, i))  i += 8;//Not supported
                  else if( hasBody && isAttlist(xmlData, i))  i += 8;//Not supported
                  else if( hasBody && isNotation(xmlData, i)) i += 9;//Not supported
                  else if( isComment)                         comment = true;
                  else                                        throw new Error("Invalid DOCTYPE");
  
                  angleBracketsCount++;
                  exp = "";
              } else if (xmlData[i] === '>') { //Read tag content
                  if(comment){
                      if( xmlData[i - 1] === "-" && xmlData[i - 2] === "-"){
                          comment = false;
                          angleBracketsCount--;
                      }
                  }else{
                      angleBracketsCount--;
                  }
                  if (angleBracketsCount === 0) {
                    break;
                  }
              }else if( xmlData[i] === '['){
                  hasBody = true;
              }else{
                  exp += xmlData[i];
              }
          }
          if(angleBracketsCount !== 0){
              throw new Error(`Unclosed DOCTYPE`);
          }
      }else{
          throw new Error(`Invalid Tag instead of DOCTYPE`);
      }
      return {entities, i};
  }
  
  function readEntityExp(xmlData,i){
      //External entities are not supported
      //    <!ENTITY ext SYSTEM "http://normal-website.com" >
  
      //Parameter entities are not supported
      //    <!ENTITY entityname "&anotherElement;">
  
      //Internal entities are supported
      //    <!ENTITY entityname "replacement text">
      
      //read EntityName
      let entityName = "";
      for (; i < xmlData.length && (xmlData[i] !== "'" && xmlData[i] !== '"' ); i++) {
          // if(xmlData[i] === " ") continue;
          // else 
          entityName += xmlData[i];
      }
      entityName = entityName.trim();
      if(entityName.indexOf(" ") !== -1) throw new Error("External entites are not supported");
  
      //read Entity Value
      const startChar = xmlData[i++];
      let val = ""
      for (; i < xmlData.length && xmlData[i] !== startChar ; i++) {
          val += xmlData[i];
      }
      return [entityName, val, i];
  }
  
  function isComment(xmlData, i){
      if(xmlData[i+1] === '!' &&
      xmlData[i+2] === '-' &&
      xmlData[i+3] === '-') return true
      return false
  }
  function isEntity(xmlData, i){
      if(xmlData[i+1] === '!' &&
      xmlData[i+2] === 'E' &&
      xmlData[i+3] === 'N' &&
      xmlData[i+4] === 'T' &&
      xmlData[i+5] === 'I' &&
      xmlData[i+6] === 'T' &&
      xmlData[i+7] === 'Y') return true
      return false
  }
  function isElement(xmlData, i){
      if(xmlData[i+1] === '!' &&
      xmlData[i+2] === 'E' &&
      xmlData[i+3] === 'L' &&
      xmlData[i+4] === 'E' &&
      xmlData[i+5] === 'M' &&
      xmlData[i+6] === 'E' &&
      xmlData[i+7] === 'N' &&
      xmlData[i+8] === 'T') return true
      return false
  }
  
  function isAttlist(xmlData, i){
      if(xmlData[i+1] === '!' &&
      xmlData[i+2] === 'A' &&
      xmlData[i+3] === 'T' &&
      xmlData[i+4] === 'T' &&
      xmlData[i+5] === 'L' &&
      xmlData[i+6] === 'I' &&
      xmlData[i+7] === 'S' &&
      xmlData[i+8] === 'T') return true
      return false
  }
  function isNotation(xmlData, i){
      if(xmlData[i+1] === '!' &&
      xmlData[i+2] === 'N' &&
      xmlData[i+3] === 'O' &&
      xmlData[i+4] === 'T' &&
      xmlData[i+5] === 'A' &&
      xmlData[i+6] === 'T' &&
      xmlData[i+7] === 'I' &&
      xmlData[i+8] === 'O' &&
      xmlData[i+9] === 'N') return true
      return false
  }
  
  function validateEntityName(name){
      if (util.isName(name))
    return name;
      else
          throw new Error(`Invalid entity name ${name}`);
  }
  
  module.exports = readDocType;
  
  },{"../util":2}],7:[function(require,module,exports){
  
  const defaultOptions = {
      preserveOrder: false,
      attributeNamePrefix: '@_',
      attributesGroupName: false,
      textNodeName: '#text',
      ignoreAttributes: true,
      removeNSPrefix: false, // remove NS from tag name or attribute name if true
      allowBooleanAttributes: false, //a tag can have attributes without any value
      //ignoreRootElement : false,
      parseTagValue: true,
      parseAttributeValue: false,
      trimValues: true, //Trim string values of tag and attributes
      cdataPropName: false,
      numberParseOptions: {
        hex: true,
        leadingZeros: true,
        eNotation: true
      },
      tagValueProcessor: function(tagName, val) {
        return val;
      },
      attributeValueProcessor: function(attrName, val) {
        return val;
      },
      stopNodes: [], //nested tags will not be parsed even for errors
      alwaysCreateTextNode: false,
      isArray: () => false,
      commentPropName: false,
      unpairedTags: [],
      processEntities: true,
      htmlEntities: false,
      ignoreDeclaration: false,
      ignorePiTags: false,
      transformTagName: false,
      transformAttributeName: false,
      updateTag: function(tagName, jPath, attrs){
        return tagName
      },
      // skipEmptyListItem: false
  };
     
  const buildOptions = function(options) {
      return Object.assign({}, defaultOptions, options);
  };
  
  exports.buildOptions = buildOptions;
  exports.defaultOptions = defaultOptions;
  },{}],8:[function(require,module,exports){
  'use strict';
  ///@ts-check
  
  const util = require('../util');
  const xmlNode = require('./xmlNode');
  const readDocType = require("./DocTypeReader");
  const toNumber = require("strnum");
  
  // const regx =
  //   '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
  //   .replace(/NAME/g, util.nameRegexp);
  
  //const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
  //const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
  
  class OrderedObjParser{
    constructor(options){
      this.options = options;
      this.currentNode = null;
      this.tagsNodeStack = [];
      this.docTypeEntities = {};
      this.lastEntities = {
        "apos" : { regex: /&(apos|#39|#x27);/g, val : "'"},
        "gt" : { regex: /&(gt|#62|#x3E);/g, val : ">"},
        "lt" : { regex: /&(lt|#60|#x3C);/g, val : "<"},
        "quot" : { regex: /&(quot|#34|#x22);/g, val : "\""},
      };
      this.ampEntity = { regex: /&(amp|#38|#x26);/g, val : "&"};
      this.htmlEntities = {
        "space": { regex: /&(nbsp|#160);/g, val: " " },
        // "lt" : { regex: /&(lt|#60);/g, val: "<" },
        // "gt" : { regex: /&(gt|#62);/g, val: ">" },
        // "amp" : { regex: /&(amp|#38);/g, val: "&" },
        // "quot" : { regex: /&(quot|#34);/g, val: "\"" },
        // "apos" : { regex: /&(apos|#39);/g, val: "'" },
        "cent" : { regex: /&(cent|#162);/g, val: "¢" },
        "pound" : { regex: /&(pound|#163);/g, val: "£" },
        "yen" : { regex: /&(yen|#165);/g, val: "¥" },
        "euro" : { regex: /&(euro|#8364);/g, val: "€" },
        "copyright" : { regex: /&(copy|#169);/g, val: "©" },
        "reg" : { regex: /&(reg|#174);/g, val: "®" },
        "inr" : { regex: /&(inr|#8377);/g, val: "₹" },
        "num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
        "num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
      };
      this.addExternalEntities = addExternalEntities;
      this.parseXml = parseXml;
      this.parseTextData = parseTextData;
      this.resolveNameSpace = resolveNameSpace;
      this.buildAttributesMap = buildAttributesMap;
      this.isItStopNode = isItStopNode;
      this.replaceEntitiesValue = replaceEntitiesValue;
      this.readStopNodeData = readStopNodeData;
      this.saveTextToParentTag = saveTextToParentTag;
      this.addChild = addChild;
    }
  
  }
  
  function addExternalEntities(externalEntities){
    const entKeys = Object.keys(externalEntities);
    for (let i = 0; i < entKeys.length; i++) {
      const ent = entKeys[i];
      this.lastEntities[ent] = {
         regex: new RegExp("&"+ent+";","g"),
         val : externalEntities[ent]
      }
    }
  }
  
  /**
   * @param {string} val
   * @param {string} tagName
   * @param {string} jPath
   * @param {boolean} dontTrim
   * @param {boolean} hasAttributes
   * @param {boolean} isLeafNode
   * @param {boolean} escapeEntities
   */
  function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
    if (val !== undefined) {
      if (this.options.trimValues && !dontTrim) {
        val = val.trim();
      }
      if(val.length > 0){
        if(!escapeEntities) val = this.replaceEntitiesValue(val);
        
        const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
        if(newval === null || newval === undefined){
          //don't parse
          return val;
        }else if(typeof newval !== typeof val || newval !== val){
          //overwrite
          return newval;
        }else if(this.options.trimValues){
          return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
        }else{
          const trimmedVal = val.trim();
          if(trimmedVal === val){
            return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
          }else{
            return val;
          }
        }
      }
    }
  }
  
  function resolveNameSpace(tagname) {
    if (this.options.removeNSPrefix) {
      const tags = tagname.split(':');
      const prefix = tagname.charAt(0) === '/' ? '/' : '';
      if (tags[0] === 'xmlns') {
        return '';
      }
      if (tags.length === 2) {
        tagname = prefix + tags[1];
      }
    }
    return tagname;
  }
  
  //TODO: change regex to capture NS
  //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
  const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
  
  function buildAttributesMap(attrStr, jPath, tagName) {
    if (!this.options.ignoreAttributes && typeof attrStr === 'string') {
      // attrStr = attrStr.replace(/\r?\n/g, ' ');
      //attrStr = attrStr || attrStr.trim();
  
      const matches = util.getAllMatches(attrStr, attrsRegx);
      const len = matches.length; //don't make it inline
      const attrs = {};
      for (let i = 0; i < len; i++) {
        const attrName = this.resolveNameSpace(matches[i][1]);
        let oldVal = matches[i][4];
        let aName = this.options.attributeNamePrefix + attrName;
        if (attrName.length) {
          if (this.options.transformAttributeName) {
            aName = this.options.transformAttributeName(aName);
          }
          if(aName === "__proto__") aName  = "#__proto__";
          if (oldVal !== undefined) {
            if (this.options.trimValues) {
              oldVal = oldVal.trim();
            }
            oldVal = this.replaceEntitiesValue(oldVal);
            const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
            if(newVal === null || newVal === undefined){
              //don't parse
              attrs[aName] = oldVal;
            }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
              //overwrite
              attrs[aName] = newVal;
            }else{
              //parse
              attrs[aName] = parseValue(
                oldVal,
                this.options.parseAttributeValue,
                this.options.numberParseOptions
              );
            }
          } else if (this.options.allowBooleanAttributes) {
            attrs[aName] = true;
          }
        }
      }
      if (!Object.keys(attrs).length) {
        return;
      }
      if (this.options.attributesGroupName) {
        const attrCollection = {};
        attrCollection[this.options.attributesGroupName] = attrs;
        return attrCollection;
      }
      return attrs
    }
  }
  
  const parseXml = function(xmlData) {
    xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
    const xmlObj = new xmlNode('!xml');
    let currentNode = xmlObj;
    let textData = "";
    let jPath = "";
    for(let i=0; i< xmlData.length; i++){//for each char in XML data
      const ch = xmlData[i];
      if(ch === '<'){
        // const nextIndex = i+1;
        // const _2ndChar = xmlData[nextIndex];
        if( xmlData[i+1] === '/') {//Closing Tag
          const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
          let tagName = xmlData.substring(i+2,closeIndex).trim();
  
          if(this.options.removeNSPrefix){
            const colonIndex = tagName.indexOf(":");
            if(colonIndex !== -1){
              tagName = tagName.substr(colonIndex+1);
            }
          }
  
          if(this.options.transformTagName) {
            tagName = this.options.transformTagName(tagName);
          }
  
          if(currentNode){
            textData = this.saveTextToParentTag(textData, currentNode, jPath);
          }
  
          //check if last tag of nested tag was unpaired tag
          const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
          if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1 ){
            throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
          }
          let propIndex = 0
          if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){
            propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1)
            this.tagsNodeStack.pop();
          }else{
            propIndex = jPath.lastIndexOf(".");
          }
          jPath = jPath.substring(0, propIndex);
  
          currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
          textData = "";
          i = closeIndex;
        } else if( xmlData[i+1] === '?') {
  
          let tagData = readTagExp(xmlData,i, false, "?>");
          if(!tagData) throw new Error("Pi Tag is not closed.");
  
          textData = this.saveTextToParentTag(textData, currentNode, jPath);
          if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){
  
          }else{
    
            const childNode = new xmlNode(tagData.tagName);
            childNode.add(this.options.textNodeName, "");
            
            if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
              childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
            }
            this.addChild(currentNode, childNode, jPath)
  
          }
  
  
          i = tagData.closeIndex + 1;
        } else if(xmlData.substr(i + 1, 3) === '!--') {
          const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
          if(this.options.commentPropName){
            const comment = xmlData.substring(i + 4, endIndex - 2);
  
            textData = this.saveTextToParentTag(textData, currentNode, jPath);
  
            currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
          }
          i = endIndex;
        } else if( xmlData.substr(i + 1, 2) === '!D') {
          const result = readDocType(xmlData, i);
          this.docTypeEntities = result.entities;
          i = result.i;
        }else if(xmlData.substr(i + 1, 2) === '![') {
          const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
          const tagExp = xmlData.substring(i + 9,closeIndex);
  
          textData = this.saveTextToParentTag(textData, currentNode, jPath);
  
          let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
          if(val == undefined) val = "";
  
          //cdata should be set even if it is 0 length string
          if(this.options.cdataPropName){
            currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
          }else{
            currentNode.add(this.options.textNodeName, val);
          }
          
          i = closeIndex + 2;
        }else {//Opening tag
          let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
          let tagName= result.tagName;
          const rawTagName = result.rawTagName;
          let tagExp = result.tagExp;
          let attrExpPresent = result.attrExpPresent;
          let closeIndex = result.closeIndex;
  
          if (this.options.transformTagName) {
            tagName = this.options.transformTagName(tagName);
          }
          
          //save text as child node
          if (currentNode && textData) {
            if(currentNode.tagname !== '!xml'){
              //when nested tag is found
              textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
            }
          }
  
          //check if last tag was unpaired tag
          const lastTag = currentNode;
          if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
            currentNode = this.tagsNodeStack.pop();
            jPath = jPath.substring(0, jPath.lastIndexOf("."));
          }
          if(tagName !== xmlObj.tagname){
            jPath += jPath ? "." + tagName : tagName;
          }
          if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) {
            let tagContent = "";
            //self-closing tag
            if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
              if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
                tagName = tagName.substr(0, tagName.length - 1);
                jPath = jPath.substr(0, jPath.length - 1);
                tagExp = tagName;
              }else{
                tagExp = tagExp.substr(0, tagExp.length - 1);
              }
              i = result.closeIndex;
            }
            //unpaired tag
            else if(this.options.unpairedTags.indexOf(tagName) !== -1){
              
              i = result.closeIndex;
            }
            //normal tag
            else{
              //read until closing tag is found
              const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
              if(!result) throw new Error(`Unexpected end of ${rawTagName}`);
              i = result.i;
              tagContent = result.tagContent;
            }
  
            const childNode = new xmlNode(tagName);
            if(tagName !== tagExp && attrExpPresent){
              childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
            }
            if(tagContent) {
              tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
            }
            
            jPath = jPath.substr(0, jPath.lastIndexOf("."));
            childNode.add(this.options.textNodeName, tagContent);
            
            this.addChild(currentNode, childNode, jPath)
          }else{
    //selfClosing tag
            if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
              if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
                tagName = tagName.substr(0, tagName.length - 1);
                jPath = jPath.substr(0, jPath.length - 1);
                tagExp = tagName;
              }else{
                tagExp = tagExp.substr(0, tagExp.length - 1);
              }
              
              if(this.options.transformTagName) {
                tagName = this.options.transformTagName(tagName);
              }
  
              const childNode = new xmlNode(tagName);
              if(tagName !== tagExp && attrExpPresent){
                childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
              }
              this.addChild(currentNode, childNode, jPath)
              jPath = jPath.substr(0, jPath.lastIndexOf("."));
            }
      //opening tag
            else{
              const childNode = new xmlNode( tagName);
              this.tagsNodeStack.push(currentNode);
              
              if(tagName !== tagExp && attrExpPresent){
                childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
              }
              this.addChild(currentNode, childNode, jPath)
              currentNode = childNode;
            }
            textData = "";
            i = closeIndex;
          }
        }
      }else{
        textData += xmlData[i];
      }
    }
    return xmlObj.child;
  }
  
  function addChild(currentNode, childNode, jPath){
    const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
    if(result === false){
    }else if(typeof result === "string"){
      childNode.tagname = result
      currentNode.addChild(childNode);
    }else{
      currentNode.addChild(childNode);
    }
  }
  
  const replaceEntitiesValue = function(val){
  
    if(this.options.processEntities){
      for(let entityName in this.docTypeEntities){
        const entity = this.docTypeEntities[entityName];
        val = val.replace( entity.regx, entity.val);
      }
      for(let entityName in this.lastEntities){
        const entity = this.lastEntities[entityName];
        val = val.replace( entity.regex, entity.val);
      }
      if(this.options.htmlEntities){
        for(let entityName in this.htmlEntities){
          const entity = this.htmlEntities[entityName];
          val = val.replace( entity.regex, entity.val);
        }
      }
      val = val.replace( this.ampEntity.regex, this.ampEntity.val);
    }
    return val;
  }
  function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
    if (textData) { //store previously collected data as textNode
      if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0
      
      textData = this.parseTextData(textData,
        currentNode.tagname,
        jPath,
        false,
        currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
        isLeafNode);
  
      if (textData !== undefined && textData !== "")
        currentNode.add(this.options.textNodeName, textData);
      textData = "";
    }
    return textData;
  }
  
  //TODO: use jPath to simplify the logic
  /**
   * 
   * @param {string[]} stopNodes 
   * @param {string} jPath
   * @param {string} currentTagName 
   */
  function isItStopNode(stopNodes, jPath, currentTagName){
    const allNodesExp = "*." + currentTagName;
    for (const stopNodePath in stopNodes) {
      const stopNodeExp = stopNodes[stopNodePath];
      if( allNodesExp === stopNodeExp || jPath === stopNodeExp  ) return true;
    }
    return false;
  }
  
  /**
   * Returns the tag Expression and where it is ending handling single-double quotes situation
   * @param {string} xmlData 
   * @param {number} i starting index
   * @returns 
   */
  function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
    let attrBoundary;
    let tagExp = "";
    for (let index = i; index < xmlData.length; index++) {
      let ch = xmlData[index];
      if (attrBoundary) {
          if (ch === attrBoundary) attrBoundary = "";//reset
      } else if (ch === '"' || ch === "'") {
          attrBoundary = ch;
      } else if (ch === closingChar[0]) {
        if(closingChar[1]){
          if(xmlData[index + 1] === closingChar[1]){
            return {
              data: tagExp,
              index: index
            }
          }
        }else{
          return {
            data: tagExp,
            index: index
          }
        }
      } else if (ch === '\t') {
        ch = " "
      }
      tagExp += ch;
    }
  }
  
  function findClosingIndex(xmlData, str, i, errMsg){
    const closingIndex = xmlData.indexOf(str, i);
    if(closingIndex === -1){
      throw new Error(errMsg)
    }else{
      return closingIndex + str.length - 1;
    }
  }
  
  function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
    const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
    if(!result) return;
    let tagExp = result.data;
    const closeIndex = result.index;
    const separatorIndex = tagExp.search(/\s/);
    let tagName = tagExp;
    let attrExpPresent = true;
    if(separatorIndex !== -1){//separate tag name and attributes expression
      tagName = tagExp.substring(0, separatorIndex);
      tagExp = tagExp.substring(separatorIndex + 1).trimStart();
    }
  
    const rawTagName = tagName;
    if(removeNSPrefix){
      const colonIndex = tagName.indexOf(":");
      if(colonIndex !== -1){
        tagName = tagName.substr(colonIndex+1);
        attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
      }
    }
  
    return {
      tagName: tagName,
      tagExp: tagExp,
      closeIndex: closeIndex,
      attrExpPresent: attrExpPresent,
      rawTagName: rawTagName,
    }
  }
  /**
   * find paired tag for a stop node
   * @param {string} xmlData 
   * @param {string} tagName 
   * @param {number} i 
   */
  function readStopNodeData(xmlData, tagName, i){
    const startIndex = i;
    // Starting at 1 since we already have an open tag
    let openTagCount = 1;
  
    for (; i < xmlData.length; i++) {
      if( xmlData[i] === "<"){ 
        if (xmlData[i+1] === "/") {//close tag
            const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
            let closeTagName = xmlData.substring(i+2,closeIndex).trim();
            if(closeTagName === tagName){
              openTagCount--;
              if (openTagCount === 0) {
                return {
                  tagContent: xmlData.substring(startIndex, i),
                  i : closeIndex
                }
              }
            }
            i=closeIndex;
          } else if(xmlData[i+1] === '?') { 
            const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.")
            i=closeIndex;
          } else if(xmlData.substr(i + 1, 3) === '!--') { 
            const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.")
            i=closeIndex;
          } else if(xmlData.substr(i + 1, 2) === '![') { 
            const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
            i=closeIndex;
          } else {
            const tagData = readTagExp(xmlData, i, '>')
  
            if (tagData) {
              const openTagName = tagData && tagData.tagName;
              if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
                openTagCount++;
              }
              i=tagData.closeIndex;
            }
          }
        }
    }//end for loop
  }
  
  function parseValue(val, shouldParse, options) {
    if (shouldParse && typeof val === 'string') {
      //console.log(options)
      const newval = val.trim();
      if(newval === 'true' ) return true;
      else if(newval === 'false' ) return false;
      else return toNumber(val, options);
    } else {
      if (util.isExist(val)) {
        return val;
      } else {
        return '';
      }
    }
  }
  
  
  module.exports = OrderedObjParser;
  
  },{"../util":2,"./DocTypeReader":6,"./xmlNode":11,"strnum":12}],9:[function(require,module,exports){
  const { buildOptions} = require("./OptionsBuilder");
  const OrderedObjParser = require("./OrderedObjParser");
  const { prettify} = require("./node2json");
  const validator = require('../validator');
  
  class XMLParser{
      
      constructor(options){
          this.externalEntities = {};
          this.options = buildOptions(options);
          
      }
      /**
       * Parse XML dats to JS object 
       * @param {string|Buffer} xmlData 
       * @param {boolean|Object} validationOption 
       */
      parse(xmlData,validationOption){
          if(typeof xmlData === "string"){
          }else if( xmlData.toString){
              xmlData = xmlData.toString();
          }else{
              throw new Error("XML data is accepted in String or Bytes[] form.")
          }
          if( validationOption){
              if(validationOption === true) validationOption = {}; //validate with default options
              
              const result = validator.validate(xmlData, validationOption);
              if (result !== true) {
                throw Error( `${result.err.msg}:${result.err.line}:${result.err.col}` )
              }
            }
          const orderedObjParser = new OrderedObjParser(this.options);
          orderedObjParser.addExternalEntities(this.externalEntities);
          const orderedResult = orderedObjParser.parseXml(xmlData);
          if(this.options.preserveOrder || orderedResult === undefined) return orderedResult;
          else return prettify(orderedResult, this.options);
      }
  
      /**
       * Add Entity which is not by default supported by this library
       * @param {string} key 
       * @param {string} value 
       */
      addEntity(key, value){
          if(value.indexOf("&") !== -1){
              throw new Error("Entity value can't have '&'")
          }else if(key.indexOf("&") !== -1 || key.indexOf(";") !== -1){
              throw new Error("An entity must be set without '&' and ';'. Eg. use '#xD' for '&#xD;'")
          }else if(value === "&"){
              throw new Error("An entity with value '&' is not permitted");
          }else{
              this.externalEntities[key] = value;
          }
      }
  }
  
  module.exports = XMLParser;
  },{"../validator":3,"./OptionsBuilder":7,"./OrderedObjParser":8,"./node2json":10}],10:[function(require,module,exports){
  'use strict';
  
  /**
   * 
   * @param {array} node 
   * @param {any} options 
   * @returns 
   */
  function prettify(node, options){
    return compress( node, options);
  }
  
  /**
   * 
   * @param {array} arr 
   * @param {object} options 
   * @param {string} jPath 
   * @returns object
   */
  function compress(arr, options, jPath){
    let text;
    const compressedObj = {};
    for (let i = 0; i < arr.length; i++) {
      const tagObj = arr[i];
      const property = propName(tagObj);
      let newJpath = "";
      if(jPath === undefined) newJpath = property;
      else newJpath = jPath + "." + property;
  
      if(property === options.textNodeName){
        if(text === undefined) text = tagObj[property];
        else text += "" + tagObj[property];
      }else if(property === undefined){
        continue;
      }else if(tagObj[property]){
        
        let val = compress(tagObj[property], options, newJpath);
        const isLeaf = isLeafTag(val, options);
  
        if(tagObj[":@"]){
          assignAttributes( val, tagObj[":@"], newJpath, options);
        }else if(Object.keys(val).length === 1 && val[options.textNodeName] !== undefined && !options.alwaysCreateTextNode){
          val = val[options.textNodeName];
        }else if(Object.keys(val).length === 0){
          if(options.alwaysCreateTextNode) val[options.textNodeName] = "";
          else val = "";
        }
  
        if(compressedObj[property] !== undefined && compressedObj.hasOwnProperty(property)) {
          if(!Array.isArray(compressedObj[property])) {
              compressedObj[property] = [ compressedObj[property] ];
          }
          compressedObj[property].push(val);
        }else{
          //TODO: if a node is not an array, then check if it should be an array
          //also determine if it is a leaf node
          if (options.isArray(property, newJpath, isLeaf )) {
            compressedObj[property] = [val];
          }else{
            compressedObj[property] = val;
          }
        }
      }
      
    }
    // if(text && text.length > 0) compressedObj[options.textNodeName] = text;
    if(typeof text === "string"){
      if(text.length > 0) compressedObj[options.textNodeName] = text;
    }else if(text !== undefined) compressedObj[options.textNodeName] = text;
    return compressedObj;
  }
  
  function propName(obj){
    const keys = Object.keys(obj);
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      if(key !== ":@") return key;
    }
  }
  
  function assignAttributes(obj, attrMap, jpath, options){
    if (attrMap) {
      const keys = Object.keys(attrMap);
      const len = keys.length; //don't make it inline
      for (let i = 0; i < len; i++) {
        const atrrName = keys[i];
        if (options.isArray(atrrName, jpath + "." + atrrName, true, true)) {
          obj[atrrName] = [ attrMap[atrrName] ];
        } else {
          obj[atrrName] = attrMap[atrrName];
        }
      }
    }
  }
  
  function isLeafTag(obj, options){
    const { textNodeName } = options;
    const propCount = Object.keys(obj).length;
    
    if (propCount === 0) {
      return true;
    }
  
    if (
      propCount === 1 &&
      (obj[textNodeName] || typeof obj[textNodeName] === "boolean" || obj[textNodeName] === 0)
    ) {
      return true;
    }
  
    return false;
  }
  exports.prettify = prettify;
  
  },{}],11:[function(require,module,exports){
  'use strict';
  
  class XmlNode{
    constructor(tagname) {
      this.tagname = tagname;
      this.child = []; //nested tags, text, cdata, comments in order
      this[":@"] = {}; //attributes map
    }
    add(key,val){
      // this.child.push( {name : key, val: val, isCdata: isCdata });
      if(key === "__proto__") key = "#__proto__";
      this.child.push( {[key]: val });
    }
    addChild(node) {
      if(node.tagname === "__proto__") node.tagname = "#__proto__";
      if(node[":@"] && Object.keys(node[":@"]).length > 0){
        this.child.push( { [node.tagname]: node.child, [":@"]: node[":@"] });
      }else{
        this.child.push( { [node.tagname]: node.child });
      }
    };
  };
  
  
  module.exports = XmlNode;
  },{}],12:[function(require,module,exports){
  const hexRegex = /^[-+]?0x[a-fA-F0-9]+$/;
  const numRegex = /^([\-\+])?(0*)(\.[0-9]+([eE]\-?[0-9]+)?|[0-9]+(\.[0-9]+([eE]\-?[0-9]+)?)?)$/;
  // const octRegex = /0x[a-z0-9]+/;
  // const binRegex = /0x[a-z0-9]+/;
  
  
  //polyfill
  if (!Number.parseInt && window.parseInt) {
      Number.parseInt = window.parseInt;
  }
  if (!Number.parseFloat && window.parseFloat) {
      Number.parseFloat = window.parseFloat;
  }
  
    
  const consider = {
      hex :  true,
      leadingZeros: true,
      decimalPoint: "\.",
      eNotation: true
      //skipLike: /regex/
  };
  
  function toNumber(str, options = {}){
      // const options = Object.assign({}, consider);
      // if(opt.leadingZeros === false){
      //     options.leadingZeros = false;
      // }else if(opt.hex === false){
      //     options.hex = false;
      // }
  
      options = Object.assign({}, consider, options );
      if(!str || typeof str !== "string" ) return str;
      
      let trimmedStr  = str.trim();
      // if(trimmedStr === "0.0") return 0;
      // else if(trimmedStr === "+0.0") return 0;
      // else if(trimmedStr === "-0.0") return -0;
  
      if(options.skipLike !== undefined && options.skipLike.test(trimmedStr)) return str;
      else if (options.hex && hexRegex.test(trimmedStr)) {
          return Number.parseInt(trimmedStr, 16);
      // } else if (options.parseOct && octRegex.test(str)) {
      //     return Number.parseInt(val, 8);
      // }else if (options.parseBin && binRegex.test(str)) {
      //     return Number.parseInt(val, 2);
      }else{
          //separate negative sign, leading zeros, and rest number
          const match = numRegex.exec(trimmedStr);
          if(match){
              const sign = match[1];
              const leadingZeros = match[2];
              let numTrimmedByZeros = trimZeros(match[3]); //complete num without leading zeros
              //trim ending zeros for floating number
              
              const eNotation = match[4] || match[6];
              if(!options.leadingZeros && leadingZeros.length > 0 && sign && trimmedStr[2] !== ".") return str; //-0123
              else if(!options.leadingZeros && leadingZeros.length > 0 && !sign && trimmedStr[1] !== ".") return str; //0123
              else{//no leading zeros or leading zeros are allowed
                  const num = Number(trimmedStr);
                  const numStr = "" + num;
                  if(numStr.search(/[eE]/) !== -1){ //given number is long and parsed to eNotation
                      if(options.eNotation) return num;
                      else return str;
                  }else if(eNotation){ //given number has enotation
                      if(options.eNotation) return num;
                      else return str;
                  }else if(trimmedStr.indexOf(".") !== -1){ //floating number
                      // const decimalPart = match[5].substr(1);
                      // const intPart = trimmedStr.substr(0,trimmedStr.indexOf("."));
  
                      
                      // const p = numStr.indexOf(".");
                      // const givenIntPart = numStr.substr(0,p);
                      // const givenDecPart = numStr.substr(p+1);
                      if(numStr === "0" && (numTrimmedByZeros === "") ) return num; //0.0
                      else if(numStr === numTrimmedByZeros) return num; //0.456. 0.79000
                      else if( sign && numStr === "-"+numTrimmedByZeros) return num;
                      else return str;
                  }
                  
                  if(leadingZeros){
                      // if(numTrimmedByZeros === numStr){
                      //     if(options.leadingZeros) return num;
                      //     else return str;
                      // }else return str;
                      if(numTrimmedByZeros === numStr) return num;
                      else if(sign+numTrimmedByZeros === numStr) return num;
                      else return str;
                  }
  
                  if(trimmedStr === numStr) return num;
                  else if(trimmedStr === sign+numStr) return num;
                  // else{
                  //     //number with +/- sign
                  //     trimmedStr.test(/[-+][0-9]);
  
                  // }
                  return str;
              }
              // else if(!eNotation && trimmedStr && trimmedStr !== Number(trimmedStr) ) return str;
              
          }else{ //non-numeric string
              return str;
          }
      }
  }
  
  /**
   * 
   * @param {string} numStr without leading zeros
   * @returns 
   */
  function trimZeros(numStr){
      if(numStr && numStr.indexOf(".") !== -1){//float
          numStr = numStr.replace(/0+$/, ""); //remove ending zeros
          if(numStr === ".")  numStr = "0";
          else if(numStr[0] === ".")  numStr = "0"+numStr;
          else if(numStr[numStr.length-1] === ".")  numStr = numStr.substr(0,numStr.length-1);
          return numStr;
      }
      return numStr;
  }
  module.exports = toNumber
  
  },{}]},{},[1])(1)
  });
  