OrderedObjParser.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  1. 'use strict';
  2. ///@ts-check
  3. const util = require('../util');
  4. const xmlNode = require('./xmlNode');
  5. const readDocType = require("./DocTypeReader");
  6. const toNumber = require("strnum");
  7. const getIgnoreAttributesFn = require('../ignoreAttributes')
  8. // const regx =
  9. // '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
  10. // .replace(/NAME/g, util.nameRegexp);
  11. //const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
  12. //const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
  13. class OrderedObjParser{
  14. constructor(options){
  15. this.options = options;
  16. this.currentNode = null;
  17. this.tagsNodeStack = [];
  18. this.docTypeEntities = {};
  19. this.lastEntities = {
  20. "apos" : { regex: /&(apos|#39|#x27);/g, val : "'"},
  21. "gt" : { regex: /&(gt|#62|#x3E);/g, val : ">"},
  22. "lt" : { regex: /&(lt|#60|#x3C);/g, val : "<"},
  23. "quot" : { regex: /&(quot|#34|#x22);/g, val : "\""},
  24. };
  25. this.ampEntity = { regex: /&(amp|#38|#x26);/g, val : "&"};
  26. this.htmlEntities = {
  27. "space": { regex: /&(nbsp|#160);/g, val: " " },
  28. // "lt" : { regex: /&(lt|#60);/g, val: "<" },
  29. // "gt" : { regex: /&(gt|#62);/g, val: ">" },
  30. // "amp" : { regex: /&(amp|#38);/g, val: "&" },
  31. // "quot" : { regex: /&(quot|#34);/g, val: "\"" },
  32. // "apos" : { regex: /&(apos|#39);/g, val: "'" },
  33. "cent" : { regex: /&(cent|#162);/g, val: "¢" },
  34. "pound" : { regex: /&(pound|#163);/g, val: "£" },
  35. "yen" : { regex: /&(yen|#165);/g, val: "¥" },
  36. "euro" : { regex: /&(euro|#8364);/g, val: "€" },
  37. "copyright" : { regex: /&(copy|#169);/g, val: "©" },
  38. "reg" : { regex: /&(reg|#174);/g, val: "®" },
  39. "inr" : { regex: /&(inr|#8377);/g, val: "₹" },
  40. "num_dec": { regex: /&#([0-9]{1,7});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 10)) },
  41. "num_hex": { regex: /&#x([0-9a-fA-F]{1,6});/g, val : (_, str) => String.fromCharCode(Number.parseInt(str, 16)) },
  42. };
  43. this.addExternalEntities = addExternalEntities;
  44. this.parseXml = parseXml;
  45. this.parseTextData = parseTextData;
  46. this.resolveNameSpace = resolveNameSpace;
  47. this.buildAttributesMap = buildAttributesMap;
  48. this.isItStopNode = isItStopNode;
  49. this.replaceEntitiesValue = replaceEntitiesValue;
  50. this.readStopNodeData = readStopNodeData;
  51. this.saveTextToParentTag = saveTextToParentTag;
  52. this.addChild = addChild;
  53. this.ignoreAttributesFn = getIgnoreAttributesFn(this.options.ignoreAttributes)
  54. }
  55. }
  56. function addExternalEntities(externalEntities){
  57. const entKeys = Object.keys(externalEntities);
  58. for (let i = 0; i < entKeys.length; i++) {
  59. const ent = entKeys[i];
  60. this.lastEntities[ent] = {
  61. regex: new RegExp("&"+ent+";","g"),
  62. val : externalEntities[ent]
  63. }
  64. }
  65. }
  66. /**
  67. * @param {string} val
  68. * @param {string} tagName
  69. * @param {string} jPath
  70. * @param {boolean} dontTrim
  71. * @param {boolean} hasAttributes
  72. * @param {boolean} isLeafNode
  73. * @param {boolean} escapeEntities
  74. */
  75. function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
  76. if (val !== undefined) {
  77. if (this.options.trimValues && !dontTrim) {
  78. val = val.trim();
  79. }
  80. if(val.length > 0){
  81. if(!escapeEntities) val = this.replaceEntitiesValue(val);
  82. const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
  83. if(newval === null || newval === undefined){
  84. //don't parse
  85. return val;
  86. }else if(typeof newval !== typeof val || newval !== val){
  87. //overwrite
  88. return newval;
  89. }else if(this.options.trimValues){
  90. return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
  91. }else{
  92. const trimmedVal = val.trim();
  93. if(trimmedVal === val){
  94. return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
  95. }else{
  96. return val;
  97. }
  98. }
  99. }
  100. }
  101. }
  102. function resolveNameSpace(tagname) {
  103. if (this.options.removeNSPrefix) {
  104. const tags = tagname.split(':');
  105. const prefix = tagname.charAt(0) === '/' ? '/' : '';
  106. if (tags[0] === 'xmlns') {
  107. return '';
  108. }
  109. if (tags.length === 2) {
  110. tagname = prefix + tags[1];
  111. }
  112. }
  113. return tagname;
  114. }
  115. //TODO: change regex to capture NS
  116. //const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
  117. const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
  118. function buildAttributesMap(attrStr, jPath, tagName) {
  119. if (this.options.ignoreAttributes !== true && typeof attrStr === 'string') {
  120. // attrStr = attrStr.replace(/\r?\n/g, ' ');
  121. //attrStr = attrStr || attrStr.trim();
  122. const matches = util.getAllMatches(attrStr, attrsRegx);
  123. const len = matches.length; //don't make it inline
  124. const attrs = {};
  125. for (let i = 0; i < len; i++) {
  126. const attrName = this.resolveNameSpace(matches[i][1]);
  127. if (this.ignoreAttributesFn(attrName, jPath)) {
  128. continue
  129. }
  130. let oldVal = matches[i][4];
  131. let aName = this.options.attributeNamePrefix + attrName;
  132. if (attrName.length) {
  133. if (this.options.transformAttributeName) {
  134. aName = this.options.transformAttributeName(aName);
  135. }
  136. if(aName === "__proto__") aName = "#__proto__";
  137. if (oldVal !== undefined) {
  138. if (this.options.trimValues) {
  139. oldVal = oldVal.trim();
  140. }
  141. oldVal = this.replaceEntitiesValue(oldVal);
  142. const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
  143. if(newVal === null || newVal === undefined){
  144. //don't parse
  145. attrs[aName] = oldVal;
  146. }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
  147. //overwrite
  148. attrs[aName] = newVal;
  149. }else{
  150. //parse
  151. attrs[aName] = parseValue(
  152. oldVal,
  153. this.options.parseAttributeValue,
  154. this.options.numberParseOptions
  155. );
  156. }
  157. } else if (this.options.allowBooleanAttributes) {
  158. attrs[aName] = true;
  159. }
  160. }
  161. }
  162. if (!Object.keys(attrs).length) {
  163. return;
  164. }
  165. if (this.options.attributesGroupName) {
  166. const attrCollection = {};
  167. attrCollection[this.options.attributesGroupName] = attrs;
  168. return attrCollection;
  169. }
  170. return attrs
  171. }
  172. }
  173. const parseXml = function(xmlData) {
  174. xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
  175. const xmlObj = new xmlNode('!xml');
  176. let currentNode = xmlObj;
  177. let textData = "";
  178. let jPath = "";
  179. for(let i=0; i< xmlData.length; i++){//for each char in XML data
  180. const ch = xmlData[i];
  181. if(ch === '<'){
  182. // const nextIndex = i+1;
  183. // const _2ndChar = xmlData[nextIndex];
  184. if( xmlData[i+1] === '/') {//Closing Tag
  185. const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
  186. let tagName = xmlData.substring(i+2,closeIndex).trim();
  187. if(this.options.removeNSPrefix){
  188. const colonIndex = tagName.indexOf(":");
  189. if(colonIndex !== -1){
  190. tagName = tagName.substr(colonIndex+1);
  191. }
  192. }
  193. if(this.options.transformTagName) {
  194. tagName = this.options.transformTagName(tagName);
  195. }
  196. if(currentNode){
  197. textData = this.saveTextToParentTag(textData, currentNode, jPath);
  198. }
  199. //check if last tag of nested tag was unpaired tag
  200. const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
  201. if(tagName && this.options.unpairedTags.indexOf(tagName) !== -1 ){
  202. throw new Error(`Unpaired tag can not be used as closing tag: </${tagName}>`);
  203. }
  204. let propIndex = 0
  205. if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){
  206. propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1)
  207. this.tagsNodeStack.pop();
  208. }else{
  209. propIndex = jPath.lastIndexOf(".");
  210. }
  211. jPath = jPath.substring(0, propIndex);
  212. currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
  213. textData = "";
  214. i = closeIndex;
  215. } else if( xmlData[i+1] === '?') {
  216. let tagData = readTagExp(xmlData,i, false, "?>");
  217. if(!tagData) throw new Error("Pi Tag is not closed.");
  218. textData = this.saveTextToParentTag(textData, currentNode, jPath);
  219. if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){
  220. }else{
  221. const childNode = new xmlNode(tagData.tagName);
  222. childNode.add(this.options.textNodeName, "");
  223. if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
  224. childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
  225. }
  226. this.addChild(currentNode, childNode, jPath)
  227. }
  228. i = tagData.closeIndex + 1;
  229. } else if(xmlData.substr(i + 1, 3) === '!--') {
  230. const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
  231. if(this.options.commentPropName){
  232. const comment = xmlData.substring(i + 4, endIndex - 2);
  233. textData = this.saveTextToParentTag(textData, currentNode, jPath);
  234. currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
  235. }
  236. i = endIndex;
  237. } else if( xmlData.substr(i + 1, 2) === '!D') {
  238. const result = readDocType(xmlData, i);
  239. this.docTypeEntities = result.entities;
  240. i = result.i;
  241. }else if(xmlData.substr(i + 1, 2) === '![') {
  242. const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
  243. const tagExp = xmlData.substring(i + 9,closeIndex);
  244. textData = this.saveTextToParentTag(textData, currentNode, jPath);
  245. let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true, true);
  246. if(val == undefined) val = "";
  247. //cdata should be set even if it is 0 length string
  248. if(this.options.cdataPropName){
  249. currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
  250. }else{
  251. currentNode.add(this.options.textNodeName, val);
  252. }
  253. i = closeIndex + 2;
  254. }else {//Opening tag
  255. let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
  256. let tagName= result.tagName;
  257. const rawTagName = result.rawTagName;
  258. let tagExp = result.tagExp;
  259. let attrExpPresent = result.attrExpPresent;
  260. let closeIndex = result.closeIndex;
  261. if (this.options.transformTagName) {
  262. tagName = this.options.transformTagName(tagName);
  263. }
  264. //save text as child node
  265. if (currentNode && textData) {
  266. if(currentNode.tagname !== '!xml'){
  267. //when nested tag is found
  268. textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
  269. }
  270. }
  271. //check if last tag was unpaired tag
  272. const lastTag = currentNode;
  273. if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
  274. currentNode = this.tagsNodeStack.pop();
  275. jPath = jPath.substring(0, jPath.lastIndexOf("."));
  276. }
  277. if(tagName !== xmlObj.tagname){
  278. jPath += jPath ? "." + tagName : tagName;
  279. }
  280. if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) {
  281. let tagContent = "";
  282. //self-closing tag
  283. if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
  284. if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
  285. tagName = tagName.substr(0, tagName.length - 1);
  286. jPath = jPath.substr(0, jPath.length - 1);
  287. tagExp = tagName;
  288. }else{
  289. tagExp = tagExp.substr(0, tagExp.length - 1);
  290. }
  291. i = result.closeIndex;
  292. }
  293. //unpaired tag
  294. else if(this.options.unpairedTags.indexOf(tagName) !== -1){
  295. i = result.closeIndex;
  296. }
  297. //normal tag
  298. else{
  299. //read until closing tag is found
  300. const result = this.readStopNodeData(xmlData, rawTagName, closeIndex + 1);
  301. if(!result) throw new Error(`Unexpected end of ${rawTagName}`);
  302. i = result.i;
  303. tagContent = result.tagContent;
  304. }
  305. const childNode = new xmlNode(tagName);
  306. if(tagName !== tagExp && attrExpPresent){
  307. childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
  308. }
  309. if(tagContent) {
  310. tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
  311. }
  312. jPath = jPath.substr(0, jPath.lastIndexOf("."));
  313. childNode.add(this.options.textNodeName, tagContent);
  314. this.addChild(currentNode, childNode, jPath)
  315. }else{
  316. //selfClosing tag
  317. if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
  318. if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
  319. tagName = tagName.substr(0, tagName.length - 1);
  320. jPath = jPath.substr(0, jPath.length - 1);
  321. tagExp = tagName;
  322. }else{
  323. tagExp = tagExp.substr(0, tagExp.length - 1);
  324. }
  325. if(this.options.transformTagName) {
  326. tagName = this.options.transformTagName(tagName);
  327. }
  328. const childNode = new xmlNode(tagName);
  329. if(tagName !== tagExp && attrExpPresent){
  330. childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
  331. }
  332. this.addChild(currentNode, childNode, jPath)
  333. jPath = jPath.substr(0, jPath.lastIndexOf("."));
  334. }
  335. //opening tag
  336. else{
  337. const childNode = new xmlNode( tagName);
  338. this.tagsNodeStack.push(currentNode);
  339. if(tagName !== tagExp && attrExpPresent){
  340. childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
  341. }
  342. this.addChild(currentNode, childNode, jPath)
  343. currentNode = childNode;
  344. }
  345. textData = "";
  346. i = closeIndex;
  347. }
  348. }
  349. }else{
  350. textData += xmlData[i];
  351. }
  352. }
  353. return xmlObj.child;
  354. }
  355. function addChild(currentNode, childNode, jPath){
  356. const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
  357. if(result === false){
  358. }else if(typeof result === "string"){
  359. childNode.tagname = result
  360. currentNode.addChild(childNode);
  361. }else{
  362. currentNode.addChild(childNode);
  363. }
  364. }
  365. const replaceEntitiesValue = function(val){
  366. if(this.options.processEntities){
  367. for(let entityName in this.docTypeEntities){
  368. const entity = this.docTypeEntities[entityName];
  369. val = val.replace( entity.regx, entity.val);
  370. }
  371. for(let entityName in this.lastEntities){
  372. const entity = this.lastEntities[entityName];
  373. val = val.replace( entity.regex, entity.val);
  374. }
  375. if(this.options.htmlEntities){
  376. for(let entityName in this.htmlEntities){
  377. const entity = this.htmlEntities[entityName];
  378. val = val.replace( entity.regex, entity.val);
  379. }
  380. }
  381. val = val.replace( this.ampEntity.regex, this.ampEntity.val);
  382. }
  383. return val;
  384. }
  385. function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
  386. if (textData) { //store previously collected data as textNode
  387. if(isLeafNode === undefined) isLeafNode = currentNode.child.length === 0
  388. textData = this.parseTextData(textData,
  389. currentNode.tagname,
  390. jPath,
  391. false,
  392. currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
  393. isLeafNode);
  394. if (textData !== undefined && textData !== "")
  395. currentNode.add(this.options.textNodeName, textData);
  396. textData = "";
  397. }
  398. return textData;
  399. }
  400. //TODO: use jPath to simplify the logic
  401. /**
  402. *
  403. * @param {string[]} stopNodes
  404. * @param {string} jPath
  405. * @param {string} currentTagName
  406. */
  407. function isItStopNode(stopNodes, jPath, currentTagName){
  408. const allNodesExp = "*." + currentTagName;
  409. for (const stopNodePath in stopNodes) {
  410. const stopNodeExp = stopNodes[stopNodePath];
  411. if( allNodesExp === stopNodeExp || jPath === stopNodeExp ) return true;
  412. }
  413. return false;
  414. }
  415. /**
  416. * Returns the tag Expression and where it is ending handling single-double quotes situation
  417. * @param {string} xmlData
  418. * @param {number} i starting index
  419. * @returns
  420. */
  421. function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
  422. let attrBoundary;
  423. let tagExp = "";
  424. for (let index = i; index < xmlData.length; index++) {
  425. let ch = xmlData[index];
  426. if (attrBoundary) {
  427. if (ch === attrBoundary) attrBoundary = "";//reset
  428. } else if (ch === '"' || ch === "'") {
  429. attrBoundary = ch;
  430. } else if (ch === closingChar[0]) {
  431. if(closingChar[1]){
  432. if(xmlData[index + 1] === closingChar[1]){
  433. return {
  434. data: tagExp,
  435. index: index
  436. }
  437. }
  438. }else{
  439. return {
  440. data: tagExp,
  441. index: index
  442. }
  443. }
  444. } else if (ch === '\t') {
  445. ch = " "
  446. }
  447. tagExp += ch;
  448. }
  449. }
  450. function findClosingIndex(xmlData, str, i, errMsg){
  451. const closingIndex = xmlData.indexOf(str, i);
  452. if(closingIndex === -1){
  453. throw new Error(errMsg)
  454. }else{
  455. return closingIndex + str.length - 1;
  456. }
  457. }
  458. function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
  459. const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
  460. if(!result) return;
  461. let tagExp = result.data;
  462. const closeIndex = result.index;
  463. const separatorIndex = tagExp.search(/\s/);
  464. let tagName = tagExp;
  465. let attrExpPresent = true;
  466. if(separatorIndex !== -1){//separate tag name and attributes expression
  467. tagName = tagExp.substring(0, separatorIndex);
  468. tagExp = tagExp.substring(separatorIndex + 1).trimStart();
  469. }
  470. const rawTagName = tagName;
  471. if(removeNSPrefix){
  472. const colonIndex = tagName.indexOf(":");
  473. if(colonIndex !== -1){
  474. tagName = tagName.substr(colonIndex+1);
  475. attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
  476. }
  477. }
  478. return {
  479. tagName: tagName,
  480. tagExp: tagExp,
  481. closeIndex: closeIndex,
  482. attrExpPresent: attrExpPresent,
  483. rawTagName: rawTagName,
  484. }
  485. }
  486. /**
  487. * find paired tag for a stop node
  488. * @param {string} xmlData
  489. * @param {string} tagName
  490. * @param {number} i
  491. */
  492. function readStopNodeData(xmlData, tagName, i){
  493. const startIndex = i;
  494. // Starting at 1 since we already have an open tag
  495. let openTagCount = 1;
  496. for (; i < xmlData.length; i++) {
  497. if( xmlData[i] === "<"){
  498. if (xmlData[i+1] === "/") {//close tag
  499. const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
  500. let closeTagName = xmlData.substring(i+2,closeIndex).trim();
  501. if(closeTagName === tagName){
  502. openTagCount--;
  503. if (openTagCount === 0) {
  504. return {
  505. tagContent: xmlData.substring(startIndex, i),
  506. i : closeIndex
  507. }
  508. }
  509. }
  510. i=closeIndex;
  511. } else if(xmlData[i+1] === '?') {
  512. const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.")
  513. i=closeIndex;
  514. } else if(xmlData.substr(i + 1, 3) === '!--') {
  515. const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.")
  516. i=closeIndex;
  517. } else if(xmlData.substr(i + 1, 2) === '![') {
  518. const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
  519. i=closeIndex;
  520. } else {
  521. const tagData = readTagExp(xmlData, i, '>')
  522. if (tagData) {
  523. const openTagName = tagData && tagData.tagName;
  524. if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
  525. openTagCount++;
  526. }
  527. i=tagData.closeIndex;
  528. }
  529. }
  530. }
  531. }//end for loop
  532. }
  533. function parseValue(val, shouldParse, options) {
  534. if (shouldParse && typeof val === 'string') {
  535. //console.log(options)
  536. const newval = val.trim();
  537. if(newval === 'true' ) return true;
  538. else if(newval === 'false' ) return false;
  539. else return toNumber(val, options);
  540. } else {
  541. if (util.isExist(val)) {
  542. return val;
  543. } else {
  544. return '';
  545. }
  546. }
  547. }
  548. module.exports = OrderedObjParser;