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

NaturalIntelligence / fast-xml-parser / 4649444305

pending completion
4649444305

push

github

amit kumar gupta
update package detail

745 of 802 branches covered (92.89%)

2485 of 2531 relevant lines covered (98.18%)

187479.5 hits per line

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

96.16
/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;
147✔
19
    this.currentNode = null;
147✔
20
    this.tagsNodeStack = [];
147✔
21
    this.docTypeEntities = {};
147✔
22
    this.lastEntities = {
147✔
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 : "&"};
147✔
29
    this.htmlEntities = {
147✔
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;
147✔
45
    this.parseXml = parseXml;
147✔
46
    this.parseTextData = parseTextData;
147✔
47
    this.resolveNameSpace = resolveNameSpace;
147✔
48
    this.buildAttributesMap = buildAttributesMap;
147✔
49
    this.isItStopNode = isItStopNode;
147✔
50
    this.replaceEntitiesValue = replaceEntitiesValue;
147✔
51
    this.readStopNodeData = readStopNodeData;
147✔
52
    this.saveTextToParentTag = saveTextToParentTag;
147✔
53
    this.addChild = addChild;
147✔
54
  }
55

56
}
57

58
function addExternalEntities(externalEntities){
59
  const entKeys = Object.keys(externalEntities);
147✔
60
  for (let i = 0; i < entKeys.length; i++) {
147✔
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) {
989!
80
    if (this.options.trimValues && !dontTrim) {
989✔
81
      val = val.trim();
925✔
82
    }
83
    if(val.length > 0){
989✔
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') {
200✔
127
    // attrStr = attrStr.replace(/\r?\n/g, ' ');
128
    //attrStr = attrStr || attrStr.trim();
129

130
    const matches = util.getAllMatches(attrStr, attrsRegx);
167✔
131
    const len = matches.length; //don't make it inline
167✔
132
    const attrs = {};
167✔
133
    for (let i = 0; i < len; i++) {
167✔
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) {
167✔
168
      return;
14✔
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
147✔
181
  const xmlObj = new xmlNode('!xml');
147✔
182
  let currentNode = xmlObj;
147✔
183
  let textData = "";
147✔
184
  let jPath = "";
147✔
185
  for(let i=0; i< xmlData.length; i++){//for each char in XML data
147✔
186
    const ch = xmlData[i];
11,506✔
187
    if(ch === '<'){
11,506✔
188
      // const nextIndex = i+1;
189
      // const _2ndChar = xmlData[nextIndex];
190
      if( xmlData[i+1] === '/') {//Closing Tag
1,271✔
191
        const closeIndex = findClosingIndex(xmlData, ">", i, "Closing Tag is not closed.")
534✔
192
        let tagName = xmlData.substring(i+2,closeIndex).trim();
533✔
193

194
        if(this.options.removeNSPrefix){
533✔
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) {
533✔
202
          tagName = this.options.transformTagName(tagName);
10✔
203
        }
204

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

209
        jPath = jPath.substr(0, jPath.lastIndexOf("."));
533✔
210
        
211
        currentNode = this.tagsNodeStack.pop();//avoid recursion, set the parent tag scope
533✔
212
        textData = "";
533✔
213
        i = closeIndex;
533✔
214
      } else if( xmlData[i+1] === '?') {
737✔
215

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

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

222
        }else{
223
  
224
          const childNode = new xmlNode(tagData.tagName);
37✔
225
          childNode.add(this.options.textNodeName, "");
37✔
226
          
227
          if(tagData.tagName !== tagData.tagExp && tagData.attrExpPresent){
37✔
228
            childNode[":@"] = this.buildAttributesMap(tagData.tagExp, jPath, tagData.tagName);
35✔
229
          }
230
          this.addChild(currentNode, childNode, jPath)
37✔
231

232
        }
233

234

235
        i = tagData.closeIndex + 1;
42✔
236
      } else if(xmlData.substr(i + 1, 3) === '!--') {
693✔
237
        const endIndex = findClosingIndex(xmlData, "-->", i+4, "Comment is not closed.")
12✔
238
        if(this.options.commentPropName){
11✔
239
          const comment = xmlData.substring(i + 4, endIndex - 2);
5✔
240

241
          textData = this.saveTextToParentTag(textData, currentNode, jPath);
5✔
242

243
          currentNode.add(this.options.commentPropName, [ { [this.options.textNodeName] : comment } ]);
5✔
244
        }
245
        i = endIndex;
11✔
246
      } else if( xmlData.substr(i + 1, 2) === '!D') {
681✔
247
        const result = readDocType(xmlData, i);
11✔
248
        this.docTypeEntities = result.entities;
10✔
249
        i = result.i;
10✔
250
      }else if(xmlData.substr(i + 1, 2) === '![') {
670✔
251
        const closeIndex = findClosingIndex(xmlData, "]]>", i, "CDATA is not closed.") - 2;
50✔
252
        const tagExp = xmlData.substring(i + 9,closeIndex);
49✔
253

254
        textData = this.saveTextToParentTag(textData, currentNode, jPath);
49✔
255

256
        //cdata should be set even if it is 0 length string
257
        if(this.options.cdataPropName){
49✔
258
          // let val = this.parseTextData(tagExp, this.options.cdataPropName, jPath + "." + this.options.cdataPropName, true, false, true);
259
          // if(!val) val = "";
260
          currentNode.add(this.options.cdataPropName, [ { [this.options.textNodeName] : tagExp } ]);
13✔
261
        }else{
262
          let val = this.parseTextData(tagExp, currentNode.tagname, jPath, true, false, true);
36✔
263
          if(val == undefined) val = "";
36✔
264
          currentNode.add(this.options.textNodeName, val);
36✔
265
        }
266
        
267
        i = closeIndex + 2;
49✔
268
      }else {//Opening tag
269
        let result = readTagExp(xmlData,i, this.options.removeNSPrefix);
620✔
270
        let tagName= result.tagName;
620✔
271
        let tagExp = result.tagExp;
620✔
272
        let attrExpPresent = result.attrExpPresent;
620✔
273
        let closeIndex = result.closeIndex;
620✔
274

275
        if (this.options.transformTagName) {
620✔
276
          tagName = this.options.transformTagName(tagName);
10✔
277
        }
278
        
279
        //save text as child node
280
        if (currentNode && textData) {
620✔
281
          if(currentNode.tagname !== '!xml'){
465✔
282
            //when nested tag is found
283
            textData = this.saveTextToParentTag(textData, currentNode, jPath, false);
425✔
284
          }
285
        }
286

287
        if(tagName !== xmlObj.tagname){
620!
288
          jPath += jPath ? "." + tagName : tagName;
620✔
289
        }
290

291
        //check if last tag was unpaired tag
292
        const lastTag = currentNode;
620✔
293
        if(lastTag && this.options.unpairedTags.indexOf(lastTag.tagname) !== -1 ){
620✔
294
          currentNode = this.tagsNodeStack.pop();
16✔
295
        }
296

297
        if (this.isItStopNode(this.options.stopNodes, jPath, tagName)) { //TODO: namespace
620✔
298
          let tagContent = "";
22✔
299
          //self-closing tag
300
          if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
22✔
301
            i = result.closeIndex;
1✔
302
          }
303
          //boolean tag
304
          else if(this.options.unpairedTags.indexOf(tagName) !== -1){
21✔
305
            i = result.closeIndex;
1✔
306
          }
307
          //normal tag
308
          else{
309
            //read until closing tag is found
310
            const result = this.readStopNodeData(xmlData, tagName, closeIndex + 1);
20✔
311
            if(!result) throw new Error(`Unexpected end of ${tagName}`);
20!
312
            i = result.i;
20✔
313
            tagContent = result.tagContent;
20✔
314
          }
315

316
          const childNode = new xmlNode(tagName);
22✔
317
          if(tagName !== tagExp && attrExpPresent){
22✔
318
            childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
8✔
319
          }
320
          if(tagContent) {
22✔
321
            tagContent = this.parseTextData(tagContent, tagName, jPath, true, attrExpPresent, true, true);
16✔
322
          }
323
          
324
          jPath = jPath.substr(0, jPath.lastIndexOf("."));
22✔
325
          childNode.add(this.options.textNodeName, tagContent);
22✔
326
          
327
          this.addChild(currentNode, childNode, jPath)
22✔
328
        }else{
329
  //selfClosing tag
330
          if(tagExp.length > 0 && tagExp.lastIndexOf("/") === tagExp.length - 1){
598✔
331
            if(tagName[tagName.length - 1] === "/"){ //remove trailing '/'
36✔
332
              tagName = tagName.substr(0, tagName.length - 1);
9✔
333
              tagExp = tagName;
9✔
334
            }else{
335
              tagExp = tagExp.substr(0, tagExp.length - 1);
27✔
336
            }
337
            
338
            if(this.options.transformTagName) {
36!
339
              tagName = this.options.transformTagName(tagName);
×
340
            }
341

342
            const childNode = new xmlNode(tagName);
36✔
343
            if(tagName !== tagExp && attrExpPresent){
36✔
344
              childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
27✔
345
            }
346
            jPath = jPath.substr(0, jPath.lastIndexOf("."));
36✔
347
            this.addChild(currentNode, childNode, jPath)
36✔
348
          }
349
    //opening tag
350
          else{
351
            const childNode = new xmlNode( tagName);
562✔
352
            this.tagsNodeStack.push(currentNode);
562✔
353
            
354
            if(tagName !== tagExp && attrExpPresent){
562✔
355
              childNode[":@"] = this.buildAttributesMap(tagExp, jPath, tagName);
130✔
356
            }
357
            this.addChild(currentNode, childNode, jPath)
562✔
358
            currentNode = childNode;
562✔
359
          }
360
          textData = "";
598✔
361
          i = closeIndex;
598✔
362
        }
363
      }
364
    }else{
365
      textData += xmlData[i];
10,235✔
366
    }
367
  }
368
  return xmlObj.child;
141✔
369
}
370

371
function addChild(currentNode, childNode, jPath){
372
  const result = this.options.updateTag(childNode.tagname, jPath, childNode[":@"])
657✔
373
  if(result === false){
657✔
374
  }else if(typeof result === "string"){
654!
375
    childNode.tagname = result
654✔
376
    currentNode.addChild(childNode);
654✔
377
  }else{
378
    currentNode.addChild(childNode);
×
379
  }
380
}
381

382
const replaceEntitiesValue = function(val){
1✔
383

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

414
    if (textData !== undefined && textData !== "")
937✔
415
      currentNode.add(this.options.textNodeName, textData);
334✔
416
    textData = "";
937✔
417
  }
418
  return textData;
1,054✔
419
}
420

421
//TODO: use jPath to simplify the logic
422
/**
423
 * 
424
 * @param {string[]} stopNodes 
425
 * @param {string} jPath
426
 * @param {string} currentTagName 
427
 */
428
function isItStopNode(stopNodes, jPath, currentTagName){
429
  const allNodesExp = "*." + currentTagName;
620✔
430
  for (const stopNodePath in stopNodes) {
620✔
431
    const stopNodeExp = stopNodes[stopNodePath];
97✔
432
    if( allNodesExp === stopNodeExp || jPath === stopNodeExp  ) return true;
97✔
433
  }
434
  return false;
598✔
435
}
436

437
/**
438
 * Returns the tag Expression and where it is ending handling single-double quotes situation
439
 * @param {string} xmlData 
440
 * @param {number} i starting index
441
 * @returns 
442
 */
443
function tagExpWithClosingIndex(xmlData, i, closingChar = ">"){
×
444
  let attrBoundary;
445
  let tagExp = "";
690✔
446
  for (let index = i; index < xmlData.length; index++) {
690✔
447
    let ch = xmlData[index];
9,381✔
448
    if (attrBoundary) {
9,381✔
449
        if (ch === attrBoundary) attrBoundary = "";//reset
2,473✔
450
    } else if (ch === '"' || ch === "'") {
6,908✔
451
        attrBoundary = ch;
270✔
452
    } else if (ch === closingChar[0]) {
6,638✔
453
      if(closingChar[1]){
731✔
454
        if(xmlData[index + 1] === closingChar[1]){
86✔
455
          return {
42✔
456
            data: tagExp,
457
            index: index
458
          }
459
        }
460
      }else{
461
        return {
645✔
462
          data: tagExp,
463
          index: index
464
        }
465
      }
466
    } else if (ch === '\t') {
5,907✔
467
      ch = " "
4✔
468
    }
469
    tagExp += ch;
8,694✔
470
  }
471
}
472

473
function findClosingIndex(xmlData, str, i, errMsg){
474
  const closingIndex = xmlData.indexOf(str, i);
642✔
475
  if(closingIndex === -1){
642✔
476
    throw new Error(errMsg)
3✔
477
  }else{
478
    return closingIndex + str.length - 1;
639✔
479
  }
480
}
481

482
function readTagExp(xmlData,i, removeNSPrefix, closingChar = ">"){
646✔
483
  const result = tagExpWithClosingIndex(xmlData, i+1, closingChar);
690✔
484
  if(!result) return;
690✔
485
  let tagExp = result.data;
687✔
486
  const closeIndex = result.index;
687✔
487
  const separatorIndex = tagExp.search(/\s/);
687✔
488
  let tagName = tagExp;
687✔
489
  let attrExpPresent = true;
687✔
490
  if(separatorIndex !== -1){//separate tag name and attributes expression
687✔
491
    tagName = tagExp.substr(0, separatorIndex).replace(/\s\s*$/, '');
212✔
492
    tagExp = tagExp.substr(separatorIndex + 1);
212✔
493
  }
494

495
  if(removeNSPrefix){
687✔
496
    const colonIndex = tagName.indexOf(":");
48✔
497
    if(colonIndex !== -1){
48✔
498
      tagName = tagName.substr(colonIndex+1);
12✔
499
      attrExpPresent = tagName !== result.data.substr(colonIndex + 1);
12✔
500
    }
501
  }
502

503
  return {
687✔
504
    tagName: tagName,
505
    tagExp: tagExp,
506
    closeIndex: closeIndex,
507
    attrExpPresent: attrExpPresent,
508
  }
509
}
510
/**
511
 * find paired tag for a stop node
512
 * @param {string} xmlData 
513
 * @param {string} tagName 
514
 * @param {number} i 
515
 */
516
function readStopNodeData(xmlData, tagName, i){
517
  const startIndex = i;
20✔
518
  // Starting at 1 since we already have an open tag
519
  let openTagCount = 1;
20✔
520

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

548
          if (tagData) {
26✔
549
            const openTagName = tagData && tagData.tagName;
25✔
550
            if (openTagName === tagName && tagData.tagExp[tagData.tagExp.length-1] !== "/") {
25✔
551
              openTagCount++;
1✔
552
            }
553
            i=tagData.closeIndex;
25✔
554
          }
555
        }
556
      }
557
  }//end for loop
558
}
559

560
function parseValue(val, shouldParse, options) {
561
  if (shouldParse && typeof val === 'string') {
583✔
562
    //console.log(options)
563
    const newval = val.trim();
399✔
564
    if(newval === 'true' ) return true;
399✔
565
    else if(newval === 'false' ) return false;
392✔
566
    else return toNumber(val, options);
389✔
567
  } else {
568
    if (util.isExist(val)) {
184!
569
      return val;
184✔
570
    } else {
571
      return '';
×
572
    }
573
  }
574
}
575

576

577
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