• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

NaturalIntelligence / fast-xml-parser / 4731098416

pending completion
4731098416

push

github

amit kumar gupta
update funding detail

751 of 808 branches covered (92.95%)

2501 of 2547 relevant lines covered (98.19%)

186303.3 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.25
/src/xmlparser/OrderedObjParser.js
1
'use strict';
2
///@ts-check
3

4
const util = require('../util');
1✔
5
const xmlNode = require('./xmlNode');
1✔
6
const readDocType = require("./DocTypeReader");
1✔
7
const toNumber = require("strnum");
1✔
8

9
const regx =
10
  '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|((NAME:)?(NAME))([^>]*)>|((\\/)(NAME)\\s*>))([^<]*)'
1✔
11
  .replace(/NAME/g, util.nameRegexp);
12

13
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
14
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
15

16
class OrderedObjParser{
17
  constructor(options){
18
    this.options = options;
148✔
19
    this.currentNode = null;
148✔
20
    this.tagsNodeStack = [];
148✔
21
    this.docTypeEntities = {};
148✔
22
    this.lastEntities = {
148✔
23
      "apos" : { regex: /&(apos|#39|#x27);/g, val : "'"},
24
      "gt" : { regex: /&(gt|#62|#x3E);/g, val : ">"},
25
      "lt" : { regex: /&(lt|#60|#x3C);/g, val : "<"},
26
      "quot" : { regex: /&(quot|#34|#x22);/g, val : "\""},
27
    };
28
    this.ampEntity = { regex: /&(amp|#38|#x26);/g, val : "&"};
148✔
29
    this.htmlEntities = {
148✔
30
      "space": { regex: /&(nbsp|#160);/g, val: " " },
31
      // "lt" : { regex: /&(lt|#60);/g, val: "<" },
32
      // "gt" : { regex: /&(gt|#62);/g, val: ">" },
33
      // "amp" : { regex: /&(amp|#38);/g, val: "&" },
34
      // "quot" : { regex: /&(quot|#34);/g, val: "\"" },
35
      // "apos" : { regex: /&(apos|#39);/g, val: "'" },
36
      "cent" : { regex: /&(cent|#162);/g, val: "¢" },
37
      "pound" : { regex: /&(pound|#163);/g, val: "£" },
38
      "yen" : { regex: /&(yen|#165);/g, val: "Â¥" },
39
      "euro" : { regex: /&(euro|#8364);/g, val: "€" },
40
      "copyright" : { regex: /&(copy|#169);/g, val: "©" },
41
      "reg" : { regex: /&(reg|#174);/g, val: "®" },
42
      "inr" : { regex: /&(inr|#8377);/g, val: "₹" },
43
    };
44
    this.addExternalEntities = addExternalEntities;
148✔
45
    this.parseXml = parseXml;
148✔
46
    this.parseTextData = parseTextData;
148✔
47
    this.resolveNameSpace = resolveNameSpace;
148✔
48
    this.buildAttributesMap = buildAttributesMap;
148✔
49
    this.isItStopNode = isItStopNode;
148✔
50
    this.replaceEntitiesValue = replaceEntitiesValue;
148✔
51
    this.readStopNodeData = readStopNodeData;
148✔
52
    this.saveTextToParentTag = saveTextToParentTag;
148✔
53
    this.addChild = addChild;
148✔
54
  }
55

56
}
57

58
function addExternalEntities(externalEntities){
59
  const entKeys = Object.keys(externalEntities);
148✔
60
  for (let i = 0; i < entKeys.length; i++) {
148✔
61
    const ent = entKeys[i];
3✔
62
    this.lastEntities[ent] = {
3✔
63
       regex: new RegExp("&"+ent+";","g"),
64
       val : externalEntities[ent]
65
    }
66
  }
67
}
68

69
/**
70
 * @param {string} val
71
 * @param {string} tagName
72
 * @param {string} jPath
73
 * @param {boolean} dontTrim
74
 * @param {boolean} hasAttributes
75
 * @param {boolean} isLeafNode
76
 * @param {boolean} escapeEntities
77
 */
78
function parseTextData(val, tagName, jPath, dontTrim, hasAttributes, isLeafNode, escapeEntities) {
79
  if (val !== undefined) {
999!
80
    if (this.options.trimValues && !dontTrim) {
999✔
81
      val = val.trim();
935✔
82
    }
83
    if(val.length > 0){
999✔
84
      if(!escapeEntities) val = this.replaceEntitiesValue(val);
384✔
85
      
86
      const newval = this.options.tagValueProcessor(tagName, val, jPath, hasAttributes, isLeafNode);
384✔
87
      if(newval === null || newval === undefined){
384✔
88
        //don't parse
89
        return val;
10✔
90
      }else if(typeof newval !== typeof val || newval !== val){
374✔
91
        //overwrite
92
        return newval;
4✔
93
      }else if(this.options.trimValues){
370✔
94
        return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
358✔
95
      }else{
96
        const trimmedVal = val.trim();
12✔
97
        if(trimmedVal === val){
12✔
98
          return parseValue(val, this.options.parseTagValue, this.options.numberParseOptions);
3✔
99
        }else{
100
          return val;
9✔
101
        }
102
      }
103
    }
104
  }
105
}
106

107
function resolveNameSpace(tagname) {
108
  if (this.options.removeNSPrefix) {
246✔
109
    const tags = tagname.split(':');
22✔
110
    const prefix = tagname.charAt(0) === '/' ? '/' : '';
22!
111
    if (tags[0] === 'xmlns') {
22✔
112
      return '';
7✔
113
    }
114
    if (tags.length === 2) {
15✔
115
      tagname = prefix + tags[1];
6✔
116
    }
117
  }
118
  return tagname;
239✔
119
}
120

121
//TODO: change regex to capture NS
122
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
123
const attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])([\\s\\S]*?)\\3)?', 'gm');
1✔
124

125
function buildAttributesMap(attrStr, jPath, tagName) {
126
  if (!this.options.ignoreAttributes && typeof attrStr === 'string') {
203✔
127
    // attrStr = attrStr.replace(/\r?\n/g, ' ');
128
    //attrStr = attrStr || attrStr.trim();
129

130
    const matches = util.getAllMatches(attrStr, attrsRegx);
170✔
131
    const len = matches.length; //don't make it inline
170✔
132
    const attrs = {};
170✔
133
    for (let i = 0; i < len; i++) {
170✔
134
      const attrName = this.resolveNameSpace(matches[i][1]);
246✔
135
      let oldVal = matches[i][4];
246✔
136
      let aName = this.options.attributeNamePrefix + attrName;
246✔
137
      if (attrName.length) {
246✔
138
        if (this.options.transformAttributeName) {
239✔
139
          aName = this.options.transformAttributeName(aName);
4✔
140
        }
141
        if(aName === "__proto__") aName  = "#__proto__";
239!
142
        if (oldVal !== undefined) {
239✔
143
          if (this.options.trimValues) {
222✔
144
            oldVal = oldVal.trim();
217✔
145
          }
146
          oldVal = this.replaceEntitiesValue(oldVal);
222✔
147
          const newVal = this.options.attributeValueProcessor(attrName, oldVal, jPath);
222✔
148
          if(newVal === null || newVal === undefined){
222!
149
            //don't parse
150
            attrs[aName] = oldVal;
×
151
          }else if(typeof newVal !== typeof oldVal || newVal !== oldVal){
222!
152
            //overwrite
153
            attrs[aName] = newVal;
×
154
          }else{
155
            //parse
156
            attrs[aName] = parseValue(
222✔
157
              oldVal,
158
              this.options.parseAttributeValue,
159
              this.options.numberParseOptions
160
            );
161
          }
162
        } else if (this.options.allowBooleanAttributes) {
17✔
163
          attrs[aName] = true;
15✔
164
        }
165
      }
166
    }
167
    if (!Object.keys(attrs).length) {
170✔
168
      return;
17✔
169
    }
170
    if (this.options.attributesGroupName) {
153✔
171
      const attrCollection = {};
12✔
172
      attrCollection[this.options.attributesGroupName] = attrs;
12✔
173
      return attrCollection;
12✔
174
    }
175
    return attrs
141✔
176
  }
177
}
178

179
const parseXml = function(xmlData) {
1✔
180
  xmlData = xmlData.replace(/\r\n?/g, "\n"); //TODO: remove this line
148✔
181
  const xmlObj = new xmlNode('!xml');
148✔
182
  let currentNode = xmlObj;
148✔
183
  let textData = "";
148✔
184
  let jPath = "";
148✔
185
  for(let i=0; i< xmlData.length; i++){//for each char in XML data
148✔
186
    const ch = xmlData[i];
11,591✔
187
    if(ch === '<'){
11,591✔
188
      // const nextIndex = i+1;
189
      // const _2ndChar = xmlData[nextIndex];
190
      if( xmlData[i+1] === '/') {//Closing Tag
1,282✔
191
        const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
536✔
192
        let tagName = xmlData.substring(i+2,closeIndex).trim();
535✔
193

194
        if(this.options.removeNSPrefix){
535✔
195
          const colonIndex = tagName.indexOf(":");
21✔
196
          if(colonIndex !== -1){
21✔
197
            tagName = tagName.substr(colonIndex+1);
10✔
198
          }
199
        }
200

201
        if(this.options.transformTagName) {
535✔
202
          tagName = this.options.transformTagName(tagName);
10✔
203
        }
204

205
        if(currentNode){
535!
206
          textData = this.saveTextToParentTag(textData, currentNode, jPath);
535✔
207
        }
208

209
        //check if last tag of nested tag was unpaired tag
210
        const lastTagName = jPath.substring(jPath.lastIndexOf(".")+1);
535✔
211
        let propIndex = 0
535✔
212
        if(lastTagName && this.options.unpairedTags.indexOf(lastTagName) !== -1 ){
535✔
213
          propIndex = jPath.lastIndexOf('.', jPath.lastIndexOf('.')-1)
13✔
214
          if(propIndex < 1) propIndex = jPath.lastIndexOf(".");
13✔
215
        }else{
216
          propIndex = jPath.lastIndexOf(".");
522✔
217
        }
218
        jPath = jPath.substring(0, propIndex);
535✔
219

220
        currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
535✔
221
        textData = "";
535✔
222
        i = closeIndex;
535✔
223
      } else if( xmlData[i+1] === '?') {
746✔
224

225
        let tagData = readTagExp(xmlData,i, false, "?>");
44✔
226
        if(!tagData) throw new Error("Pi Tag is not closed.");
44✔
227

228
        textData = this.saveTextToParentTag(textData, currentNode, jPath);
42✔
229
        if( (this.options.ignoreDeclaration && tagData.tagName === "?xml") || this.options.ignorePiTags){
42✔
230

231
        }else{
232
  
233
          const childNode = new xmlNode(tagData.tagName);
37✔
234
          childNode.add(this.options.textNodeName, "");
37✔
235
          
236
          if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
37✔
237
            childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
35✔
238
          }
239
          this.addChild(currentNode, childNode, jPath)
37✔
240

241
        }
242

243

244
        i = tagData.closeIndex + 1;
42✔
245
      } else if(xmlData.substr(i + 1, 3) === '!--') {
702✔
246
        const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
12✔
247
        if(this.options.commentPropName){
11✔
248
          const comment = xmlData.substring(i + 4, endIndex - 2);
5✔
249

250
          textData = this.saveTextToParentTag(textData, currentNode, jPath);
5✔
251

252
          currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
5✔
253
        }
254
        i = endIndex;
11✔
255
      } else if( xmlData.substr(i + 1, 2) === '!D') {
690✔
256
        const result = readDocType(xmlData, i);
11✔
257
        this.docTypeEntities = result.entities;
10✔
258
        i = result.i;
10✔
259
      }else if(xmlData.substr(i + 1, 2) === '![') {
679✔
260
        const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
50✔
261
        const tagExp = xmlData.substring(i + 9,closeIndex);
49✔
262

263
        textData = this.saveTextToParentTag(textData, currentNode, jPath);
49✔
264

265
        //cdata should be set even if it is 0 length string
266
        if(this.options.cdataPropName){
49✔
267
          // let val = this.parseTextData(tagExp, this.options.cdataPropName, jPath + "." + this.options.cdataPropName, true, false, true);
268
          // if(!val) val = "";
269
          currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
13✔
270
        }else{
271
          let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true);
36✔
272
          if(val == undefined) val = "";
36✔
273
          currentNode.add(this.options.textNodeName, val);
36✔
274
        }
275
        
276
        i = closeIndex + 2;
49✔
277
      }else {//Opening tag
278
        let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
629✔
279
        let tagName= result.tagName;
629✔
280
        let tagExp = result.tagExp;
629✔
281
        let attrExpPresent = result.attrExpPresent;
629✔
282
        let closeIndex = result.closeIndex;
629✔
283

284
        if (this.options.transformTagName) {
629✔
285
          tagName = this.options.transformTagName(tagName);
10✔
286
        }
287
        
288
        //save text as child node
289
        if (currentNode && textData) {
629✔
290
          if(currentNode.tagname !== '!xml'){
473✔
291
            //when nested tag is found
292
            textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
433✔
293
          }
294
        }
295

296
        //check if last tag was unpaired tag
297
        const lastTag = currentNode;
629✔
298
        if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
629✔
299
          currentNode = this.tagsNodeStack.pop();
18✔
300
          jPath = jPath.substring(0, jPath.lastIndexOf("."));
18✔
301
        }
302
        if(tagName !== xmlObj.tagname){
629!
303
          jPath += jPath ? "." + tagName : tagName;
629✔
304
        }
305
        if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) { //TODO: namespace
629✔
306
          let tagContent = "";
22✔
307
          //self-closing tag
308
          if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
22✔
309
            i = result.closeIndex;
1✔
310
          }
311
          //unpaired tag
312
          else if(this.options.unpairedTags.indexOf(tagName) !== -1){
21✔
313
            i = result.closeIndex;
1✔
314
          }
315
          //normal tag
316
          else{
317
            //read until closing tag is found
318
            const result = this.readStopNodeData(xmlData, tagName, closeIndex + 1);
20✔
319
            if(!result) throw new Error(`Unexpected end of ${tagName}`);
20!
320
            i = result.i;
20✔
321
            tagContent = result.tagContent;
20✔
322
          }
323

324
          const childNode = new xmlNode(tagName);
22✔
325
          if(tagName !== tagExp && attrExpPresent){
22✔
326
            childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
8✔
327
          }
328
          if(tagContent) {
22✔
329
            tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
16✔
330
          }
331
          
332
          jPath = jPath.substr(0, jPath.lastIndexOf("."));
22✔
333
          childNode.add(this.options.textNodeName, tagContent);
22✔
334
          
335
          this.addChild(currentNode, childNode, jPath)
22✔
336
        }else{
337
  //selfClosing tag
338
          if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
607✔
339
            if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
39✔
340
              tagName = tagName.substr(0, tagName.length - 1);
9✔
341
              tagExp = tagName;
9✔
342
            }else{
343
              tagExp = tagExp.substr(0, tagExp.length - 1);
30✔
344
            }
345
            
346
            if(this.options.transformTagName) {
39!
347
              tagName = this.options.transformTagName(tagName);
×
348
            }
349

350
            const childNode = new xmlNode(tagName);
39✔
351
            if(tagName !== tagExp && attrExpPresent){
39✔
352
              childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
30✔
353
            }
354
            this.addChild(currentNode, childNode, jPath)
39✔
355
            jPath = jPath.substr(0, jPath.lastIndexOf("."));
39✔
356
          }
357
    //opening tag
358
          else{
359
            const childNode = new xmlNode( tagName);
568✔
360
            this.tagsNodeStack.push(currentNode);
568✔
361
            
362
            if(tagName !== tagExp && attrExpPresent){
568✔
363
              childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
130✔
364
            }
365
            this.addChild(currentNode, childNode, jPath)
568✔
366
            currentNode = childNode;
568✔
367
          }
368
          textData = "";
607✔
369
          i = closeIndex;
607✔
370
        }
371
      }
372
    }else{
373
      textData += xmlData[i];
10,309✔
374
    }
375
  }
376
  return xmlObj.child;
142✔
377
}
378

379
function addChild(currentNode, childNode, jPath){
380
  const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
666✔
381
  if(result === false){
666✔
382
  }else if(typeof result === "string"){
663!
383
    childNode.tagname = result
663✔
384
    currentNode.addChild(childNode);
663✔
385
  }else{
386
    currentNode.addChild(childNode);
×
387
  }
388
}
389

390
const replaceEntitiesValue = function(val){
1✔
391

392
  if(this.options.processEntities){
590✔
393
    for(let entityName in this.docTypeEntities){
577✔
394
      const entity = this.docTypeEntities[entityName];
42✔
395
      val = val.replace( entity.regx, entity.val);
42✔
396
    }
397
    for(let entityName in this.lastEntities){
577✔
398
      const entity = this.lastEntities[entityName];
2,309✔
399
      val = val.replace( entity.regex, entity.val);
2,309✔
400
    }
401
    if(this.options.htmlEntities){
577✔
402
      for(let entityName in this.htmlEntities){
24✔
403
        const entity = this.htmlEntities[entityName];
192✔
404
        val = val.replace( entity.regex, entity.val);
192✔
405
      }
406
    }
407
    val = val.replace( this.ampEntity.regex, this.ampEntity.val);
577✔
408
  }
409
  return val;
590✔
410
}
411
function saveTextToParentTag(textData, currentNode, jPath, isLeafNode) {
412
  if (textData) { //store previously collected data as textNode
1,064✔
413
    if(isLeafNode === undefined) isLeafNode = Object.keys(currentNode.child).length === 0
947✔
414
    
415
    textData = this.parseTextData(textData,
947✔
416
      currentNode.tagname,
417
      jPath,
418
      false,
419
      currentNode[":@"] ? Object.keys(currentNode[":@"]).length !== 0 : false,
947✔
420
      isLeafNode);
421

422
    if (textData !== undefined && textData !== "")
947✔
423
      currentNode.add(this.options.textNodeName, textData);
334✔
424
    textData = "";
947✔
425
  }
426
  return textData;
1,064✔
427
}
428

429
//TODO: use jPath to simplify the logic
430
/**
431
 * 
432
 * @param {string[]} stopNodes 
433
 * @param {string} jPath
434
 * @param {string} currentTagName 
435
 */
436
function isItStopNode(stopNodes, jPath, currentTagName){
437
  const allNodesExp = "*." + currentTagName;
629✔
438
  for (const stopNodePath in stopNodes) {
629✔
439
    const stopNodeExp = stopNodes[stopNodePath];
106✔
440
    if( allNodesExp === stopNodeExp || jPath === stopNodeExp  ) return true;
106✔
441
  }
442
  return false;
607✔
443
}
444

445
/**
446
 * Returns the tag Expression and where it is ending handling single-double quotes situation
447
 * @param {string} xmlData 
448
 * @param {number} i starting index
449
 * @returns 
450
 */
451
function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
×
452
  let attrBoundary;
453
  let tagExp = "";
699✔
454
  for (let index = i; index < xmlData.length; index++) {
699✔
455
    let ch = xmlData[index];
9,456✔
456
    if (attrBoundary) {
9,456✔
457
        if (ch === attrBoundary) attrBoundary = "";//reset
2,473✔
458
    } else if (ch === '"' || ch === "'") {
6,983✔
459
        attrBoundary = ch;
270✔
460
    } else if (ch === closingChar[0]) {
6,713✔
461
      if(closingChar[1]){
740✔
462
        if(xmlData[index + 1] === closingChar[1]){
86✔
463
          return {
42✔
464
            data: tagExp,
465
            index: index
466
          }
467
        }
468
      }else{
469
        return {
654✔
470
          data: tagExp,
471
          index: index
472
        }
473
      }
474
    } else if (ch === '\t') {
5,973✔
475
      ch = " "
4✔
476
    }
477
    tagExp += ch;
8,760✔
478
  }
479
}
480

481
function findClosingIndex(xmlData, str, i, errMsg){
482
  const closingIndex = xmlData.indexOf(str, i);
644✔
483
  if(closingIndex === -1){
644✔
484
    throw new Error(errMsg)
3✔
485
  }else{
486
    return closingIndex + str.length - 1;
641✔
487
  }
488
}
489

490
function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
655✔
491
  const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
699✔
492
  if(!result) return;
699✔
493
  let tagExp = result.data;
696✔
494
  const closeIndex = result.index;
696✔
495
  const separatorIndex = tagExp.search(/\s/);
696✔
496
  let tagName = tagExp;
696✔
497
  let attrExpPresent = true;
696✔
498
  if(separatorIndex !== -1){//separate tag name and attributes expression
696✔
499
    tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
215✔
500
    tagExp = tagExp.substr(separatorIndex + 1);
215✔
501
  }
502

503
  if(removeNSPrefix){
696✔
504
    const colonIndex = tagName.indexOf(":");
48✔
505
    if(colonIndex !== -1){
48✔
506
      tagName = tagName.substr(colonIndex+1);
12✔
507
      attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
12✔
508
    }
509
  }
510

511
  return {
696✔
512
    tagName: tagName,
513
    tagExp: tagExp,
514
    closeIndex: closeIndex,
515
    attrExpPresent: attrExpPresent,
516
  }
517
}
518
/**
519
 * find paired tag for a stop node
520
 * @param {string} xmlData 
521
 * @param {string} tagName 
522
 * @param {number} i 
523
 */
524
function readStopNodeData(xmlData, tagName, i){
525
  const startIndex = i;
20✔
526
  // Starting at 1 since we already have an open tag
527
  let openTagCount = 1;
20✔
528

529
  for (; i < xmlData.length; i++) {
20✔
530
    if( xmlData[i] === "<"){ 
896✔
531
      if (xmlData[i+1] === "/") {//close tag
72✔
532
          const closeIndex = findClosingIndex(xmlData, ">", i, `${tagName} is not closed`);
44✔
533
          let closeTagName = xmlData.substring(i+2,closeIndex).trim();
44✔
534
          if(closeTagName === tagName){
44✔
535
            openTagCount--;
21✔
536
            if (openTagCount === 0) {
21✔
537
              return {
20✔
538
                tagContent: xmlData.substring(startIndex, i),
539
                i : closeIndex
540
              }
541
            }
542
          }
543
          i=closeIndex;
24✔
544
        } else if(xmlData[i+1] === '?') { 
28!
545
          const closeIndex = findClosingIndex(xmlData, "?>", i+1, "StopNode is not closed.")
×
546
          i=closeIndex;
×
547
        } else if(xmlData.substr(i + 1, 3) === '!--') { 
28✔
548
          const closeIndex = findClosingIndex(xmlData, "-->", i+3, "StopNode is not closed.")
1✔
549
          i=closeIndex;
1✔
550
        } else if(xmlData.substr(i + 1, 2) === '![') { 
27✔
551
          const closeIndex = findClosingIndex(xmlData, "]]>", i, "StopNode is not closed.") - 2;
1✔
552
          i=closeIndex;
1✔
553
        } else {
554
          const tagData = readTagExp(xmlData, i, '>')
26✔
555

556
          if (tagData) {
26✔
557
            const openTagName = tagData && tagData.tagName;
25✔
558
            if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
25✔
559
              openTagCount++;
1✔
560
            }
561
            i=tagData.closeIndex;
25✔
562
          }
563
        }
564
      }
565
  }//end for loop
566
}
567

568
function parseValue(val, shouldParse, options) {
569
  if (shouldParse && typeof val === 'string') {
583✔
570
    //console.log(options)
571
    const newval = val.trim();
399✔
572
    if(newval === 'true' ) return true;
399✔
573
    else if(newval === 'false' ) return false;
392✔
574
    else return toNumber(val, options);
389✔
575
  } else {
576
    if (util.isExist(val)) {
184!
577
      return val;
184✔
578
    } else {
579
      return '';
×
580
    }
581
  }
582
}
583

584

585
module.exports = OrderedObjParser;
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc