node-red-contrib-tak-registration 0.7.2 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +1 -1
  2. package/node_modules/fast-xml-parser/CHANGELOG.md +561 -0
  3. package/node_modules/fast-xml-parser/README.md +134 -287
  4. package/node_modules/fast-xml-parser/package.json +17 -40
  5. package/node_modules/fast-xml-parser/{cli.js → src/cli/cli.js} +16 -24
  6. package/node_modules/fast-xml-parser/src/cli/man.js +12 -0
  7. package/node_modules/fast-xml-parser/src/fxp.d.ts +108 -0
  8. package/node_modules/fast-xml-parser/src/fxp.js +11 -0
  9. package/node_modules/fast-xml-parser/src/util.js +0 -36
  10. package/node_modules/fast-xml-parser/src/validator.js +15 -5
  11. package/node_modules/fast-xml-parser/src/xmlbuilder/json2xml.js +269 -0
  12. package/node_modules/fast-xml-parser/src/xmlbuilder/orderedJs2Xml.js +131 -0
  13. package/node_modules/fast-xml-parser/src/xmlbuilder/prettifyJs2Xml.js +0 -0
  14. package/node_modules/fast-xml-parser/src/xmlparser/DocTypeReader.js +152 -0
  15. package/node_modules/fast-xml-parser/src/xmlparser/OptionsBuilder.js +48 -0
  16. package/node_modules/fast-xml-parser/src/xmlparser/OrderedObjParser.js +589 -0
  17. package/node_modules/fast-xml-parser/src/xmlparser/XMLParser.js +58 -0
  18. package/node_modules/fast-xml-parser/src/xmlparser/node2json.js +113 -0
  19. package/node_modules/fast-xml-parser/src/xmlparser/xmlNode.js +25 -0
  20. package/package.json +2 -2
  21. package/tak-ingest.js +3 -2
  22. package/node_modules/fast-xml-parser/src/json2xml.js +0 -280
  23. package/node_modules/fast-xml-parser/src/nimndata.js +0 -144
  24. package/node_modules/fast-xml-parser/src/node2json.js +0 -42
  25. package/node_modules/fast-xml-parser/src/node2json_str.js +0 -63
  26. package/node_modules/fast-xml-parser/src/parser.d.ts +0 -79
  27. package/node_modules/fast-xml-parser/src/parser.js +0 -76
  28. package/node_modules/fast-xml-parser/src/xmlNode.js +0 -17
  29. package/node_modules/fast-xml-parser/src/xmlstr2xmlnode.js +0 -339
  30. /package/node_modules/fast-xml-parser/src/{read.js → cli/read.js} +0 -0
@@ -3,26 +3,19 @@
3
3
  /*eslint-disable no-console*/
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
- const parser = require('./src/parser');
7
- const readToEnd = require('./src/read').readToEnd;
6
+ const {XMLParser, XMLValidator} = require("../fxp");
7
+ const readToEnd = require('./read').readToEnd;
8
8
 
9
+ const version = require('./../../package.json').version;
9
10
  if (process.argv[2] === '--help' || process.argv[2] === '-h') {
10
- console.log('Fast XML Parser ' + require(path.join(__dirname + '/package.json')).version);
11
- console.log('----------------');
12
- console.log('xml2js [-ns|-a|-c|-v|-V] <filename> [-o outputfile.json]');
13
- console.log('cat xmlfile.xml | xml2js [-ns|-a|-c|-v|-V] [-o outputfile.json]');
14
- console.log('-ns: remove namespace from tag and atrribute name.');
15
- console.log("-a: don't parse attributes.");
16
- console.log('-c: parse values to premitive type.');
17
- console.log('-v: validate before parsing.');
18
- console.log('-V: validate only.');
11
+ console.log(require("./man"));
19
12
  } else if (process.argv[2] === '--version') {
20
- console.log(require(path.join(__dirname + '/package.json')).version);
13
+ console.log(version);
21
14
  } else {
22
15
  const options = {
23
- ignoreNameSpace: true,
16
+ removeNSPrefix: true,
24
17
  ignoreAttributes: false,
25
- parseNodeValue: true,
18
+ parseTagValue: true,
26
19
  parseAttributeValue: true,
27
20
  };
28
21
  let fileName = '';
@@ -31,11 +24,11 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
31
24
  let validateOnly = false;
32
25
  for (let i = 2; i < process.argv.length; i++) {
33
26
  if (process.argv[i] === '-ns') {
34
- options.ignoreNameSpace = false;
27
+ options.removeNSPrefix = false;
35
28
  } else if (process.argv[i] === '-a') {
36
29
  options.ignoreAttributes = true;
37
30
  } else if (process.argv[i] === '-c') {
38
- options.parseNodeValue = false;
31
+ options.parseTagValue = false;
39
32
  options.parseAttributeValue = false;
40
33
  } else if (process.argv[i] === '-o') {
41
34
  outputFileName = process.argv[++i];
@@ -48,20 +41,18 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
48
41
  fileName = process.argv[i];
49
42
  }
50
43
  }
44
+
51
45
  const callback = function(xmlData) {
52
46
  let output = '';
53
47
  if (validate) {
54
- const result = parser.validate(xmlData);
55
- if (result === true) {
56
- output = JSON.stringify(parser.parse(xmlData, options), null, 4);
57
- } else {
58
- output = result;
59
- }
48
+ const parser = new XMLParser(options);
49
+ output = parser.parse(xmlData,validate);
60
50
  } else if (validateOnly) {
61
- output = parser.validate(xmlData);
51
+ output = XMLValidator.validate(xmlData);
62
52
  process.exitCode = output === true ? 0 : 1;
63
53
  } else {
64
- output = JSON.stringify(parser.parse(xmlData, options), null, 4);
54
+ const parser = new XMLParser(options);
55
+ output = JSON.stringify(parser.parse(xmlData,validate), null, 4);
65
56
  }
66
57
  if (outputFileName) {
67
58
  writeToFile(outputFileName, output);
@@ -71,6 +62,7 @@ if (process.argv[2] === '--help' || process.argv[2] === '-h') {
71
62
  };
72
63
 
73
64
  try {
65
+
74
66
  if (!fileName) {
75
67
  readToEnd(process.stdin, function(err, data) {
76
68
  if (err) {
@@ -0,0 +1,12 @@
1
+ module.exports = `Fast XML Parser 4.0.0
2
+ ----------------
3
+ $ fxparser [-ns|-a|-c|-v|-V] <filename> [-o outputfile.json]
4
+ $ cat xmlfile.xml | fxparser [-ns|-a|-c|-v|-V] [-o outputfile.json]
5
+
6
+ Options
7
+ ----------------
8
+ -ns: remove namespace from tag and atrribute name.
9
+ -a: don't parse attributes.
10
+ -c: parse values to premitive type.
11
+ -v: validate before parsing.
12
+ -V: validate only.`
@@ -0,0 +1,108 @@
1
+ type X2jOptions = {
2
+ preserveOrder: boolean;
3
+ attributeNamePrefix: string;
4
+ attributesGroupName: false | string;
5
+ textNodeName: string;
6
+ ignoreAttributes: boolean;
7
+ removeNSPrefix: boolean;
8
+ allowBooleanAttributes: boolean;
9
+ parseTagValue: boolean;
10
+ parseAttributeValue: boolean;
11
+ trimValues: boolean;
12
+ cdataPropName: false | string;
13
+ commentPropName: false | string;
14
+ /**
15
+ Control how tag value should be parsed. Called only if tag value is not empty
16
+
17
+ @returns {undefined|null} `undefined` or `null` to set original value.
18
+ @returns {unknown}
19
+ 1. Different value or value with different data type to set new value. <br>
20
+ 2. Same value to set parsed value if `parseTagValue: true`.
21
+ */
22
+ tagValueProcessor: (tagName: string, tagValue: string, jPath: string, hasAttributes: boolean, isLeafNode: boolean) => unknown;
23
+ attributeValueProcessor: (attrName: string, attrValue: string, jPath: string) => unknown;
24
+ numberParseOptions: strnumOptions;
25
+ stopNodes: string[];
26
+ unpairedTags: string[];
27
+ alwaysCreateTextNode: boolean;
28
+ isArray: (tagName: string, jPath: string, isLeafNode: boolean, isAttribute: boolean) => boolean;
29
+ processEntities: boolean;
30
+ htmlEntities: boolean;
31
+ ignoreDeclaration: boolean;
32
+ ignorePiTags: boolean;
33
+ transformTagName: ((tagName: string) => string) | false;
34
+ transformAttributeName: ((attributeName: string) => string) | false;
35
+ /**
36
+ Change the tag name when a different name is returned. Skip the tag from parsed result when false is returned.
37
+ Modify `attrs` object to control attributes for the given tag.
38
+
39
+ @returns {string} new tag name.
40
+ @returns false to skip the tag
41
+ */
42
+ updateTag: (tagName: string, jPath: string, attrs: {[k: string]: string}) => string | boolean;
43
+ };
44
+ type strnumOptions = {
45
+ hex: boolean;
46
+ leadingZeros: boolean,
47
+ skipLike?: RegExp,
48
+ eNotation?: boolean
49
+ }
50
+ type X2jOptionsOptional = Partial<X2jOptions>;
51
+ type validationOptions = {
52
+ allowBooleanAttributes: boolean;
53
+ unpairedTags: string[];
54
+ };
55
+ type validationOptionsOptional = Partial<validationOptions>;
56
+
57
+ type XmlBuilderOptions = {
58
+ attributeNamePrefix: string;
59
+ attributesGroupName: false | string;
60
+ textNodeName: string;
61
+ ignoreAttributes: boolean;
62
+ cdataPropName: false | string;
63
+ commentPropName: false | string;
64
+ format: boolean;
65
+ indentBy: string;
66
+ arrayNodeName: string;
67
+ suppressEmptyNode: boolean;
68
+ suppressUnpairedNode: boolean;
69
+ suppressBooleanAttributes: boolean;
70
+ preserveOrder: boolean;
71
+ unpairedTags: string[];
72
+ stopNodes: string[];
73
+ tagValueProcessor: (name: string, value: unknown) => string;
74
+ attributeValueProcessor: (name: string, value: unknown) => string;
75
+ processEntities: boolean;
76
+ oneListGroup: boolean;
77
+ };
78
+ type XmlBuilderOptionsOptional = Partial<XmlBuilderOptions>;
79
+
80
+ type ESchema = string | object | Array<string|object>;
81
+
82
+ type ValidationError = {
83
+ err: {
84
+ code: string;
85
+ msg: string,
86
+ line: number,
87
+ col: number
88
+ };
89
+ };
90
+
91
+ export class XMLParser {
92
+ constructor(options?: X2jOptionsOptional);
93
+ parse(xmlData: string | Buffer ,validationOptions?: validationOptionsOptional | boolean): any;
94
+ /**
95
+ * Add Entity which is not by default supported by this library
96
+ * @param entityIndentifier {string} Eg: 'ent' for &ent;
97
+ * @param entityValue {string} Eg: '\r'
98
+ */
99
+ addEntity(entityIndentifier: string, entityValue: string): void;
100
+ }
101
+
102
+ export class XMLValidator{
103
+ static validate( xmlData: string, options?: validationOptionsOptional): true | ValidationError;
104
+ }
105
+ export class XMLBuilder {
106
+ constructor(options?: XmlBuilderOptionsOptional);
107
+ build(jObj: any): any;
108
+ }
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ const validator = require('./validator');
4
+ const XMLParser = require('./xmlparser/XMLParser');
5
+ const XMLBuilder = require('./xmlbuilder/json2xml');
6
+
7
+ module.exports = {
8
+ XMLParser: XMLParser,
9
+ XMLValidator: validator,
10
+ XMLBuilder: XMLBuilder
11
+ }
@@ -67,42 +67,6 @@ exports.getValue = function(v) {
67
67
  // const fakeCall = function(a) {return a;};
68
68
  // const fakeCallNoReturn = function() {};
69
69
 
70
- exports.buildOptions = function(options, defaultOptions, props) {
71
- let newOptions = {};
72
- if (!options) {
73
- return defaultOptions; //if there are not options
74
- }
75
-
76
- for (let i = 0; i < props.length; i++) {
77
- if (options[props[i]] !== undefined) {
78
- newOptions[props[i]] = options[props[i]];
79
- } else {
80
- newOptions[props[i]] = defaultOptions[props[i]];
81
- }
82
- }
83
- return newOptions;
84
- };
85
-
86
- /**
87
- * Check if a tag name should be treated as array
88
- *
89
- * @param tagName the node tagname
90
- * @param arrayMode the array mode option
91
- * @param parentTagName the parent tag name
92
- * @returns {boolean} true if node should be parsed as array
93
- */
94
- exports.isTagNameInArrayMode = function (tagName, arrayMode, parentTagName) {
95
- if (arrayMode === false) {
96
- return false;
97
- } else if (arrayMode instanceof RegExp) {
98
- return arrayMode.test(tagName);
99
- } else if (typeof arrayMode === 'function') {
100
- return !!arrayMode(tagName, parentTagName);
101
- }
102
-
103
- return arrayMode === "strict";
104
- }
105
-
106
70
  exports.isName = isName;
107
71
  exports.getAllMatches = getAllMatches;
108
72
  exports.nameRegexp = nameRegexp;
@@ -4,13 +4,12 @@ const util = require('./util');
4
4
 
5
5
  const defaultOptions = {
6
6
  allowBooleanAttributes: false, //A tag can have attributes without any value
7
+ unpairedTags: []
7
8
  };
8
9
 
9
- const props = ['allowBooleanAttributes'];
10
-
11
10
  //const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
12
11
  exports.validate = function (xmlData, options) {
13
- options = util.buildOptions(options, defaultOptions, props);
12
+ options = Object.assign({}, defaultOptions, options);
14
13
 
15
14
  //xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
16
15
  //xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
@@ -25,7 +24,7 @@ exports.validate = function (xmlData, options) {
25
24
  // check for byte order mark (BOM)
26
25
  xmlData = xmlData.substr(1);
27
26
  }
28
-
27
+
29
28
  for (let i = 0; i < xmlData.length; i++) {
30
29
 
31
30
  if (xmlData[i] === '<' && xmlData[i+1] === '?') {
@@ -130,6 +129,8 @@ exports.validate = function (xmlData, options) {
130
129
  //if the root level has been reached before ...
131
130
  if (reachedRoot === true) {
132
131
  return getErrorObject('InvalidXml', 'Multiple possible root nodes found.', getLineNumberForPosition(xmlData, i));
132
+ } else if(options.unpairedTags.indexOf(tagName) !== -1){
133
+ //don't push into stack
133
134
  } else {
134
135
  tags.push({tagName, tagStartPos});
135
136
  }
@@ -156,6 +157,10 @@ exports.validate = function (xmlData, options) {
156
157
  if (afterAmp == -1)
157
158
  return getErrorObject('InvalidChar', "char '&' is not expected.", getLineNumberForPosition(xmlData, i));
158
159
  i = afterAmp;
160
+ }else{
161
+ if (reachedRoot === true && !isWhiteSpace(xmlData[i])) {
162
+ return getErrorObject('InvalidXml', "Extra text at the end", getLineNumberForPosition(xmlData, i));
163
+ }
159
164
  }
160
165
  } //end of reading tag text value
161
166
  if (xmlData[i] === '<') {
@@ -163,7 +168,7 @@ exports.validate = function (xmlData, options) {
163
168
  }
164
169
  }
165
170
  } else {
166
- if (xmlData[i] === ' ' || xmlData[i] === '\t' || xmlData[i] === '\n' || xmlData[i] === '\r') {
171
+ if ( isWhiteSpace(xmlData[i])) {
167
172
  continue;
168
173
  }
169
174
  return getErrorObject('InvalidChar', "char '"+xmlData[i]+"' is not expected.", getLineNumberForPosition(xmlData, i));
@@ -183,6 +188,9 @@ exports.validate = function (xmlData, options) {
183
188
  return true;
184
189
  };
185
190
 
191
+ function isWhiteSpace(char){
192
+ return char === ' ' || char === '\t' || char === '\n' || char === '\r';
193
+ }
186
194
  /**
187
195
  * Read Processing insstructions and skip
188
196
  * @param {*} xmlData
@@ -318,6 +326,8 @@ function validateAttributeString(attrStr, options) {
318
326
  if (matches[i][1].length === 0) {
319
327
  //nospace before attribute name: a="sd"b="saf"
320
328
  return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' has no space in starting.", getPositionFromMatch(matches[i]))
329
+ } else if (matches[i][3] !== undefined && matches[i][4] === undefined) {
330
+ return getErrorObject('InvalidAttr', "Attribute '"+matches[i][2]+"' is without value.", getPositionFromMatch(matches[i]));
321
331
  } else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
322
332
  //independent attribute: ab
323
333
  return getErrorObject('InvalidAttr', "boolean attribute '"+matches[i][2]+"' is not allowed.", getPositionFromMatch(matches[i]));
@@ -0,0 +1,269 @@
1
+ 'use strict';
2
+ //parse Empty Node as self closing node
3
+ const buildFromOrderedJs = require('./orderedJs2Xml');
4
+
5
+ const defaultOptions = {
6
+ attributeNamePrefix: '@_',
7
+ attributesGroupName: false,
8
+ textNodeName: '#text',
9
+ ignoreAttributes: true,
10
+ cdataPropName: false,
11
+ format: false,
12
+ indentBy: ' ',
13
+ suppressEmptyNode: false,
14
+ suppressUnpairedNode: true,
15
+ suppressBooleanAttributes: true,
16
+ tagValueProcessor: function(key, a) {
17
+ return a;
18
+ },
19
+ attributeValueProcessor: function(attrName, a) {
20
+ return a;
21
+ },
22
+ preserveOrder: false,
23
+ commentPropName: false,
24
+ unpairedTags: [],
25
+ entities: [
26
+ { regex: new RegExp("&", "g"), val: "&amp;" },//it must be on top
27
+ { regex: new RegExp(">", "g"), val: "&gt;" },
28
+ { regex: new RegExp("<", "g"), val: "&lt;" },
29
+ { regex: new RegExp("\'", "g"), val: "&apos;" },
30
+ { regex: new RegExp("\"", "g"), val: "&quot;" }
31
+ ],
32
+ processEntities: true,
33
+ stopNodes: [],
34
+ // transformTagName: false,
35
+ // transformAttributeName: false,
36
+ oneListGroup: false
37
+ };
38
+
39
+ function Builder(options) {
40
+ this.options = Object.assign({}, defaultOptions, options);
41
+ if (this.options.ignoreAttributes || this.options.attributesGroupName) {
42
+ this.isAttribute = function(/*a*/) {
43
+ return false;
44
+ };
45
+ } else {
46
+ this.attrPrefixLen = this.options.attributeNamePrefix.length;
47
+ this.isAttribute = isAttribute;
48
+ }
49
+
50
+ this.processTextOrObjNode = processTextOrObjNode
51
+
52
+ if (this.options.format) {
53
+ this.indentate = indentate;
54
+ this.tagEndChar = '>\n';
55
+ this.newLine = '\n';
56
+ } else {
57
+ this.indentate = function() {
58
+ return '';
59
+ };
60
+ this.tagEndChar = '>';
61
+ this.newLine = '';
62
+ }
63
+ }
64
+
65
+ Builder.prototype.build = function(jObj) {
66
+ if(this.options.preserveOrder){
67
+ return buildFromOrderedJs(jObj, this.options);
68
+ }else {
69
+ if(Array.isArray(jObj) && this.options.arrayNodeName && this.options.arrayNodeName.length > 1){
70
+ jObj = {
71
+ [this.options.arrayNodeName] : jObj
72
+ }
73
+ }
74
+ return this.j2x(jObj, 0).val;
75
+ }
76
+ };
77
+
78
+ Builder.prototype.j2x = function(jObj, level) {
79
+ let attrStr = '';
80
+ let val = '';
81
+ for (let key in jObj) {
82
+ if (typeof jObj[key] === 'undefined') {
83
+ // supress undefined node only if it is not an attribute
84
+ if (this.isAttribute(key)) {
85
+ val += '';
86
+ }
87
+ } else if (jObj[key] === null) {
88
+ // null attribute should be ignored by the attribute list, but should not cause the tag closing
89
+ if (this.isAttribute(key)) {
90
+ val += '';
91
+ } else if (key[0] === '?') {
92
+ val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
93
+ } else {
94
+ val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
95
+ }
96
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
97
+ } else if (jObj[key] instanceof Date) {
98
+ val += this.buildTextValNode(jObj[key], key, '', level);
99
+ } else if (typeof jObj[key] !== 'object') {
100
+ //premitive type
101
+ const attr = this.isAttribute(key);
102
+ if (attr) {
103
+ attrStr += this.buildAttrPairStr(attr, '' + jObj[key]);
104
+ }else {
105
+ //tag value
106
+ if (key === this.options.textNodeName) {
107
+ let newval = this.options.tagValueProcessor(key, '' + jObj[key]);
108
+ val += this.replaceEntitiesValue(newval);
109
+ } else {
110
+ val += this.buildTextValNode(jObj[key], key, '', level);
111
+ }
112
+ }
113
+ } else if (Array.isArray(jObj[key])) {
114
+ //repeated nodes
115
+ const arrLen = jObj[key].length;
116
+ let listTagVal = "";
117
+ for (let j = 0; j < arrLen; j++) {
118
+ const item = jObj[key][j];
119
+ if (typeof item === 'undefined') {
120
+ // supress undefined node
121
+ } else if (item === null) {
122
+ if(key[0] === "?") val += this.indentate(level) + '<' + key + '?' + this.tagEndChar;
123
+ else val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
124
+ // val += this.indentate(level) + '<' + key + '/' + this.tagEndChar;
125
+ } else if (typeof item === 'object') {
126
+ if(this.options.oneListGroup ){
127
+ listTagVal += this.j2x(item, level + 1).val;
128
+ }else{
129
+ listTagVal += this.processTextOrObjNode(item, key, level)
130
+ }
131
+ } else {
132
+ listTagVal += this.buildTextValNode(item, key, '', level);
133
+ }
134
+ }
135
+ if(this.options.oneListGroup){
136
+ listTagVal = this.buildObjectNode(listTagVal, key, '', level);
137
+ }
138
+ val += listTagVal;
139
+ } else {
140
+ //nested node
141
+ if (this.options.attributesGroupName && key === this.options.attributesGroupName) {
142
+ const Ks = Object.keys(jObj[key]);
143
+ const L = Ks.length;
144
+ for (let j = 0; j < L; j++) {
145
+ attrStr += this.buildAttrPairStr(Ks[j], '' + jObj[key][Ks[j]]);
146
+ }
147
+ } else {
148
+ val += this.processTextOrObjNode(jObj[key], key, level)
149
+ }
150
+ }
151
+ }
152
+ return {attrStr: attrStr, val: val};
153
+ };
154
+
155
+ Builder.prototype.buildAttrPairStr = function(attrName, val){
156
+ val = this.options.attributeValueProcessor(attrName, '' + val);
157
+ val = this.replaceEntitiesValue(val);
158
+ if (this.options.suppressBooleanAttributes && val === "true") {
159
+ return ' ' + attrName;
160
+ } else return ' ' + attrName + '="' + val + '"';
161
+ }
162
+
163
+ function processTextOrObjNode (object, key, level) {
164
+ const result = this.j2x(object, level + 1);
165
+ if (object[this.options.textNodeName] !== undefined && Object.keys(object).length === 1) {
166
+ return this.buildTextValNode(object[this.options.textNodeName], key, result.attrStr, level);
167
+ } else {
168
+ return this.buildObjectNode(result.val, key, result.attrStr, level);
169
+ }
170
+ }
171
+
172
+ Builder.prototype.buildObjectNode = function(val, key, attrStr, level) {
173
+ if(val === ""){
174
+ if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
175
+ else {
176
+ return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
177
+ }
178
+ }else{
179
+
180
+ let tagEndExp = '</' + key + this.tagEndChar;
181
+ let piClosingChar = "";
182
+
183
+ if(key[0] === "?") {
184
+ piClosingChar = "?";
185
+ tagEndExp = "";
186
+ }
187
+
188
+ // attrStr is an empty string in case the attribute came as undefined or null
189
+ if ((attrStr || attrStr === '') && val.indexOf('<') === -1) {
190
+ return ( this.indentate(level) + '<' + key + attrStr + piClosingChar + '>' + val + tagEndExp );
191
+ } else if (this.options.commentPropName !== false && key === this.options.commentPropName && piClosingChar.length === 0) {
192
+ return this.indentate(level) + `<!--${val}-->` + this.newLine;
193
+ }else {
194
+ return (
195
+ this.indentate(level) + '<' + key + attrStr + piClosingChar + this.tagEndChar +
196
+ val +
197
+ this.indentate(level) + tagEndExp );
198
+ }
199
+ }
200
+ }
201
+
202
+ Builder.prototype.closeTag = function(key){
203
+ let closeTag = "";
204
+ if(this.options.unpairedTags.indexOf(key) !== -1){ //unpaired
205
+ if(!this.options.suppressUnpairedNode) closeTag = "/"
206
+ }else if(this.options.suppressEmptyNode){ //empty
207
+ closeTag = "/";
208
+ }else{
209
+ closeTag = `></${key}`
210
+ }
211
+ return closeTag;
212
+ }
213
+
214
+ function buildEmptyObjNode(val, key, attrStr, level) {
215
+ if (val !== '') {
216
+ return this.buildObjectNode(val, key, attrStr, level);
217
+ } else {
218
+ if(key[0] === "?") return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
219
+ else {
220
+ return this.indentate(level) + '<' + key + attrStr + '/' + this.tagEndChar;
221
+ // return this.buildTagStr(level,key, attrStr);
222
+ }
223
+ }
224
+ }
225
+
226
+ Builder.prototype.buildTextValNode = function(val, key, attrStr, level) {
227
+ if (this.options.cdataPropName !== false && key === this.options.cdataPropName) {
228
+ return this.indentate(level) + `<![CDATA[${val}]]>` + this.newLine;
229
+ }else if (this.options.commentPropName !== false && key === this.options.commentPropName) {
230
+ return this.indentate(level) + `<!--${val}-->` + this.newLine;
231
+ }else if(key[0] === "?") {//PI tag
232
+ return this.indentate(level) + '<' + key + attrStr+ '?' + this.tagEndChar;
233
+ }else{
234
+ let textValue = this.options.tagValueProcessor(key, val);
235
+ textValue = this.replaceEntitiesValue(textValue);
236
+
237
+ if( textValue === ''){
238
+ return this.indentate(level) + '<' + key + attrStr + this.closeTag(key) + this.tagEndChar;
239
+ }else{
240
+ return this.indentate(level) + '<' + key + attrStr + '>' +
241
+ textValue +
242
+ '</' + key + this.tagEndChar;
243
+ }
244
+ }
245
+ }
246
+
247
+ Builder.prototype.replaceEntitiesValue = function(textValue){
248
+ if(textValue && textValue.length > 0 && this.options.processEntities){
249
+ for (let i=0; i<this.options.entities.length; i++) {
250
+ const entity = this.options.entities[i];
251
+ textValue = textValue.replace(entity.regex, entity.val);
252
+ }
253
+ }
254
+ return textValue;
255
+ }
256
+
257
+ function indentate(level) {
258
+ return this.options.indentBy.repeat(level);
259
+ }
260
+
261
+ function isAttribute(name /*, options*/) {
262
+ if (name.startsWith(this.options.attributeNamePrefix) && name !== this.options.textNodeName) {
263
+ return name.substr(this.attrPrefixLen);
264
+ } else {
265
+ return false;
266
+ }
267
+ }
268
+
269
+ module.exports = Builder;