xml-twig 1.2.80 → 1.2.82
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.
- package/README.md +3 -3
- package/doc/twig.md +93 -100
- package/package.json +1 -1
- package/twig.js +121 -121
package/twig.js
CHANGED
|
@@ -26,12 +26,12 @@ const Any = new AnyHandler();
|
|
|
26
26
|
/**
|
|
27
27
|
* Optional settings for the Twig parser
|
|
28
28
|
* @typedef ParserOptions
|
|
29
|
-
* @
|
|
30
|
-
* @
|
|
31
|
-
* @
|
|
32
|
-
* @
|
|
33
|
-
* @
|
|
34
|
-
* @
|
|
29
|
+
* @property {string} [method] - The underlying parser. Either `'sax'` or `'expat'`.
|
|
30
|
+
* @property {string} [encoding] - Encoding of the XML File. Applies only to `expat` parser.
|
|
31
|
+
* @property {boolean} [xmlns] - If `true`, then namespaces are accessible by `namespace` property.
|
|
32
|
+
* @property {boolean} [trim] - If `true`, then turn any whitespace into a single space. Text and comments are trimmed.
|
|
33
|
+
* @property {boolean} [resumeAfterError] - If `true` then parser continues reading after an error. Otherwise it throws exception.
|
|
34
|
+
* @property {boolean} [partial] - It `true` then unhandled elements are purged.
|
|
35
35
|
* @example { encoding: 'UTF-8', xmlns: true }
|
|
36
36
|
* @default { method: 'sax', encoding: 'UTF-8', xmlns: false, trim: true, resumeAfterError: false, partial: false }
|
|
37
37
|
*/
|
|
@@ -41,9 +41,9 @@ const Any = new AnyHandler();
|
|
|
41
41
|
* Element can be specified as string, Regular Expression, custom function, `Twig.Root` or `Twig.Any`<br>
|
|
42
42
|
* You can specify a `function` or a `event` name
|
|
43
43
|
* @typedef TwigHandler
|
|
44
|
-
* @
|
|
45
|
-
* @
|
|
46
|
-
* @
|
|
44
|
+
* @property {HandlerCondition} tag - Element specification
|
|
45
|
+
* @property {?HandlerFunction} function - Definition of handler function, either anonymous or explicit function
|
|
46
|
+
* @property {?string} event - Type of the event to be emitted
|
|
47
47
|
*/
|
|
48
48
|
|
|
49
49
|
/**
|
|
@@ -54,24 +54,22 @@ const Any = new AnyHandler();
|
|
|
54
54
|
* - If [HandlerConditionFilter](#HandlerConditionFilter) then function must return `true`
|
|
55
55
|
* - Use `Twig.Root` to call the handler on root element, i.e. when the end of document is reached
|
|
56
56
|
* - Use `Twig.Any` to call the handler on every element
|
|
57
|
-
* @typedef {string|RegExp|HandlerConditionFilter|RootHandler|AnyHandler} HandlerCondition
|
|
57
|
+
* @typedef {string|RegExp|HandlerConditionFilter|RootHandler|AnyHandler|undefined} HandlerCondition
|
|
58
58
|
*/
|
|
59
59
|
|
|
60
60
|
/**
|
|
61
61
|
* Handler function for Twig objects, i.e. the way you like to use the XML element.
|
|
62
62
|
* @typedef HandlerFunction
|
|
63
|
-
* @
|
|
63
|
+
* @property {Twig} elt - The current Twig element on which the function was called.
|
|
64
64
|
*/
|
|
65
65
|
|
|
66
66
|
/**
|
|
67
67
|
* Custom filter function to specify when handler shall be called
|
|
68
68
|
* @typedef {function} HandlerConditionFilter
|
|
69
69
|
* @param {string} name - Name of the element
|
|
70
|
+
* @returns {boolean} If the function returns `true`, then it is included in the filter
|
|
70
71
|
*/
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
73
|
/**
|
|
76
74
|
* Optional condition to get elements<br>
|
|
77
75
|
* - If `undefined`, then all elements are returned.<br>
|
|
@@ -79,7 +77,7 @@ const Any = new AnyHandler();
|
|
|
79
77
|
* - If `RegExp` then the element name must match the Regular Expression
|
|
80
78
|
* - If [ElementConditionFilter](#ElementConditionFilter) then function must return `true`
|
|
81
79
|
* - Use [Twig](#Twig) object to find a specific element
|
|
82
|
-
* @typedef {string|RegExp|ElementConditionFilter|Twig} ElementCondition
|
|
80
|
+
* @typedef {string|RegExp|ElementConditionFilter|Twig|undefined} ElementCondition
|
|
83
81
|
*/
|
|
84
82
|
|
|
85
83
|
/**
|
|
@@ -87,6 +85,7 @@ const Any = new AnyHandler();
|
|
|
87
85
|
* @typedef {function} ElementConditionFilter
|
|
88
86
|
* @param {string} name - Name of the element
|
|
89
87
|
* @param {Twig} elt - The Twig object
|
|
88
|
+
* @returns {boolean} If the function returns `true`, then it is included in the filter
|
|
90
89
|
*/
|
|
91
90
|
|
|
92
91
|
|
|
@@ -94,11 +93,11 @@ const Any = new AnyHandler();
|
|
|
94
93
|
/**
|
|
95
94
|
* Create a new Twig parser
|
|
96
95
|
* @param {TwigHandler|TwigHandler[]} handler - Object or array of element specification and function to handle elements
|
|
97
|
-
* @param {
|
|
96
|
+
* @param {ParserOptions} [options] - Object of optional options
|
|
98
97
|
* @throws {UnsupportedParser} - For an unsupported parser. Currently `expat` and `sax` (default) are supported.
|
|
99
98
|
*/
|
|
100
99
|
function createParser(handler, options) {
|
|
101
|
-
options = Object.assign({ method: SAX, encoding: 'UTF-8', xmlns: false, trim: true, resumeAfterError: false, partial: false }, options)
|
|
100
|
+
options = Object.assign({ method: SAX, encoding: 'UTF-8', xmlns: false, trim: true, resumeAfterError: false, partial: false }, options);
|
|
102
101
|
let parser;
|
|
103
102
|
let namespaces = {};
|
|
104
103
|
let closeEvent;
|
|
@@ -117,11 +116,11 @@ function createParser(handler, options) {
|
|
|
117
116
|
|
|
118
117
|
Object.defineProperty(parser, 'currentLine', {
|
|
119
118
|
enumerable: true,
|
|
120
|
-
get() { return parser._parser.line + 1 }
|
|
119
|
+
get() { return parser._parser.line + 1; }
|
|
121
120
|
});
|
|
122
121
|
Object.defineProperty(parser, 'currentColumn', {
|
|
123
122
|
enumerable: true,
|
|
124
|
-
get() { return parser._parser.column + 1 }
|
|
123
|
+
get() { return parser._parser.column + 1; }
|
|
125
124
|
});
|
|
126
125
|
|
|
127
126
|
closeEvent = "closetag";
|
|
@@ -161,7 +160,7 @@ function createParser(handler, options) {
|
|
|
161
160
|
}
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
|
-
})
|
|
163
|
+
});
|
|
165
164
|
|
|
166
165
|
parser.on("processinginstruction", function (pi) {
|
|
167
166
|
if (pi.name === 'xml') {
|
|
@@ -184,7 +183,7 @@ function createParser(handler, options) {
|
|
|
184
183
|
enumerable: true
|
|
185
184
|
});
|
|
186
185
|
}
|
|
187
|
-
})
|
|
186
|
+
});
|
|
188
187
|
|
|
189
188
|
parser.on("attribute", function (attr) {
|
|
190
189
|
current.attribute(attr.name, attr.value);
|
|
@@ -196,17 +195,17 @@ function createParser(handler, options) {
|
|
|
196
195
|
enumerable: true
|
|
197
196
|
});
|
|
198
197
|
}
|
|
199
|
-
})
|
|
198
|
+
});
|
|
200
199
|
parser.on("cdata", function (str) {
|
|
201
200
|
current.text = current.text ?? '' + str;
|
|
202
|
-
})
|
|
201
|
+
});
|
|
203
202
|
|
|
204
203
|
let hndl = Array.isArray(handler) ? handler : [handler];
|
|
205
204
|
let rootHandler = hndl.find(x => x.tag instanceof RootHandler);
|
|
206
205
|
parser.on("end", function () {
|
|
207
206
|
if (typeof rootHandler?.function === 'function') rootHandler.function(tree);
|
|
208
207
|
if (typeof rootHandler?.event === 'string') parser.emit(rootHandler.event, tree);
|
|
209
|
-
})
|
|
208
|
+
});
|
|
210
209
|
|
|
211
210
|
} else if (options.method === EXPAT) {
|
|
212
211
|
parser = require("node-expat").createParser();
|
|
@@ -214,11 +213,11 @@ function createParser(handler, options) {
|
|
|
214
213
|
|
|
215
214
|
Object.defineProperty(parser, 'currentLine', {
|
|
216
215
|
enumerable: true,
|
|
217
|
-
get() { return parser.parser.getCurrentLineNumber() }
|
|
216
|
+
get() { return parser.parser.getCurrentLineNumber(); }
|
|
218
217
|
});
|
|
219
218
|
Object.defineProperty(parser, 'currentColumn', {
|
|
220
219
|
enumerable: true,
|
|
221
|
-
get() { return parser.parser.getCurrentColumnNumber() }
|
|
220
|
+
get() { return parser.parser.getCurrentColumnNumber(); }
|
|
222
221
|
});
|
|
223
222
|
closeEvent = "endElement";
|
|
224
223
|
|
|
@@ -260,7 +259,7 @@ function createParser(handler, options) {
|
|
|
260
259
|
}
|
|
261
260
|
}
|
|
262
261
|
}
|
|
263
|
-
})
|
|
262
|
+
});
|
|
264
263
|
|
|
265
264
|
parser.on('xmlDecl', function (version, encoding, standalone) {
|
|
266
265
|
tree = new Twig(null);
|
|
@@ -273,11 +272,11 @@ function createParser(handler, options) {
|
|
|
273
272
|
writable: false,
|
|
274
273
|
enumerable: true
|
|
275
274
|
});
|
|
276
|
-
})
|
|
275
|
+
});
|
|
277
276
|
|
|
278
277
|
parser.on('processingInstruction', function (target, data) {
|
|
279
278
|
tree.PI = { target: target, data: data };
|
|
280
|
-
})
|
|
279
|
+
});
|
|
281
280
|
} else {
|
|
282
281
|
throw new UnsupportedParser(options.method);
|
|
283
282
|
}
|
|
@@ -314,17 +313,17 @@ function createParser(handler, options) {
|
|
|
314
313
|
current.purge();
|
|
315
314
|
current = current.parent();
|
|
316
315
|
|
|
317
|
-
})
|
|
316
|
+
});
|
|
318
317
|
|
|
319
318
|
// Common events
|
|
320
319
|
parser.on('text', function (str) {
|
|
321
320
|
current.text = current.text ?? '' + options.trim ? str.trim() : str;
|
|
322
|
-
})
|
|
321
|
+
});
|
|
323
322
|
|
|
324
323
|
parser.on("comment", function (str) {
|
|
325
324
|
if (current.hasOwnProperty('comment')) {
|
|
326
325
|
if (typeof current.comment === 'string') {
|
|
327
|
-
current.comment = [current.comment, str.trim()]
|
|
326
|
+
current.comment = [current.comment, str.trim()];
|
|
328
327
|
} else {
|
|
329
328
|
current.comment.push(str.trim());
|
|
330
329
|
}
|
|
@@ -337,7 +336,7 @@ function createParser(handler, options) {
|
|
|
337
336
|
});
|
|
338
337
|
}
|
|
339
338
|
|
|
340
|
-
})
|
|
339
|
+
});
|
|
341
340
|
|
|
342
341
|
parser.on('error', function (err) {
|
|
343
342
|
console.error(`error at line [${parser.currentLine}], column [${parser.currentColumn}]`, err);
|
|
@@ -429,10 +428,10 @@ class Twig {
|
|
|
429
428
|
|
|
430
429
|
/**
|
|
431
430
|
* Create a new Twig object
|
|
432
|
-
* @param {string} name - The name of the XML element
|
|
433
|
-
* @param {Twig} parent - The parent object
|
|
434
|
-
* @param {
|
|
435
|
-
* @param {string|number} index - Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last'
|
|
431
|
+
* @param {?string} name - The name of the XML element
|
|
432
|
+
* @param {Twig} [parent] - The parent object
|
|
433
|
+
* @param {object} [attributes] - Attribute object
|
|
434
|
+
* @param {string|number} [index] - Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last'
|
|
436
435
|
*/
|
|
437
436
|
constructor(name, parent, attributes, index) {
|
|
438
437
|
if (index === undefined)
|
|
@@ -472,11 +471,11 @@ class Twig {
|
|
|
472
471
|
purge = function () {
|
|
473
472
|
if (!this.isRoot)
|
|
474
473
|
this.#parent.#children = this.#parent.#children.filter(x => !Object.is(this, x));
|
|
475
|
-
}
|
|
474
|
+
};
|
|
476
475
|
|
|
477
476
|
/**
|
|
478
477
|
* Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.
|
|
479
|
-
* @param {Twig} elt - Up to this element the tree will be purged. The `elt` object itself is not purged.<br>
|
|
478
|
+
* @param {Twig} [elt] - Up to this element the tree will be purged. The `elt` object itself is not purged.<br>
|
|
480
479
|
* If `undefined` then the current element is purged (i.e. `purge()`)
|
|
481
480
|
*/
|
|
482
481
|
purgeUpTo = function (elt) {
|
|
@@ -493,7 +492,7 @@ class Twig {
|
|
|
493
492
|
purgeThis = prev;
|
|
494
493
|
}
|
|
495
494
|
}
|
|
496
|
-
}
|
|
495
|
+
};
|
|
497
496
|
|
|
498
497
|
/**
|
|
499
498
|
* Escapes special XML characters. According W3C specification these are only `&, <, >, ", '` - this is a XML parser, not HTML!
|
|
@@ -506,7 +505,7 @@ class Twig {
|
|
|
506
505
|
.replaceAll(">", ">")
|
|
507
506
|
.replaceAll('"', """)
|
|
508
507
|
.replaceAll("'", "'");
|
|
509
|
-
}
|
|
508
|
+
};
|
|
510
509
|
|
|
511
510
|
/**
|
|
512
511
|
* Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
|
|
@@ -596,9 +595,9 @@ class Twig {
|
|
|
596
595
|
*/
|
|
597
596
|
set text(value) {
|
|
598
597
|
if (typeof value === 'string')
|
|
599
|
-
this.#text = value
|
|
598
|
+
this.#text = value;
|
|
600
599
|
else if (['number', 'bigint', 'boolean'].includes(typeof value))
|
|
601
|
-
this.#text = value.toString()
|
|
600
|
+
this.#text = value.toString();
|
|
602
601
|
else
|
|
603
602
|
throw new UnsupportedType(value);
|
|
604
603
|
}
|
|
@@ -608,7 +607,7 @@ class Twig {
|
|
|
608
607
|
*/
|
|
609
608
|
pin = function () {
|
|
610
609
|
this.#pinned = true;
|
|
611
|
-
}
|
|
610
|
+
};
|
|
612
611
|
|
|
613
612
|
/**
|
|
614
613
|
* Checks if element is pinned
|
|
@@ -623,12 +622,12 @@ class Twig {
|
|
|
623
622
|
*/
|
|
624
623
|
close = function () {
|
|
625
624
|
Object.seal(this);
|
|
626
|
-
}
|
|
625
|
+
};
|
|
627
626
|
|
|
628
627
|
/**
|
|
629
628
|
* Internal recursive function used by `writer()`
|
|
630
629
|
* @param {XMLWriter} xw - The writer object
|
|
631
|
-
* @param {Twig[]}
|
|
630
|
+
* @param {Twig[]} childArray - Array of child elements
|
|
632
631
|
*/
|
|
633
632
|
#addChild = function (xw, childArray) {
|
|
634
633
|
for (let elt of childArray) {
|
|
@@ -640,7 +639,7 @@ class Twig {
|
|
|
640
639
|
this.#addChild(xw, elt.children());
|
|
641
640
|
}
|
|
642
641
|
xw.endElement();
|
|
643
|
-
}
|
|
642
|
+
};
|
|
644
643
|
|
|
645
644
|
|
|
646
645
|
/**
|
|
@@ -660,13 +659,13 @@ class Twig {
|
|
|
660
659
|
this.#addChild(xw, this.#children);
|
|
661
660
|
xw.endElement();
|
|
662
661
|
return xw;
|
|
663
|
-
}
|
|
662
|
+
};
|
|
664
663
|
|
|
665
664
|
/**
|
|
666
665
|
* Returns attribute value or `null` if not found.<br>
|
|
667
666
|
* If more than one matches the condition, then it returns object as [attribute()](#attribute)
|
|
668
|
-
* @param {
|
|
669
|
-
* @returns {string|number|object} - The value of the attribute or `null` if the does not exist
|
|
667
|
+
* @param {AttributeCondition} [condition] - Optional condition to select attribute
|
|
668
|
+
* @returns {?string|number|object} - The value of the attribute or `null` if the does not exist
|
|
670
669
|
*/
|
|
671
670
|
attr = function (condition) {
|
|
672
671
|
let attr = this.attribute(condition);
|
|
@@ -674,7 +673,7 @@ class Twig {
|
|
|
674
673
|
return null;
|
|
675
674
|
|
|
676
675
|
return Object.keys(attr).length === 1 ? attr[Object.keys(attr)[0]] : attr;
|
|
677
|
-
}
|
|
676
|
+
};
|
|
678
677
|
|
|
679
678
|
/**
|
|
680
679
|
* Returns all attributes of the element
|
|
@@ -691,12 +690,12 @@ class Twig {
|
|
|
691
690
|
*/
|
|
692
691
|
hasAttribute = function (name) {
|
|
693
692
|
return this.#attributes[name] !== undefined;
|
|
694
|
-
}
|
|
693
|
+
};
|
|
695
694
|
|
|
696
695
|
/**
|
|
697
696
|
* Retrieve or update XML attribute. For update, the condition must be a string, i.e. must match to one attribute only.
|
|
698
|
-
* @param {
|
|
699
|
-
* @param {
|
|
697
|
+
* @param {AttributeCondition} [condition] - Optional condition to select attributes
|
|
698
|
+
* @param {string|number|bigint|boolean} [value] - New value of the attribute.<br>If `undefined` then existing attributes is returned.
|
|
700
699
|
* @returns {object} Attributes or `null` if no matching attribute found
|
|
701
700
|
* @example attribute((name, val) => { return name === 'age' && val > 50})
|
|
702
701
|
* attribute((name) => { return ['firstName', 'lastName'].includes(name) })
|
|
@@ -711,9 +710,9 @@ class Twig {
|
|
|
711
710
|
} else if (typeof condition === 'function') {
|
|
712
711
|
attr = Object.fromEntries(Object.entries(this.#attributes).filter(([key, val]) => condition(key, val)));
|
|
713
712
|
} else if (typeof condition === 'string') {
|
|
714
|
-
attr = this.attribute(key =>
|
|
713
|
+
attr = this.attribute(key => key === condition);
|
|
715
714
|
} else if (condition instanceof RegExp) {
|
|
716
|
-
attr = this.attribute(key =>
|
|
715
|
+
attr = this.attribute(key => condition.test(key));
|
|
717
716
|
} else if (condition instanceof Twig) {
|
|
718
717
|
throw new UnsupportedCondition(condition, ['string', 'RegEx', 'function']);
|
|
719
718
|
} else {
|
|
@@ -722,15 +721,15 @@ class Twig {
|
|
|
722
721
|
return attr === null || Object.keys(attr).length == 0 ? null : attr;
|
|
723
722
|
} else if (typeof condition === 'string') {
|
|
724
723
|
if (typeof value === 'string')
|
|
725
|
-
this.#attributes[condition] = value
|
|
724
|
+
this.#attributes[condition] = value;
|
|
726
725
|
else if (['number', 'bigint', 'boolean'].includes(typeof value))
|
|
727
|
-
this.#attributes[condition] = value.toString()
|
|
726
|
+
this.#attributes[condition] = value.toString();
|
|
728
727
|
else
|
|
729
728
|
throw new UnsupportedType(value);
|
|
730
729
|
} else {
|
|
731
730
|
console.warn('Condition must be a `string` if you like to update an attribute');
|
|
732
731
|
}
|
|
733
|
-
}
|
|
732
|
+
};
|
|
734
733
|
|
|
735
734
|
/**
|
|
736
735
|
* Delete the attribute
|
|
@@ -738,7 +737,7 @@ class Twig {
|
|
|
738
737
|
*/
|
|
739
738
|
deleteAttribute = function (name) {
|
|
740
739
|
delete this.#attributes[name];
|
|
741
|
-
}
|
|
740
|
+
};
|
|
742
741
|
|
|
743
742
|
/**
|
|
744
743
|
* Returns the root object
|
|
@@ -754,7 +753,7 @@ class Twig {
|
|
|
754
753
|
}
|
|
755
754
|
return ret;
|
|
756
755
|
}
|
|
757
|
-
}
|
|
756
|
+
};
|
|
758
757
|
|
|
759
758
|
/**
|
|
760
759
|
* Returns the parent element or null if root element
|
|
@@ -762,42 +761,44 @@ class Twig {
|
|
|
762
761
|
*/
|
|
763
762
|
parent = function () {
|
|
764
763
|
return this.isRoot ? null : this.#parent;
|
|
765
|
-
}
|
|
764
|
+
};
|
|
766
765
|
|
|
767
766
|
/**
|
|
768
767
|
* @returns {Twig} - The current element
|
|
769
768
|
*/
|
|
770
769
|
self = function () {
|
|
771
770
|
return this;
|
|
772
|
-
}
|
|
771
|
+
};
|
|
773
772
|
|
|
774
773
|
/**
|
|
775
774
|
* Common function to filter Twig elements from array
|
|
776
775
|
* @param {Twig|Twig[]} elements - Array of elements you like to filter or a single element
|
|
777
|
-
* @param {
|
|
776
|
+
* @param {ElementCondition} [condition] - The filter condition
|
|
778
777
|
* @returns {Twig[]} List of matching elements or empty array
|
|
779
778
|
*/
|
|
780
779
|
filterElements(elements, condition) {
|
|
781
780
|
if (!Array.isArray(elements))
|
|
782
|
-
return filterElements([elements], condition);
|
|
781
|
+
return this.filterElements([elements], condition);
|
|
783
782
|
|
|
784
|
-
if (condition
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
783
|
+
if (condition !== undefined) {
|
|
784
|
+
if (typeof condition === 'string') {
|
|
785
|
+
return elements.filter(x => x.name === condition);
|
|
786
|
+
} else if (condition instanceof RegExp) {
|
|
787
|
+
return elements.filter(x => x.condition.test(x.name));
|
|
788
|
+
} else if (condition instanceof Twig) {
|
|
789
|
+
return elements.filter(x => Object.is(x, condition));
|
|
790
|
+
} else if (typeof condition === 'function') {
|
|
791
|
+
return elements.filter(x => condition(x.name, x));
|
|
792
|
+
}
|
|
794
793
|
}
|
|
794
|
+
|
|
795
|
+
return elements;
|
|
795
796
|
}
|
|
796
797
|
|
|
797
798
|
/**
|
|
798
799
|
* Common function to filter Twig element
|
|
799
800
|
* @param {Twig} element - Element you like to filter
|
|
800
|
-
* @param {
|
|
801
|
+
* @param {ElementCondition} [condition] - The filter condition
|
|
801
802
|
* @returns {boolean} `true` if the condition matches
|
|
802
803
|
*/
|
|
803
804
|
testElement(element, condition) {
|
|
@@ -817,17 +818,17 @@ class Twig {
|
|
|
817
818
|
|
|
818
819
|
/**
|
|
819
820
|
* All children, optionally matching `condition` of the current element or empty array
|
|
820
|
-
* @param {
|
|
821
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
821
822
|
* @returns {Twig[]}
|
|
822
823
|
*/
|
|
823
824
|
children = function (condition) {
|
|
824
825
|
return this.filterElements(this.#children, condition);
|
|
825
|
-
}
|
|
826
|
+
};
|
|
826
827
|
|
|
827
828
|
/**
|
|
828
829
|
* Returns the next matching element.
|
|
829
|
-
* @param {
|
|
830
|
-
* @returns {Twig} - The next element
|
|
830
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
831
|
+
* @returns {?Twig} - The next element
|
|
831
832
|
* @see https://www.w3.org/TR/xpath-datamodel-31/#document-order
|
|
832
833
|
*/
|
|
833
834
|
next = function (condition) {
|
|
@@ -848,12 +849,12 @@ class Twig {
|
|
|
848
849
|
return null;
|
|
849
850
|
|
|
850
851
|
return this.testElement(elt, condition) ? elt : elt.next(condition);
|
|
851
|
-
}
|
|
852
|
+
};
|
|
852
853
|
|
|
853
854
|
/**
|
|
854
855
|
* Returns the previous matching element.
|
|
855
|
-
* @param {
|
|
856
|
-
* @returns {Twig} - The previous element
|
|
856
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
857
|
+
* @returns {?Twig} - The previous element
|
|
857
858
|
* @see https://www.w3.org/TR/xpath-datamodel-31/#document-order
|
|
858
859
|
*/
|
|
859
860
|
previous = function (condition) {
|
|
@@ -869,23 +870,23 @@ class Twig {
|
|
|
869
870
|
}
|
|
870
871
|
|
|
871
872
|
return this.testElement(elt, condition) ? elt : elt.previous(condition);
|
|
872
|
-
}
|
|
873
|
+
};
|
|
873
874
|
|
|
874
875
|
/**
|
|
875
876
|
* Returns the first matching element. This is usually the root element
|
|
876
|
-
* @param {
|
|
877
|
-
* @returns {Twig} - The first element
|
|
877
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
878
|
+
* @returns {?Twig} - The first element
|
|
878
879
|
*/
|
|
879
880
|
first = function (condition) {
|
|
880
881
|
if (this === null)
|
|
881
882
|
return null;
|
|
882
883
|
return this.testElement(this.root(), condition) ? this.root() : this.root().next(condition);
|
|
883
|
-
}
|
|
884
|
+
};
|
|
884
885
|
|
|
885
886
|
/**
|
|
886
887
|
* Returns the last matching element.
|
|
887
|
-
* @param {
|
|
888
|
-
* @returns {Twig} - The last element
|
|
888
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
889
|
+
* @returns {?Twig} - The last element
|
|
889
890
|
*/
|
|
890
891
|
last = function (condition) {
|
|
891
892
|
if (this === null)
|
|
@@ -899,7 +900,7 @@ class Twig {
|
|
|
899
900
|
}
|
|
900
901
|
|
|
901
902
|
return this.testElement(elt, condition) ? elt : elt.previous(condition);
|
|
902
|
-
}
|
|
903
|
+
};
|
|
903
904
|
|
|
904
905
|
/**
|
|
905
906
|
* Check if the element is the first child of the parent
|
|
@@ -909,7 +910,7 @@ class Twig {
|
|
|
909
910
|
if (this.isRoot) {
|
|
910
911
|
return false;
|
|
911
912
|
} else {
|
|
912
|
-
return this.index === 0
|
|
913
|
+
return this.index === 0;
|
|
913
914
|
}
|
|
914
915
|
}
|
|
915
916
|
|
|
@@ -927,7 +928,7 @@ class Twig {
|
|
|
927
928
|
|
|
928
929
|
/**
|
|
929
930
|
* Returns descendants (children, grandchildren, etc.) of the current element
|
|
930
|
-
* @param {
|
|
931
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
931
932
|
* @returns {Twig[]} - Array of descendants or empty array
|
|
932
933
|
*/
|
|
933
934
|
descendant = function (condition) {
|
|
@@ -937,11 +938,11 @@ class Twig {
|
|
|
937
938
|
elts = elts.concat(c.descendant());
|
|
938
939
|
}
|
|
939
940
|
return this.filterElements(elts, condition);
|
|
940
|
-
}
|
|
941
|
+
};
|
|
941
942
|
|
|
942
943
|
/**
|
|
943
944
|
* Returns descendants (children, grandchildren, etc.) of the current element and the current element itself
|
|
944
|
-
* @param {
|
|
945
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
945
946
|
* @returns {Twig[]} - Array of descendants or empty array
|
|
946
947
|
*/
|
|
947
948
|
descendantOrSelf = function (condition) {
|
|
@@ -951,11 +952,11 @@ class Twig {
|
|
|
951
952
|
elts = elts.concat(c.descendant());
|
|
952
953
|
}
|
|
953
954
|
return this.filterElements(elts, condition);
|
|
954
|
-
}
|
|
955
|
+
};
|
|
955
956
|
|
|
956
957
|
/**
|
|
957
958
|
* Returns ancestors (parent, grandparent, etc.) of the current element
|
|
958
|
-
* @param {
|
|
959
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
959
960
|
* @returns {Twig[]} - Array of ancestors or empty array
|
|
960
961
|
*/
|
|
961
962
|
ancestor = function (condition) {
|
|
@@ -969,11 +970,11 @@ class Twig {
|
|
|
969
970
|
}
|
|
970
971
|
}
|
|
971
972
|
return this.filterElements(elts, condition);
|
|
972
|
-
}
|
|
973
|
+
};
|
|
973
974
|
|
|
974
975
|
/**
|
|
975
976
|
* Returns ancestors (parent, grandparent, etc.) of the current element and the current element itself
|
|
976
|
-
* @param {
|
|
977
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
977
978
|
* @returns {Twig[]} - Array of ancestors or empty array
|
|
978
979
|
*/
|
|
979
980
|
ancestorOrSelf = function (condition) {
|
|
@@ -987,11 +988,11 @@ class Twig {
|
|
|
987
988
|
}
|
|
988
989
|
}
|
|
989
990
|
return this.filterElements(elts, condition);
|
|
990
|
-
}
|
|
991
|
+
};
|
|
991
992
|
|
|
992
993
|
/**
|
|
993
994
|
* Returns all sibling element of the current element
|
|
994
|
-
* @param {
|
|
995
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
995
996
|
* @returns {Twig[]} - Array of sibling or empty array
|
|
996
997
|
*/
|
|
997
998
|
sibling = function (condition) {
|
|
@@ -1000,11 +1001,11 @@ class Twig {
|
|
|
1000
1001
|
elts = this.#parent.#children.filter(x => !Object.is(x, this));
|
|
1001
1002
|
}
|
|
1002
1003
|
return this.filterElements(elts, condition);
|
|
1003
|
-
}
|
|
1004
|
+
};
|
|
1004
1005
|
|
|
1005
1006
|
/**
|
|
1006
1007
|
* Returns all sibling element of the current element and the current element itself
|
|
1007
|
-
* @param {
|
|
1008
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
1008
1009
|
* @returns {Twig[]} - Array of sibling or empty array
|
|
1009
1010
|
*/
|
|
1010
1011
|
siblingOrSelf = function (condition) {
|
|
@@ -1013,11 +1014,11 @@ class Twig {
|
|
|
1013
1014
|
elts = this.#parent.#children;
|
|
1014
1015
|
}
|
|
1015
1016
|
return this.filterElements(elts, condition);
|
|
1016
|
-
}
|
|
1017
|
+
};
|
|
1017
1018
|
|
|
1018
1019
|
/**
|
|
1019
1020
|
* Returns all following sibling element of the current element
|
|
1020
|
-
* @param {
|
|
1021
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
1021
1022
|
* @returns {Twig[]} - Array of sibling or empty array
|
|
1022
1023
|
*/
|
|
1023
1024
|
followingSibling = function (condition) {
|
|
@@ -1026,11 +1027,11 @@ class Twig {
|
|
|
1026
1027
|
elts = this.#parent.#children.slice(this.index + 1);
|
|
1027
1028
|
}
|
|
1028
1029
|
return this.filterElements(elts, condition);
|
|
1029
|
-
}
|
|
1030
|
+
};
|
|
1030
1031
|
|
|
1031
1032
|
/**
|
|
1032
1033
|
* Returns all preceding sibling element of the current element
|
|
1033
|
-
* @param {
|
|
1034
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
1034
1035
|
* @returns {Twig[]} - Array of sibling or empty array
|
|
1035
1036
|
*/
|
|
1036
1037
|
precedingSibling = function (condition) {
|
|
@@ -1039,12 +1040,12 @@ class Twig {
|
|
|
1039
1040
|
elts = this.#parent.#children.slice(0, this.index);
|
|
1040
1041
|
}
|
|
1041
1042
|
return this.filterElements(elts, condition);
|
|
1042
|
-
}
|
|
1043
|
+
};
|
|
1043
1044
|
|
|
1044
1045
|
/**
|
|
1045
1046
|
* Returns next sibling element of the current element
|
|
1046
|
-
* @param {
|
|
1047
|
-
* @returns {Twig} - The next sibling or `null`
|
|
1047
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
1048
|
+
* @returns {?Twig} - The next sibling or `null`
|
|
1048
1049
|
*/
|
|
1049
1050
|
nextSibling = function (condition) {
|
|
1050
1051
|
let elt;
|
|
@@ -1054,12 +1055,12 @@ class Twig {
|
|
|
1054
1055
|
return null;
|
|
1055
1056
|
|
|
1056
1057
|
return this.testElement(elt, condition) ? elt : elt.nextSibling(condition);
|
|
1057
|
-
}
|
|
1058
|
+
};
|
|
1058
1059
|
|
|
1059
1060
|
/**
|
|
1060
1061
|
* Returns previous sibling element of the current element
|
|
1061
|
-
* @param {
|
|
1062
|
-
* @returns {Twig} - The previous sibling or `null`
|
|
1062
|
+
* @param {ElementCondition} [condition] - Optional condition
|
|
1063
|
+
* @returns {?Twig} - The previous sibling or `null`
|
|
1063
1064
|
*/
|
|
1064
1065
|
prevSibling = function (condition) {
|
|
1065
1066
|
if (!this.isRoot && this.index > 0) {
|
|
@@ -1068,12 +1069,12 @@ class Twig {
|
|
|
1068
1069
|
} else {
|
|
1069
1070
|
return null;
|
|
1070
1071
|
}
|
|
1071
|
-
}
|
|
1072
|
+
};
|
|
1072
1073
|
|
|
1073
1074
|
/**
|
|
1074
1075
|
* Find a specific element within current element. Same as `.descendant(condition)[0]`
|
|
1075
1076
|
* @param {ElementCondition} condition - Find condition
|
|
1076
|
-
* @returns {Twig} - First matching element or `null`
|
|
1077
|
+
* @returns {?Twig} - First matching element or `null`
|
|
1077
1078
|
*/
|
|
1078
1079
|
find = function (condition) {
|
|
1079
1080
|
let children = this.filterElements(this.#children, condition);
|
|
@@ -1086,14 +1087,14 @@ class Twig {
|
|
|
1086
1087
|
return ret;
|
|
1087
1088
|
}
|
|
1088
1089
|
return null;
|
|
1089
|
-
}
|
|
1090
|
+
};
|
|
1090
1091
|
|
|
1091
1092
|
/**
|
|
1092
1093
|
* Add a new element in the current element
|
|
1093
1094
|
* @param {string} name - The tag name
|
|
1094
|
-
* @param {?string} text - Text of the element
|
|
1095
|
-
* @param {?object} attributes - Element attributes
|
|
1096
|
-
* @param {name|number} position - Position name 'first', 'last' or the position in the `children`
|
|
1095
|
+
* @param {?string} [text] - Text of the element
|
|
1096
|
+
* @param {?object} [attributes] - Element attributes
|
|
1097
|
+
* @param {name|number} [position] - Position name 'first', 'last' or the position in the `children`
|
|
1097
1098
|
* @returns {Twig} - The appended element
|
|
1098
1099
|
*/
|
|
1099
1100
|
addElement = function (name, text, attributes, position) {
|
|
@@ -1101,14 +1102,14 @@ class Twig {
|
|
|
1101
1102
|
twig.#text = text ?? null;
|
|
1102
1103
|
twig.close();
|
|
1103
1104
|
return twig;
|
|
1104
|
-
}
|
|
1105
|
+
};
|
|
1105
1106
|
|
|
1106
1107
|
/**
|
|
1107
1108
|
* Deletes the current element from tree, same as `purge()`. The root object cannot be deleted.
|
|
1108
1109
|
*/
|
|
1109
1110
|
delete = function () {
|
|
1110
1111
|
this.purge();
|
|
1111
|
-
}
|
|
1112
|
+
};
|
|
1112
1113
|
|
|
1113
1114
|
|
|
1114
1115
|
}
|
|
@@ -1167,4 +1168,3 @@ class UnsupportedCondition extends TypeError {
|
|
|
1167
1168
|
|
|
1168
1169
|
|
|
1169
1170
|
module.exports = { createParser, Twig, Any, Root };
|
|
1170
|
-
|