xml-twig 1.1.4 → 1.2.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.
package/README.md CHANGED
@@ -80,7 +80,7 @@ In my tests I parsed a 750 MB big XML file, the `node-expat` is around two times
80
80
 
81
81
  The key feature of this module is to read and process XML files in chunks. You need to create handler functions for elements you like to process.<br>
82
82
  The most notable difference to other parsers is the `purge()` and `purgeUpTo()` method. The parser reads the element and you decide how long you need to keep it in the memory.
83
- In many cases you will purge it immediatly after you have used it but in some cases you may keep the element for later use. The parser knows the element position in the XML-Tree.
83
+ In many cases you will purge it immediately after you have used it but in some cases you may keep the element for later use. The parser knows the element position in the XML-Tree.
84
84
 
85
85
 
86
86
  ```
@@ -303,20 +303,16 @@ You can specify condition on above methods. You can filter elements by following
303
303
 
304
304
  - If `undefined`, then all elements are returned.
305
305
 
306
- - If `string` then the element name must be equal to the string
307
-
306
+ - If `string` then the element name must be equal to the string<br>
308
307
  Example: `"book"`
309
308
 
310
- - If `RegExp` then the element name must match the Regular Expression
311
-
309
+ - If `RegExp` then the element name must match the Regular Expression<br>
312
310
  Example: `/book$/i`
313
311
 
314
312
  - With `ElementConditionFilter` you can specify any custom filter function.<br>
315
-
316
313
  Example: `(name, elt) => { return name === 'book' && elt.children().length > 1 }`
317
314
 
318
- - With a `Twig` object, you can specify the element directly. Apart from `purgeUpTo(elt)`, it is rarely used, because when you know the element then there is no reason to find it again.
319
-
315
+ - With a `Twig` object, you can specify the element directly. Apart from `purgeUpTo(elt)`, it is rarely used, because when you know the element then there is no reason to find it again.<br>
320
316
  Example: `elt.children()[2]`
321
317
 
322
318
 
@@ -341,7 +337,7 @@ For methods which return a single **Twig** element (e.g. `elt.next("book")`) the
341
337
 
342
338
  `.isLastChild` - **boolean**: `true` if the element is the last child in the parent
343
339
 
344
- `.index` - **integer**: The postion (starting at 0) of the element within the parent. The root element returns always 0
340
+ `.index` - **integer**: The position (starting at 0) of the element within the parent. The root element returns always 0
345
341
 
346
342
  `.name` - **string**: Name of the element/tag
347
343
 
@@ -353,19 +349,39 @@ For methods which return a single **Twig** element (e.g. `elt.next("book")`) the
353
349
 
354
350
  `.comment` - **string|string[]**: Comments or array of comments inside the element
355
351
 
356
- `.declaration` - **object**: The XML-Declaration object, exist only on `root`.
357
-
352
+ `.declaration` - **object**: The XML-Declaration object, exist only on `root`.<br>
358
353
  Example `{version: '1.0', encoding: 'UTF-8'}`.
359
354
 
360
- `.PI` - **object**: Processing Instruction, exist only on `root`.
361
-
355
+ `.PI` - **object**: Processing Instruction, exist only on `root`.<br>
362
356
  Example `{ target: 'xml-stylesheet', data: 'type="text/xsl" href="style.xsl"' }`.
363
357
 
364
- `.namespace` - **object**: Namespace of the element or `null`. Only available if parsed with option `xmlns: true`.
365
-
358
+ `.namespace` - **object**: Namespace of the element or `null`. Only available if parsed with option `xmlns: true`.<br>
366
359
  Example `{ local: 'h', uri: 'http://www.w3.org/TR/html4/' }`
367
360
 
368
361
 
362
+ #### Update XML Elements
363
+
364
+ To update the XML, use these methods. These methods modify the XML tree in the memory, not the input XML file. Use `writer()` to print modified XML or save it to a file.
365
+
366
+
367
+ `.attribute(name, value)`: Updates an attribute.<br>
368
+ `name`: Name of the attribute. Regular Expression or other conditions are not supported<br>
369
+ `value`: Then new value
370
+
371
+ `.deleteAttribute(name)`: Deletes the attribute.<br>
372
+ `name`: Name of the attribute. Regular Expression or other conditions are not supported
373
+
374
+ `.text(value)`: Update the text (PCDATA) of current element<br>
375
+ `value`: The new text or `null` to remove any text
376
+
377
+ `.addElement(name, text, attributes, position)`: Adds a new child element to the current element<br>
378
+ `name`: The name/tag of the new element<br>
379
+ `text`: The text (PCDATA) of the element or `null`<br>
380
+ `attributes`: Object of XML attributes, example: `{ id: 1, lang="en" }` or `null`<br>
381
+ `position`: The position in `children()` array where you like to add new child. You can also specify `'first'` or `'last'`
382
+
383
+
384
+
369
385
  ## Limitations
370
386
 
371
387
  This `xml-twig` module focus on reading a XML files. In principle it would be possible to create a XML file from scratch with the [Twig](./doc/twig.md#Twig) class. However, I think there are better modules available. Of course, you may run operations like `elt.root().children().push(elt.root().children()[0])`, but I think this is not so handy to use.
package/demo.js ADDED
@@ -0,0 +1,12 @@
1
+ const fs = require('fs');
2
+ const twig = require('./twig.js');
3
+
4
+
5
+ const parser = twig.createParser({ tag: 'book', event: 'bookElement' });
6
+ fs.createReadStream(`${__dirname}/samples/bookstore.xml`).pipe(parser);
7
+
8
+
9
+ parser.on('bookElement', (elt) => {
10
+ console.log(`<${elt.name}> finished after ${parser.currentLine} lines`);
11
+ })
12
+
package/doc/build.sh CHANGED
@@ -1,6 +1,6 @@
1
- #!/bin/bash
2
-
3
- jsdoc2md --private ../twig.js > ./twig.md
4
-
5
- # see https://github.com/jsdoc2md/jsdoc-to-markdown
6
-
1
+ #!/bin/bash
2
+
3
+ jsdoc2md --private ../twig.js > ./twig.md
4
+
5
+ # see https://github.com/jsdoc2md/jsdoc-to-markdown
6
+
package/doc/twig.md CHANGED
@@ -5,6 +5,9 @@
5
5
  <dd></dd>
6
6
  <dt><a href="#Twig">Twig</a></dt>
7
7
  <dd></dd>
8
+ <dt><a href="#NotImplementedYet">NotImplementedYet</a></dt>
9
+ <dd><p>Generic error for non implemented feature</p>
10
+ </dd>
8
11
  <dt><a href="#UnsupportedParser">UnsupportedParser</a></dt>
9
12
  <dd><p>Error for unsupported data types</p>
10
13
  </dd>
@@ -108,13 +111,16 @@ You can specify a <code>function</code> or a <code>event</code> name</p>
108
111
 
109
112
  * [Twig](#Twig)
110
113
  * [new Twig()](#new_Twig_new)
111
- * [new Twig(name, parent, attributes)](#new_Twig_new)
114
+ * [new Twig(name, parent, attributes, index)](#new_Twig_new)
112
115
  * [.attributes](#Twig+attributes) ℗
113
116
  * [.text](#Twig+text) ℗
114
117
  * [.name](#Twig+name) ℗
115
118
  * [.children](#Twig+children) ℗
116
119
  * [.parent](#Twig+parent) ℗
117
120
  * [.pinned](#Twig+pinned) ℗
121
+ * [.purge](#Twig+purge)
122
+ * [.purgeUpTo](#Twig+purgeUpTo)
123
+ * [.escapeEntity](#Twig+escapeEntity)
118
124
  * [.isEmpty](#Twig+isEmpty) ⇒ <code>boolean</code>
119
125
  * [.level](#Twig+level) ⇒ <code>number</code>
120
126
  * [.isRoot](#Twig+isRoot) ⇒ <code>boolean</code>
@@ -126,7 +132,6 @@ You can specify a <code>function</code> or a <code>event</code> name</p>
126
132
  * [.text](#Twig+text)
127
133
  * [.pin](#Twig+pin)
128
134
  * [.pinned](#Twig+pinned) ⇒ <code>boolean</code>
129
- * [.text](#Twig+text)
130
135
  * [.close](#Twig+close)
131
136
  * [.addChild](#Twig+addChild) ℗
132
137
  * [.writer](#Twig+writer) ⇒ <code>XMLWriter</code>
@@ -134,6 +139,7 @@ You can specify a <code>function</code> or a <code>event</code> name</p>
134
139
  * [.attributes](#Twig+attributes) ⇒ <code>object</code>
135
140
  * [.hasAttribute](#Twig+hasAttribute) ⇒ <code>boolean</code>
136
141
  * [.attribute](#Twig+attribute) ⇒ <code>object</code>
142
+ * [.deleteAttribute](#Twig+deleteAttribute)
137
143
  * [.root](#Twig+root) ⇒ [<code>Twig</code>](#Twig)
138
144
  * [.parent](#Twig+parent) ⇒ [<code>Twig</code>](#Twig)
139
145
  * [.self](#Twig+self) ⇒ [<code>Twig</code>](#Twig)
@@ -155,8 +161,8 @@ You can specify a <code>function</code> or a <code>event</code> name</p>
155
161
  * [.nextSibling](#Twig+nextSibling) ⇒ [<code>Twig</code>](#Twig)
156
162
  * [.prevSibling](#Twig+prevSibling) ⇒ [<code>Twig</code>](#Twig)
157
163
  * [.find](#Twig+find) ⇒ [<code>Twig</code>](#Twig)
158
- * [.purge()](#Twig+purge)
159
- * [.purgeUpTo(elt)](#Twig+purgeUpTo)
164
+ * [.addElement](#Twig+addElement) ⇒ [<code>Twig</code>](#Twig)
165
+ * [.delete](#Twig+delete)
160
166
  * [.setRoot(name)](#Twig+setRoot) ℗
161
167
  * [.filterElements(elements, condition)](#Twig+filterElements) ⇒ [<code>Array.&lt;Twig&gt;</code>](#Twig)
162
168
  * [.testElement(element, condition)](#Twig+testElement) ⇒ <code>boolean</code>
@@ -168,7 +174,7 @@ Generic class modeling a XML Node
168
174
 
169
175
  <a name="new_Twig_new"></a>
170
176
 
171
- ### new Twig(name, parent, attributes)
177
+ ### new Twig(name, parent, attributes, index)
172
178
  Create a new Twig object
173
179
 
174
180
 
@@ -177,6 +183,7 @@ Create a new Twig object
177
183
  | name | <code>string</code> | The name of the XML element |
178
184
  | parent | [<code>Twig</code>](#Twig) | The parent object |
179
185
  | attributes | <code>object</code> | Attribute object |
186
+ | index | <code>string</code> \| <code>number</code> | Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last' |
180
187
 
181
188
  <a name="Twig+attributes"></a>
182
189
 
@@ -244,6 +251,34 @@ Create a new Twig object
244
251
  | --- | --- | --- |
245
252
  | #pinned | <code>boolean</code> | Determines whether twig is needed in partial load |
246
253
 
254
+ <a name="Twig+purge"></a>
255
+
256
+ ### twig.purge
257
+ Purges the current, typically used after element has been processed.<br>The root object cannot be purged.
258
+
259
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
260
+ <a name="Twig+purgeUpTo"></a>
261
+
262
+ ### twig.purgeUpTo
263
+ Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.
264
+
265
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
266
+
267
+ | Param | Type | Description |
268
+ | --- | --- | --- |
269
+ | elt | [<code>Twig</code>](#Twig) | Up to this element the tree will be purged. The `elt` object itself is not purged.<br> If `undefined` then the current element is purged (i.e. `purge()`) |
270
+
271
+ <a name="Twig+escapeEntity"></a>
272
+
273
+ ### twig.escapeEntity
274
+ Escapes special XML characters. According W3C specification these are only `&, <, >, ", '` - this is a XML parser, not HTML!
275
+
276
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
277
+
278
+ | Param | Type | Description |
279
+ | --- | --- | --- |
280
+ | text | <code>string</code> | Input text to be escaped |
281
+
247
282
  <a name="Twig+isEmpty"></a>
248
283
 
249
284
  ### twig.isEmpty ⇒ <code>boolean</code>
@@ -303,17 +338,17 @@ The text of the element. No matter if given as text or CDATA entity
303
338
  <a name="Twig+text"></a>
304
339
 
305
340
  ### twig.text
306
- Modifies the text of the element
341
+ Update the text of the element
307
342
 
308
343
  **Kind**: instance property of [<code>Twig</code>](#Twig)
309
344
  **Throws**:
310
345
 
311
- - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string or numeric type
346
+ - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string, boolean or numeric type
312
347
 
313
348
 
314
349
  | Param | Type | Description |
315
350
  | --- | --- | --- |
316
- | value | <code>string</code> | New value of the attribute |
351
+ | value | <code>string</code> \| <code>number</code> \| <code>bigint</code> \| <code>boolean</code> | New text of the element |
317
352
 
318
353
  <a name="Twig+pin"></a>
319
354
 
@@ -328,21 +363,6 @@ Checks if element is pinned
328
363
 
329
364
  **Kind**: instance property of [<code>Twig</code>](#Twig)
330
365
  **Returns**: <code>boolean</code> - `true` when the element is pinned
331
- <a name="Twig+text"></a>
332
-
333
- ### twig.text
334
- Modifies the text of the element
335
-
336
- **Kind**: instance property of [<code>Twig</code>](#Twig)
337
- **Throws**:
338
-
339
- - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string or numeric type
340
-
341
-
342
- | Param | Type | Description |
343
- | --- | --- | --- |
344
- | value | <code>string</code> | New value of the attribute |
345
-
346
366
  <a name="Twig+close"></a>
347
367
 
348
368
  ### twig.close
@@ -407,7 +427,7 @@ Check if the attribute exist or not
407
427
  <a name="Twig+attribute"></a>
408
428
 
409
429
  ### twig.attribute ⇒ <code>object</code>
410
- Retrieve or update XML attribute.
430
+ Retrieve or update XML attribute. For update, the condition must be a string, i.e. must match to one attribute only.
411
431
 
412
432
  **Kind**: instance property of [<code>Twig</code>](#Twig)
413
433
  **Returns**: <code>object</code> - Attributes or `null` if no matching attribute found
@@ -415,12 +435,23 @@ Retrieve or update XML attribute.
415
435
  | Param | Type | Description |
416
436
  | --- | --- | --- |
417
437
  | condition | [<code>AttributeCondition</code>](#AttributeCondition) | Optional condition to select attributes |
418
- | text | <code>string</code> \| <code>number</code> | New value of the attribute |
438
+ | value | <code>string</code> \| <code>number</code> \| <code>bigint</code> \| <code>boolean</code> | New value of the attribute.<br>If `undefined` then existing attributes is returned. |
419
439
 
420
440
  **Example**
421
441
  ```js
422
442
  attribute((name, val) => { return name === 'age' && val > 50})
423
443
  ```
444
+ <a name="Twig+deleteAttribute"></a>
445
+
446
+ ### twig.deleteAttribute
447
+ Delete the attribute
448
+
449
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
450
+
451
+ | Param | Type | Description |
452
+ | --- | --- | --- |
453
+ | name | <code>string</code> | The attribute name |
454
+
424
455
  <a name="Twig+root"></a>
425
456
 
426
457
  ### twig.root ⇒ [<code>Twig</code>](#Twig)
@@ -647,23 +678,27 @@ Find a specific element within current element. Same as `.descendant(condition)[
647
678
  | --- | --- | --- |
648
679
  | condition | [<code>ElementCondition</code>](#ElementCondition) | Find condition |
649
680
 
650
- <a name="Twig+purge"></a>
651
-
652
- ### twig.purge()
653
- Purges the current, typically used after element has been processed.<br>The root object cannot be purged.
681
+ <a name="Twig+addElement"></a>
654
682
 
655
- **Kind**: instance method of [<code>Twig</code>](#Twig)
656
- <a name="Twig+purgeUpTo"></a>
657
-
658
- ### twig.purgeUpTo(elt)
659
- Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.
683
+ ### twig.addElement [<code>Twig</code>](#Twig)
684
+ Add a new element in the current element
660
685
 
661
- **Kind**: instance method of [<code>Twig</code>](#Twig)
686
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
687
+ **Returns**: [<code>Twig</code>](#Twig) - - The appended element
662
688
 
663
689
  | Param | Type | Description |
664
690
  | --- | --- | --- |
665
- | elt | [<code>Twig</code>](#Twig) | Up to this element the tree will be purged. The `elt` object itself is not purged.<br> If `undefined` then the current element is purged (i.e. `purge()`) |
691
+ | name | <code>string</code> | The tag name |
692
+ | text | <code>string</code> | Text of the element |
693
+ | attributes | <code>object</code> | Element attributes |
694
+ | position | <code>name</code> \| <code>number</code> | Position name 'first', 'last' or the position in the `children` |
695
+
696
+ <a name="Twig+delete"></a>
666
697
 
698
+ ### twig.delete
699
+ Deletes the current element from tree, same as `purge()`. The root object cannot be deleted.
700
+
701
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
667
702
  <a name="Twig+setRoot"></a>
668
703
 
669
704
  ### twig.setRoot(name) ℗
@@ -709,13 +744,16 @@ Common function to filter Twig element
709
744
 
710
745
  * [Twig](#Twig)
711
746
  * [new Twig()](#new_Twig_new)
712
- * [new Twig(name, parent, attributes)](#new_Twig_new)
747
+ * [new Twig(name, parent, attributes, index)](#new_Twig_new)
713
748
  * [.attributes](#Twig+attributes) ℗
714
749
  * [.text](#Twig+text) ℗
715
750
  * [.name](#Twig+name) ℗
716
751
  * [.children](#Twig+children) ℗
717
752
  * [.parent](#Twig+parent) ℗
718
753
  * [.pinned](#Twig+pinned) ℗
754
+ * [.purge](#Twig+purge)
755
+ * [.purgeUpTo](#Twig+purgeUpTo)
756
+ * [.escapeEntity](#Twig+escapeEntity)
719
757
  * [.isEmpty](#Twig+isEmpty) ⇒ <code>boolean</code>
720
758
  * [.level](#Twig+level) ⇒ <code>number</code>
721
759
  * [.isRoot](#Twig+isRoot) ⇒ <code>boolean</code>
@@ -727,7 +765,6 @@ Common function to filter Twig element
727
765
  * [.text](#Twig+text)
728
766
  * [.pin](#Twig+pin)
729
767
  * [.pinned](#Twig+pinned) ⇒ <code>boolean</code>
730
- * [.text](#Twig+text)
731
768
  * [.close](#Twig+close)
732
769
  * [.addChild](#Twig+addChild) ℗
733
770
  * [.writer](#Twig+writer) ⇒ <code>XMLWriter</code>
@@ -735,6 +772,7 @@ Common function to filter Twig element
735
772
  * [.attributes](#Twig+attributes) ⇒ <code>object</code>
736
773
  * [.hasAttribute](#Twig+hasAttribute) ⇒ <code>boolean</code>
737
774
  * [.attribute](#Twig+attribute) ⇒ <code>object</code>
775
+ * [.deleteAttribute](#Twig+deleteAttribute)
738
776
  * [.root](#Twig+root) ⇒ [<code>Twig</code>](#Twig)
739
777
  * [.parent](#Twig+parent) ⇒ [<code>Twig</code>](#Twig)
740
778
  * [.self](#Twig+self) ⇒ [<code>Twig</code>](#Twig)
@@ -756,8 +794,8 @@ Common function to filter Twig element
756
794
  * [.nextSibling](#Twig+nextSibling) ⇒ [<code>Twig</code>](#Twig)
757
795
  * [.prevSibling](#Twig+prevSibling) ⇒ [<code>Twig</code>](#Twig)
758
796
  * [.find](#Twig+find) ⇒ [<code>Twig</code>](#Twig)
759
- * [.purge()](#Twig+purge)
760
- * [.purgeUpTo(elt)](#Twig+purgeUpTo)
797
+ * [.addElement](#Twig+addElement) ⇒ [<code>Twig</code>](#Twig)
798
+ * [.delete](#Twig+delete)
761
799
  * [.setRoot(name)](#Twig+setRoot) ℗
762
800
  * [.filterElements(elements, condition)](#Twig+filterElements) ⇒ [<code>Array.&lt;Twig&gt;</code>](#Twig)
763
801
  * [.testElement(element, condition)](#Twig+testElement) ⇒ <code>boolean</code>
@@ -769,7 +807,7 @@ Generic class modeling a XML Node
769
807
 
770
808
  <a name="new_Twig_new"></a>
771
809
 
772
- ### new Twig(name, parent, attributes)
810
+ ### new Twig(name, parent, attributes, index)
773
811
  Create a new Twig object
774
812
 
775
813
 
@@ -778,6 +816,7 @@ Create a new Twig object
778
816
  | name | <code>string</code> | The name of the XML element |
779
817
  | parent | [<code>Twig</code>](#Twig) | The parent object |
780
818
  | attributes | <code>object</code> | Attribute object |
819
+ | index | <code>string</code> \| <code>number</code> | Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last' |
781
820
 
782
821
  <a name="Twig+attributes"></a>
783
822
 
@@ -845,6 +884,34 @@ Create a new Twig object
845
884
  | --- | --- | --- |
846
885
  | #pinned | <code>boolean</code> | Determines whether twig is needed in partial load |
847
886
 
887
+ <a name="Twig+purge"></a>
888
+
889
+ ### twig.purge
890
+ Purges the current, typically used after element has been processed.<br>The root object cannot be purged.
891
+
892
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
893
+ <a name="Twig+purgeUpTo"></a>
894
+
895
+ ### twig.purgeUpTo
896
+ Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.
897
+
898
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
899
+
900
+ | Param | Type | Description |
901
+ | --- | --- | --- |
902
+ | elt | [<code>Twig</code>](#Twig) | Up to this element the tree will be purged. The `elt` object itself is not purged.<br> If `undefined` then the current element is purged (i.e. `purge()`) |
903
+
904
+ <a name="Twig+escapeEntity"></a>
905
+
906
+ ### twig.escapeEntity
907
+ Escapes special XML characters. According W3C specification these are only `&, <, >, ", '` - this is a XML parser, not HTML!
908
+
909
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
910
+
911
+ | Param | Type | Description |
912
+ | --- | --- | --- |
913
+ | text | <code>string</code> | Input text to be escaped |
914
+
848
915
  <a name="Twig+isEmpty"></a>
849
916
 
850
917
  ### twig.isEmpty ⇒ <code>boolean</code>
@@ -904,17 +971,17 @@ The text of the element. No matter if given as text or CDATA entity
904
971
  <a name="Twig+text"></a>
905
972
 
906
973
  ### twig.text
907
- Modifies the text of the element
974
+ Update the text of the element
908
975
 
909
976
  **Kind**: instance property of [<code>Twig</code>](#Twig)
910
977
  **Throws**:
911
978
 
912
- - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string or numeric type
979
+ - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string, boolean or numeric type
913
980
 
914
981
 
915
982
  | Param | Type | Description |
916
983
  | --- | --- | --- |
917
- | value | <code>string</code> | New value of the attribute |
984
+ | value | <code>string</code> \| <code>number</code> \| <code>bigint</code> \| <code>boolean</code> | New text of the element |
918
985
 
919
986
  <a name="Twig+pin"></a>
920
987
 
@@ -929,21 +996,6 @@ Checks if element is pinned
929
996
 
930
997
  **Kind**: instance property of [<code>Twig</code>](#Twig)
931
998
  **Returns**: <code>boolean</code> - `true` when the element is pinned
932
- <a name="Twig+text"></a>
933
-
934
- ### twig.text
935
- Modifies the text of the element
936
-
937
- **Kind**: instance property of [<code>Twig</code>](#Twig)
938
- **Throws**:
939
-
940
- - [<code>UnsupportedType</code>](#UnsupportedType) - If value is not a string or numeric type
941
-
942
-
943
- | Param | Type | Description |
944
- | --- | --- | --- |
945
- | value | <code>string</code> | New value of the attribute |
946
-
947
999
  <a name="Twig+close"></a>
948
1000
 
949
1001
  ### twig.close
@@ -1008,7 +1060,7 @@ Check if the attribute exist or not
1008
1060
  <a name="Twig+attribute"></a>
1009
1061
 
1010
1062
  ### twig.attribute ⇒ <code>object</code>
1011
- Retrieve or update XML attribute.
1063
+ Retrieve or update XML attribute. For update, the condition must be a string, i.e. must match to one attribute only.
1012
1064
 
1013
1065
  **Kind**: instance property of [<code>Twig</code>](#Twig)
1014
1066
  **Returns**: <code>object</code> - Attributes or `null` if no matching attribute found
@@ -1016,12 +1068,23 @@ Retrieve or update XML attribute.
1016
1068
  | Param | Type | Description |
1017
1069
  | --- | --- | --- |
1018
1070
  | condition | [<code>AttributeCondition</code>](#AttributeCondition) | Optional condition to select attributes |
1019
- | text | <code>string</code> \| <code>number</code> | New value of the attribute |
1071
+ | value | <code>string</code> \| <code>number</code> \| <code>bigint</code> \| <code>boolean</code> | New value of the attribute.<br>If `undefined` then existing attributes is returned. |
1020
1072
 
1021
1073
  **Example**
1022
1074
  ```js
1023
1075
  attribute((name, val) => { return name === 'age' && val > 50})
1024
1076
  ```
1077
+ <a name="Twig+deleteAttribute"></a>
1078
+
1079
+ ### twig.deleteAttribute
1080
+ Delete the attribute
1081
+
1082
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
1083
+
1084
+ | Param | Type | Description |
1085
+ | --- | --- | --- |
1086
+ | name | <code>string</code> | The attribute name |
1087
+
1025
1088
  <a name="Twig+root"></a>
1026
1089
 
1027
1090
  ### twig.root ⇒ [<code>Twig</code>](#Twig)
@@ -1248,23 +1311,27 @@ Find a specific element within current element. Same as `.descendant(condition)[
1248
1311
  | --- | --- | --- |
1249
1312
  | condition | [<code>ElementCondition</code>](#ElementCondition) | Find condition |
1250
1313
 
1251
- <a name="Twig+purge"></a>
1252
-
1253
- ### twig.purge()
1254
- Purges the current, typically used after element has been processed.<br>The root object cannot be purged.
1314
+ <a name="Twig+addElement"></a>
1255
1315
 
1256
- **Kind**: instance method of [<code>Twig</code>](#Twig)
1257
- <a name="Twig+purgeUpTo"></a>
1316
+ ### twig.addElement [<code>Twig</code>](#Twig)
1317
+ Add a new element in the current element
1258
1318
 
1259
- ### twig.purgeUpTo(elt)
1260
- Purges up to the elt element. This allows you to keep part of the tree in memory when you purge.
1261
-
1262
- **Kind**: instance method of [<code>Twig</code>](#Twig)
1319
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
1320
+ **Returns**: [<code>Twig</code>](#Twig) - - The appended element
1263
1321
 
1264
1322
  | Param | Type | Description |
1265
1323
  | --- | --- | --- |
1266
- | elt | [<code>Twig</code>](#Twig) | Up to this element the tree will be purged. The `elt` object itself is not purged.<br> If `undefined` then the current element is purged (i.e. `purge()`) |
1324
+ | name | <code>string</code> | The tag name |
1325
+ | text | <code>string</code> | Text of the element |
1326
+ | attributes | <code>object</code> | Element attributes |
1327
+ | position | <code>name</code> \| <code>number</code> | Position name 'first', 'last' or the position in the `children` |
1328
+
1329
+ <a name="Twig+delete"></a>
1267
1330
 
1331
+ ### twig.delete
1332
+ Deletes the current element from tree, same as `purge()`. The root object cannot be deleted.
1333
+
1334
+ **Kind**: instance property of [<code>Twig</code>](#Twig)
1268
1335
  <a name="Twig+setRoot"></a>
1269
1336
 
1270
1337
  ### twig.setRoot(name) ℗
@@ -1303,6 +1370,12 @@ Common function to filter Twig element
1303
1370
  | element | [<code>Twig</code>](#Twig) | Element you like to filter |
1304
1371
  | condition | [<code>ElementCondition</code>](#ElementCondition) | The filter condition |
1305
1372
 
1373
+ <a name="NotImplementedYet"></a>
1374
+
1375
+ ## NotImplementedYet
1376
+ Generic error for non implemented feature
1377
+
1378
+ **Kind**: global class
1306
1379
  <a name="UnsupportedParser"></a>
1307
1380
 
1308
1381
  ## UnsupportedParser
package/package.json CHANGED
@@ -5,13 +5,12 @@
5
5
  },
6
6
  "name": "xml-twig",
7
7
  "description": "Node module for processing huge XML documents in tree mode",
8
- "version": "1.1.4",
8
+ "version": "1.2.0",
9
9
  "main": "twig.js",
10
10
  "directories": {
11
11
  "doc": "doc"
12
12
  },
13
13
  "devDependencies": {
14
- "jsdoc-to-markdown": "^8.0.0",
15
14
  "luxon": "^2.1.1",
16
15
  "node-expat": "^2.4.0"
17
16
  },
package/samples/sample.js CHANGED
@@ -86,3 +86,11 @@ function rootHandler(elt) {
86
86
  }
87
87
 
88
88
 
89
+ function bookHandler(elt) {
90
+ if (elt.attr('category') == 'fantasy') {
91
+ console.log(elt.writer(true).toString());
92
+ let t = elt.addElement('newTag', 'some Text > more', {id:1},2);
93
+ console.log(t.writer(true).toString());
94
+ console.log(elt.root().writer(true).toString());
95
+ }
96
+ }
package/twig.js CHANGED
@@ -123,7 +123,6 @@ function createParser(handler, options) {
123
123
  enumerable: true,
124
124
  get() { return parser._parser.column + 1 }
125
125
  });
126
- parser.underlyingParser = parser._parser;
127
126
 
128
127
  closeEvent = "closetag";
129
128
  parser.on("opentagstart", function (node) {
@@ -199,7 +198,7 @@ function createParser(handler, options) {
199
198
  }
200
199
  })
201
200
  parser.on("cdata", function (str) {
202
- current.text = str;
201
+ current.text = current.text ?? '' + str;
203
202
  })
204
203
 
205
204
  let hndl = Array.isArray(handler) ? handler : [handler];
@@ -221,7 +220,6 @@ function createParser(handler, options) {
221
220
  enumerable: true,
222
221
  get() { return parser.parser.getCurrentColumnNumber() }
223
222
  });
224
- parser.underlyingParser = parser.parser;
225
223
  closeEvent = "endElement";
226
224
 
227
225
  parser.on("startElement", function (name, attrs) {
@@ -320,7 +318,7 @@ function createParser(handler, options) {
320
318
 
321
319
  // Common events
322
320
  parser.on('text', function (str) {
323
- current.text = options.trim ? str.trim() : str;
321
+ current.text = current.text ?? '' + options.trim ? str.trim() : str;
324
322
  })
325
323
 
326
324
  parser.on("comment", function (str) {
@@ -352,7 +350,6 @@ function createParser(handler, options) {
352
350
  return parser;
353
351
  }
354
352
 
355
-
356
353
  /**
357
354
  * Generic class modeling a XML Node
358
355
  * @class Twig
@@ -435,9 +432,12 @@ class Twig {
435
432
  * @param {string} name - The name of the XML element
436
433
  * @param {Twig} parent - The parent object
437
434
  * @param {?object} attributes - Attribute object
435
+ * @param {string|number} index - Position name 'first', 'last' or the position in the current `children` array.<br>Defaults to 'last'
438
436
  */
439
- constructor(name, parent, attributes) {
440
- current = this;
437
+ constructor(name, parent, attributes, index) {
438
+ if (index === undefined)
439
+ current = this;
440
+
441
441
  if (name === null) {
442
442
  // Root element not available yet
443
443
  tree = this;
@@ -452,7 +452,16 @@ class Twig {
452
452
  this.#parent = parent;
453
453
  if (this.#parent.#pinned)
454
454
  this.#pinned = true;
455
- parent.#children.push(this);
455
+ if (index === 'last' || index === undefined) {
456
+ parent.#children.push(this);
457
+ } else if (index === 'first') {
458
+ parent.#children.unshift(this);
459
+ } else if (typeof index === 'number') {
460
+ parent.#children = parent.#children.slice(0, index).concat(this, parent.#children.slice(index));
461
+ } else {
462
+ parent.#children.push(this);
463
+ }
464
+
456
465
  }
457
466
  }
458
467
  }
@@ -460,7 +469,7 @@ class Twig {
460
469
  /**
461
470
  * Purges the current, typically used after element has been processed.<br>The root object cannot be purged.
462
471
  */
463
- purge() {
472
+ purge = function () {
464
473
  if (!this.isRoot)
465
474
  this.#parent.#children = this.#parent.#children.filter(x => !Object.is(this, x));
466
475
  }
@@ -470,7 +479,7 @@ class Twig {
470
479
  * @param {Twig} elt - Up to this element the tree will be purged. The `elt` object itself is not purged.<br>
471
480
  * If `undefined` then the current element is purged (i.e. `purge()`)
472
481
  */
473
- purgeUpTo(elt) {
482
+ purgeUpTo = function (elt) {
474
483
  if (elt === undefined) {
475
484
  this.purge();
476
485
  } else {
@@ -486,6 +495,19 @@ class Twig {
486
495
  }
487
496
  }
488
497
 
498
+ /**
499
+ * Escapes special XML characters. According W3C specification these are only `&, <, >, ", '` - this is a XML parser, not HTML!
500
+ * @param {string} text - Input text to be escaped
501
+ */
502
+ escapeEntity = function (text) {
503
+ return text
504
+ .replaceAll("&", "&amp;")
505
+ .replaceAll("<", "&lt;")
506
+ .replaceAll(">", "&gt;")
507
+ .replaceAll('"', "&quot;")
508
+ .replaceAll("'", "&apos;");
509
+ }
510
+
489
511
  /**
490
512
  * Sets the name of root element. In some cases the root is created before the XML-Root element is available<br>
491
513
  * Used internally!
@@ -568,14 +590,17 @@ class Twig {
568
590
  }
569
591
 
570
592
  /**
571
- * Modifies the text of the element
572
- * @param {string} value - New value of the attribute
573
- * @throws {UnsupportedType} - If value is not a string or numeric type
593
+ * Update the text of the element
594
+ * @param {string|number|bigint|boolean} value - New text of the element
595
+ * @throws {UnsupportedType} - If value is not a string, boolean or numeric type
574
596
  */
575
597
  set text(value) {
576
- if (!['string', 'number', 'bigint'].includes(typeof value))
598
+ if (typeof value === 'string')
599
+ this.#text = value
600
+ else if (['number', 'bigint', 'boolean'].includes(typeof value))
601
+ this.#text = value.toString()
602
+ else
577
603
  throw new UnsupportedType(value);
578
- this.#text = this.#text ?? '' + value;
579
604
  }
580
605
 
581
606
  /**
@@ -593,18 +618,6 @@ class Twig {
593
618
  return this.#pinned;
594
619
  }
595
620
 
596
- /**
597
- * Modifies the text of the element
598
- * @param {string} value - New value of the attribute
599
- * @throws {UnsupportedType} - If value is not a string or numeric type
600
- */
601
- set text(value) {
602
- if (!['string', 'number', 'bigint'].includes(typeof value))
603
- throw new UnsupportedType(value);
604
- this.#text = this.#text ?? '' + value;
605
- }
606
-
607
-
608
621
  /**
609
622
  * Closes the element
610
623
  */
@@ -681,17 +694,17 @@ class Twig {
681
694
  }
682
695
 
683
696
  /**
684
- * Retrieve or update XML attribute.
697
+ * Retrieve or update XML attribute. For update, the condition must be a string, i.e. must match to one attribute only.
685
698
  * @param {?AttributeCondition} condition - Optional condition to select attributes
686
- * @param {?string|number} text - New value of the attribute
699
+ * @param {?string|number|bigint|boolean} value - New value of the attribute.<br>If `undefined` then existing attributes is returned.
687
700
  * @returns {object} Attributes or `null` if no matching attribute found
688
701
  * @example attribute((name, val) => { return name === 'age' && val > 50})
689
702
  * attribute((name) => { return ['firstName', 'lastName'].includes(name) })
690
703
  * attribute('firstName')
691
704
  * attribute(/name/i)
692
705
  */
693
- attribute = function (condition, text) {
694
- if (text === undefined) {
706
+ attribute = function (condition, value) {
707
+ if (value === undefined) {
695
708
  let attr;
696
709
  if (condition === undefined) {
697
710
  attr = this.#attributes;
@@ -707,19 +720,26 @@ class Twig {
707
720
  return this.attribute();
708
721
  }
709
722
  return attr === null || Object.keys(attr).length == 0 ? null : attr;
723
+ } else if (typeof condition === 'string') {
724
+ if (typeof value === 'string')
725
+ this.#attributes[condition] = value
726
+ else if (['number', 'bigint', 'boolean'].includes(typeof value))
727
+ this.#attributes[condition] = value.toString()
728
+ else
729
+ throw new UnsupportedType(value);
710
730
  } else {
711
- if (text === null) {
712
- delete this.#attributes[condition];
713
- } else {
714
- if (!['string', 'number', 'bigint'].includes(typeof text))
715
- throw new UnsupportedType(text);
716
- if (typeof condition !== 'string')
717
- throw new UnsupportedCondition(condition, ['string']);
718
- this.#attributes[condition] = text;
719
- }
731
+ console.warn('Condition must be a `string` if you like to update an attribute');
720
732
  }
721
733
  }
722
734
 
735
+ /**
736
+ * Delete the attribute
737
+ * @param {string} name - The attribute name
738
+ */
739
+ deleteAttribute = function (name) {
740
+ delete this.#attributes[name];
741
+ }
742
+
723
743
  /**
724
744
  * Returns the root object
725
745
  * @returns {Twig} The root element of XML tree
@@ -1068,6 +1088,40 @@ class Twig {
1068
1088
  return null;
1069
1089
  }
1070
1090
 
1091
+ /**
1092
+ * Add a new element in the current element
1093
+ * @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`
1097
+ * @returns {Twig} - The appended element
1098
+ */
1099
+ addElement = function (name, text, attributes, position) {
1100
+ let twig = new Twig(name, this, attributes ?? {}, position ?? 'last');
1101
+ twig.#text = text ?? null;
1102
+ twig.close();
1103
+ return twig;
1104
+ }
1105
+
1106
+ /**
1107
+ * Deletes the current element from tree, same as `purge()`. The root object cannot be deleted.
1108
+ */
1109
+ delete = function () {
1110
+ this.purge();
1111
+ }
1112
+
1113
+
1114
+ }
1115
+
1116
+
1117
+ /**
1118
+ * Generic error for non implemented feature
1119
+ * @exception NotImplementedYet
1120
+ */
1121
+ class NotImplementedYet extends TypeError {
1122
+ constructor() {
1123
+ super(`Net yet implemented`);
1124
+ }
1071
1125
  }
1072
1126
 
1073
1127
 
@@ -1084,7 +1138,6 @@ class UnsupportedParser extends TypeError {
1084
1138
  }
1085
1139
  }
1086
1140
 
1087
-
1088
1141
  /**
1089
1142
  * Generic error for unsupported data types
1090
1143
  * @exception UnsupportedType
@@ -1112,5 +1165,6 @@ class UnsupportedCondition extends TypeError {
1112
1165
  }
1113
1166
  }
1114
1167
 
1168
+
1115
1169
  module.exports = { createParser, Twig, Any, Root };
1116
1170