@@ -4,9 +4,10 @@ const util = require('./util');
4
4
5
5
const defaultOptions = {
6
6
allowBooleanAttributes : false , //A tag can have attributes without any value
7
+ ignoreNameSpace : false //Ignore namespace verification
7
8
} ;
8
9
9
- const props = [ 'allowBooleanAttributes' ] ;
10
+ const props = [ 'allowBooleanAttributes' , 'ignoreNameSpace' ] ;
10
11
11
12
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
12
13
exports . validate = function ( xmlData , options ) {
@@ -16,6 +17,7 @@ exports.validate = function (xmlData, options) {
16
17
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
17
18
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
18
19
const tags = [ ] ;
20
+ let nameSpaces = [ ] ;
19
21
let tagFound = false ;
20
22
21
23
//indicates that the root tag has been closed (aka. depth 0 has been reached)
@@ -77,10 +79,26 @@ exports.validate = function (xmlData, options) {
77
79
return getErrorObject ( 'InvalidTag' , msg , getLineNumberForPosition ( xmlData , i ) ) ;
78
80
}
79
81
80
- const result = readAttributeStr ( xmlData , i ) ;
82
+ const result = readAttributeStr ( xmlData , i , options ) ;
81
83
if ( result === false ) {
82
84
return getErrorObject ( 'InvalidAttr' , "Attributes for '" + tagName + "' have open quote." , getLineNumberForPosition ( xmlData , i ) ) ;
83
85
}
86
+
87
+ if ( ! options . ignoreNameSpace ) {
88
+ if ( result . nsArray . length > 0 ) {
89
+ //Pushing namespaces defined in tag
90
+ for ( let x = 0 ; x < result . nsArray . length ; x ++ ) {
91
+ nameSpaces . push ( result . nsArray [ x ] ) ;
92
+ }
93
+ }
94
+
95
+ const nsResult = validateNameSpace ( tagName , nameSpaces ) ;
96
+
97
+ if ( ! nsResult . isValid ) {
98
+ return getErrorObject ( 'InvalidNS' , nsResult . errorMsg , getLineNumberForPosition ( xmlData , i ) ) ;
99
+ }
100
+ }
101
+
84
102
let attrStr = result . value ;
85
103
i = result . index ;
86
104
@@ -104,8 +122,15 @@ exports.validate = function (xmlData, options) {
104
122
return getErrorObject ( 'InvalidTag' , "Closing tag '" + tagName + "' can't have attributes or invalid starting." , getLineNumberForPosition ( xmlData , i ) ) ;
105
123
} else {
106
124
const otg = tags . pop ( ) ;
107
- if ( tagName !== otg ) {
108
- return getErrorObject ( 'InvalidTag' , "Closing tag '" + otg + "' is expected inplace of '" + tagName + "'." , getLineNumberForPosition ( xmlData , i ) ) ;
125
+ if ( tagName !== otg . name ) {
126
+ return getErrorObject ( 'InvalidTag' , "Closing tag '" + otg . name + "' is expected inplace of '" + tagName + "'." , getLineNumberForPosition ( xmlData , i ) ) ;
127
+ }
128
+
129
+ if ( ! options . ignoreNameSpace && otg . nsArray . length > 0 ) {
130
+ //Pushing namespaces defined in tag
131
+ for ( let x = 0 ; x < otg . nsArray . length ; x ++ ) {
132
+ nameSpaces . pop ( otg . nsArray [ x ] ) ;
133
+ }
109
134
}
110
135
111
136
//when there are no more tags, we reached the root level.
@@ -126,7 +151,15 @@ exports.validate = function (xmlData, options) {
126
151
if ( reachedRoot === true ) {
127
152
return getErrorObject ( 'InvalidXml' , 'Multiple possible root nodes found.' , getLineNumberForPosition ( xmlData , i ) ) ;
128
153
} else {
129
- tags . push ( tagName ) ;
154
+ let tagObject = {
155
+ name : tagName
156
+ } ;
157
+
158
+ if ( ! options . ignoreNameSpace ) {
159
+ tagObject [ "nsArray" ] = result . nsArray ;
160
+ }
161
+
162
+ tags . push ( tagObject ) ;
130
163
}
131
164
tagFound = true ;
132
165
}
@@ -255,7 +288,7 @@ var singleQuote = "'";
255
288
* @param {string } xmlData
256
289
* @param {number } i
257
290
*/
258
- function readAttributeStr ( xmlData , i ) {
291
+ function readAttributeStr ( xmlData , i , options ) {
259
292
let attrStr = '' ;
260
293
let startChar = '' ;
261
294
let tagClosed = false ;
@@ -281,11 +314,17 @@ function readAttributeStr(xmlData, i) {
281
314
return false ;
282
315
}
283
316
284
- return {
317
+ let result = {
285
318
value : attrStr ,
286
319
index : i ,
287
320
tagClosed : tagClosed
288
321
} ;
322
+
323
+ if ( ! options . ignoreNameSpace ) {
324
+ result [ "nsArray" ] = getNameSpaceDefinitions ( attrStr ) ;
325
+ }
326
+
327
+ return result ;
289
328
}
290
329
291
330
/**
@@ -384,6 +423,42 @@ function validateTagName(tagname) {
384
423
return util . isName ( tagname ) /* && !tagname.match(startsWithXML) */ ;
385
424
}
386
425
426
+ function validateNameSpace ( tagName , nsArray ) {
427
+ let tagSplit = tagName . split ( ":" ) ;
428
+ let isValid , errorMsg ;
429
+ switch ( tagSplit . length ) {
430
+ case 1 :
431
+ isValid = true ;
432
+ break ;
433
+ case 2 :
434
+ if ( nsArray . indexOf ( tagSplit [ 0 ] ) > - 1 ) {
435
+ isValid = true ;
436
+ } else {
437
+ isValid = false ;
438
+ errorMsg = "Namespace prefix '" + tagSplit [ 0 ] + "' is not defined for tag '" + tagName + "'" ;
439
+ }
440
+ break ;
441
+ default :
442
+ isValid = false ;
443
+ errorMsg = "Tag '" + tagName + "' cannot have multiple namespace prefixes" ;
444
+ }
445
+ return {
446
+ isValid : isValid ,
447
+ errorMsg : errorMsg
448
+ }
449
+ }
450
+
451
+ function getNameSpaceDefinitions ( attributeString ) {
452
+ const regexNs = / x m l n s : ( [ ^ = ] + ) = / g;
453
+ let nsArray = [ ] ;
454
+ let matches = regexNs . exec ( attributeString ) ;
455
+ while ( matches ) {
456
+ nsArray . push ( matches [ 1 ] ) ;
457
+ matches = regexNs . exec ( attributeString ) ;
458
+ }
459
+ return nsArray ;
460
+ }
461
+
387
462
//this function returns the line number for the character at the given index
388
463
function getLineNumberForPosition ( xmlData , index ) {
389
464
var lines = xmlData . substring ( 0 , index ) . split ( / \r ? \n / ) ;
0 commit comments