@cntwg/xml-lib-js 0.0.16

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.
@@ -0,0 +1,1025 @@
1
+ // [v0.1.045-20220906]
2
+
3
+ // === module init block ===
4
+
5
+ const fs = require('fs');
6
+ const fse = fs.promises;
7
+ const xmlParser = require('@ygracs/xml-js6');
8
+
9
+ const {
10
+ valueToIndex, readAsBool, readAsString, isNotEmptyString,
11
+ isArray, isObject, isPlainObject,
12
+ valueToArray,
13
+ } = require('@ygracs/bsfoc-lib-js');
14
+
15
+ const xObj = require('@ygracs/xobj-lib-js');
16
+ const TXmlContentParseOptions = xObj.TXmlContentParseOptions;
17
+ const XML_DEF_PARSE_OPTIONS = xObj.DEF_XML_PARSE_OPTIONS;
18
+
19
+ const XML_DEF_ROOT_ETAG_NAME = 'root';
20
+ const XML_LANG_ATTR_TNAME = 'lang';
21
+
22
+ const XML_TE_INVARG_EMSG = 'invalid argument';
23
+ const XML_TE_NOBJ_EMSG = `${XML_TE_INVARG_EMSG} (an object is expected)`;
24
+ const XML_TE_NARR_EMSG = `${XML_TE_INVARG_EMSG} (an array is expected)`;
25
+ const XML_TE_NROOT_EMSG = 'root element not found';
26
+
27
+ // === module extra block (helper functions) ===
28
+
29
+ // === module main block (function definitions) ===
30
+
31
+ // === module main block (class definitions) ===
32
+
33
+ class TXmlAttributesMapper {
34
+ #_host = null;
35
+ #_options = null;
36
+
37
+ constructor(obj, opt){
38
+ // init an elements content
39
+ this.#_host = isPlainObject(obj) ? obj : {};
40
+ // load options
41
+ let _options = (
42
+ isPlainObject(opt) && isPlainObject(opt.parseOptions)
43
+ ? opt.parseOptions
44
+ : {}
45
+ );
46
+ let { settings: _settings } = _options;
47
+ if (!isPlainObject(_settings)) _options.settings = _settings = {};
48
+ let { attributesKey } = _settings;
49
+ if (
50
+ typeof attributesKey !== 'string'
51
+ || ((attributesKey = attributesKey.trim()) === '')
52
+ ) {
53
+ attributesKey = XML_DEF_PARSE_OPTIONS.attributesKey;
54
+ };
55
+ // save options
56
+ _settings.attributesKey = attributesKey;
57
+ this.#_options = _options;
58
+ }
59
+
60
+ get entries(){
61
+ let items = xObj.getXObjAttributes(
62
+ this.#_host,
63
+ this.#_options.settings.attributesKey,
64
+ );
65
+ return isPlainObject(items) ? Object.entries(items) : [];
66
+ }
67
+
68
+ hasAttribute(value){
69
+ let name = '';
70
+ if (
71
+ typeof value !== 'string' || ((name = value.trim()) === '')
72
+ ) {
73
+ return false;
74
+ };
75
+ // // TODO: await implementation for class method provided by xObj
76
+ let item = xObj.getXObjAttributes(
77
+ this.#_host,
78
+ this.#_options.settings.attributesKey,
79
+ );
80
+ return isPlainObject(item) && item[name] !== undefined;
81
+ }
82
+
83
+ getAttribute(value){
84
+ let name = '';
85
+ if (
86
+ typeof value !== 'string' || ((name = value.trim()) === '')
87
+ ) {
88
+ return '';
89
+ };
90
+ return xObj.readXObjAttr(
91
+ this.#_host,
92
+ name,
93
+ this.#_options.settings.attributesKey,
94
+ );
95
+ }
96
+
97
+ setAttribute(attr, value){
98
+ let name = '';
99
+ if (
100
+ typeof attr !== 'string' || ((name = attr.trim()) === '')
101
+ || value === undefined
102
+ ) {
103
+ return false;
104
+ };
105
+ return xObj.writeXObjAttr(
106
+ this.#_host,
107
+ name,
108
+ value,
109
+ this.#_options.settings.attributesKey,
110
+ );
111
+ }
112
+
113
+ delAttribute(value){
114
+ let name = '';
115
+ if (
116
+ typeof value !== 'string' || ((name = value.trim()) === '')
117
+ ) {
118
+ return false;
119
+ };
120
+ // // TODO: await implementation for class method provided by xObj
121
+ let items = xObj.getXObjAttributes(
122
+ this.#_host,
123
+ this.#_options.settings.attributesKey,
124
+ );
125
+ let isSUCCEED = isPlainObject(items);
126
+ if (isSUCCEED) {
127
+ // // TODO: catch errors in strict mode
128
+ isSUCCEED = delete items[name];
129
+ };
130
+ return isSUCCEED;
131
+ }
132
+
133
+ clear(){
134
+ xObj.insertXObjElement(
135
+ this.#_host,
136
+ this.#_options.settings.attributesKey,
137
+ {
138
+ force: true,
139
+ rip_oldies: true,
140
+ },
141
+ );
142
+ }
143
+
144
+ }
145
+
146
+ class TXmlElementController {
147
+ #_host = null;
148
+ #_options = null;
149
+ #_p_opts = null;
150
+ #_attributes = null;
151
+ #_name = null;
152
+
153
+ constructor(obj, opt){
154
+ // load options
155
+ let _options = isPlainObject(opt) ? opt : {};
156
+ let { proxyModeEnable } = _options;
157
+ if (typeof proxyModeEnable !== 'boolean') proxyModeEnable = false;
158
+ // init an elements content
159
+ if (isObject(obj)) {
160
+ if (isArray(obj)) return new TXmlElementsListController(obj, opt);
161
+ // // TODO: split a plain object and class instances
162
+ this.#_host = obj;
163
+ } else {
164
+ if (proxyModeEnable) {
165
+ let msg = [
166
+ 'TXmlElementController in "proxyMode":', XML_TE_NOBJ_EMSG,
167
+ ].join(' ');
168
+ throw new TypeError(msg);
169
+ };
170
+ this.#_host = {};
171
+ };
172
+ // set parser options
173
+ let { parseOptions: _parseOptions } = _options;
174
+ if (!isPlainObject(_parseOptions)) _parseOptions = {};
175
+ let { settings: _settings } = _parseOptions;
176
+ if (!isPlainObject(_settings)) {
177
+ _parseOptions.settings = _settings = {};
178
+ // // TODO: set defaults
179
+ };
180
+ // save parser options
181
+ _options.parseOptions = _parseOptions;
182
+ this.#_p_opts = _parseOptions;
183
+ // save options
184
+ _options.proxyModeEnable = proxyModeEnable;
185
+ this.#_options = _options;
186
+ // bind atributes mapper
187
+ this.#_attributes = new TXmlAttributesMapper(obj, _options);
188
+ }
189
+
190
+ get attributes(){
191
+ return this.#_attributes;
192
+ }
193
+
194
+ get name(){
195
+ let result = '';
196
+ if (this.#_p_opts.settings.compact) {
197
+ result = xObj.readXObjParam(this.#_host, this.#_p_opts.settings.nameKey);
198
+ } else {
199
+ // TODO: read name of element in non-compact mode
200
+ };
201
+ return result;
202
+ }
203
+
204
+ get textValue(){
205
+ return xObj.readXObjParam(this.#_host, this.#_p_opts.settings.textKey);
206
+ }
207
+
208
+ set textValue(value){
209
+ xObj.writeXObjParam(this.#_host, value, this.#_p_opts.settings.textKey);
210
+ }
211
+
212
+ hasAttribute(name){
213
+ return this.#_attributes.hasAttribute(name);
214
+ }
215
+
216
+ getAttribute(name){
217
+ return this.#_attributes.getAttribute(name);
218
+ }
219
+
220
+ setAttribute(...args){
221
+ return this.#_attributes.setAttribute(...args);
222
+ }
223
+
224
+ delAttribute(name){
225
+ return this.#_attributes.delAttribute(name);
226
+ }
227
+
228
+ getTextValue(){
229
+ return [
230
+ this.#_attributes.getAttribute(XML_LANG_ATTR_TNAME),
231
+ this.textValue,
232
+ ];
233
+ }
234
+
235
+ setTextValue(value){
236
+ value = valueToArray(value);
237
+ let val_size = value.length;
238
+ if (val_size === 1) {
239
+ return xObj.writeXObjParam(
240
+ this.#_host,
241
+ value[0],
242
+ this.#_p_opts.settings.textKey,
243
+ );
244
+ } else if (val_size > 1) {
245
+ if (xObj.writeXObjParam(
246
+ this.#_host,
247
+ value[1],
248
+ this.#_p_opts.settings.textKey,
249
+ )) {
250
+ return this.#_attributes.setAttribute(XML_LANG_ATTR_TNAME, value[0]);
251
+ };
252
+ };
253
+ return false;
254
+ }
255
+
256
+ getChild(value){
257
+ let name = '';
258
+ if (
259
+ typeof value !== 'string' || ((name = value.trim()) === '')
260
+ ) {
261
+ return null;
262
+ };
263
+ let item = xObj.getXObjElement(this.#_host, name);
264
+ if (isArray(item)) {
265
+ item = new TXmlElementsListController(item, this.#_options);
266
+ } else if (isObject(item)) {
267
+ item = new TXmlElementController(item, this.#_options);
268
+ } else {
269
+ item = null;
270
+ };
271
+ return item;
272
+ }
273
+
274
+ _getChildRaw(name){
275
+ //console.log('CHECK: TXmlElementController._getChildRaw() => was called...');
276
+ //console.log('CHECK: TXmlElementController._getChildRaw() => _host => '+JSON.stringify(this.#_host, null, 2));
277
+ let item = xObj.getXObjElement(this.#_host, name);
278
+ //console.log('CHECK: TXmlElementController._getChildRaw() => item => '+item);
279
+ //console.log('CHECK: TXmlElementController._getChildRaw() => item.name() => ['+name+']');
280
+ //console.log('CHECK: TXmlElementController._getChildRaw() => item.typeof() => '+typeof item);
281
+ //console.log('CHECK: TXmlElementController._getChildRaw() => item => '+JSON.stringify(item, null, 2));
282
+ //console.log('CHECK: TXmlElementController._getChildRaw() => was left...');
283
+ return item !== undefined ? item : null;
284
+ }
285
+
286
+ _setChildRaw(name, obj){
287
+ //console.log('CHECK: TXmlElementController._setChildRaw() => was called...');
288
+ let isSUCCEED = false; let item = null;
289
+ if (isArray(obj)) {
290
+ //console.log('CHECK: TXmlElementController._setChildRaw() => <obj> is array...');
291
+ item = xObj.insertXObjEList(this.#_host, name, true);
292
+ // load elements
293
+ for (let element of obj) {
294
+ if (isPlainObject(element)) item.push(element);
295
+ };
296
+ } else if (isObject(obj)) {
297
+ //console.log('CHECK: TXmlElementController._setChildRaw() => <obj> is object...');
298
+ item = xObj.insertXObjElement(this.#_host, name, true);
299
+ // // TODO: set element
300
+ };
301
+ if (item) isSUCCEED = true;
302
+ return isSUCCEED;
303
+ }
304
+
305
+ _addChildRaw(name){//}, obj){
306
+ //console.log('CHECK: TXmlElementController._addChild() => was called...');
307
+ //console.log('CHECK: TXmlElementController._addChildRaw() => _host => '+JSON.stringify(this.#_host, null, 2));
308
+ let result = xObj.addXObjElement(this.#_host, name);
309
+ //console.log('CHECK: TXmlElementController._addChildRaw() => result => '+result);
310
+ //console.log('CHECK: TXmlElementController._addChildRaw() => item.name() => ['+name+']');
311
+ //console.log('CHECK: TXmlElementController._addChildRaw() => item.typeof() => '+typeof result.item);
312
+ //console.log('CHECK: TXmlElementController._addChildRaw() => item => '+JSON.stringify(result.item, null, 2));
313
+ //console.log('CHECK: TXmlElementController._addChildRaw() => was left...');
314
+ return result.isSucceed ? result.item : null;
315
+ }
316
+
317
+ addChild(value){
318
+ let name = '';
319
+ if (
320
+ typeof value !== 'string' || ((name = value.trim()) === '')
321
+ ) {
322
+ return null;
323
+ };
324
+ let result = xObj.addXObjElement(this.#_host, name);
325
+ if (!result.isSucceed) return null;
326
+ return new TXmlElementController(result.item, this.#_options);
327
+ }
328
+
329
+ delChild(value){
330
+ let name = '';
331
+ if (
332
+ typeof value !== 'string' || ((name = value.trim()) === '')
333
+ ) {
334
+ return false;
335
+ };
336
+ let item = xObj.getXObjElement(this.#_host, name);
337
+ if (isArray(item)) {
338
+ //console.log('CHECK: TXmlElementController.delChild() => item.typeof() => [object Array]');
339
+ } else if (isObject(item)) {
340
+ return xObj.deleteXObjElement(this.#_host, name);
341
+ } else {
342
+ //console.log('CHECK: TXmlElementController.delChild() => item.typeof() => ['+typeof item+']');
343
+ //console.log('CHECK: TXmlElementController.delChild() => item.value() => ['+item+']');
344
+ };
345
+ return false;
346
+ }
347
+
348
+ clear(){
349
+ TXmlElementController.clear(this);
350
+ }
351
+
352
+ static __unwrap(item){
353
+ return (item instanceof TXmlElementController) ? item.#_host : null;
354
+ }
355
+
356
+ static clear(obj){
357
+ let item = TXmlElementController.__unwrap(obj);
358
+ if (item) {
359
+ for (let prop in item) {
360
+ // // TODO: catch errors in strict mode
361
+ delete item[prop];
362
+ };
363
+ };
364
+ }
365
+
366
+ };
367
+
368
+ class TXmlElementsListController {
369
+ #_host = null;
370
+ #_options = null;
371
+ #_count = null;
372
+
373
+ constructor(obj, opt){
374
+ // load options
375
+ let _options = isPlainObject(opt) ? opt : {};
376
+ let { proxyModeEnable, isNullItemsAllowed } = _options;
377
+ if (typeof proxyModeEnable !== 'boolean') proxyModeEnable = false;
378
+ if (typeof isNullItemsAllowed !== 'boolean') isNullItemsAllowed = false;
379
+ // set parser options
380
+ let { parseOptions: _parseOptions } = _options;
381
+ if (!isPlainObject(_parseOptions)) _parseOptions = {};
382
+ let { settings: _settings } = _parseOptions;
383
+ if (!isPlainObject(_settings)) {
384
+ _parseOptions.settings = _settings = {};
385
+ // // TODO: set defaults
386
+ };
387
+ // init an elements list content
388
+ if (isArray(obj)) {
389
+ this.#_host = obj;
390
+ } else {
391
+ if (proxyModeEnable) {
392
+ let msg = [
393
+ 'TXmlElementsListController in "proxyMode":', XML_TE_NARR_EMSG,
394
+ ].join(' ');
395
+ throw new TypeError(msg);
396
+ } else {
397
+ // // TODO: split a plain object and class instances
398
+ this.#_host = [];
399
+ };
400
+ };
401
+ // save parser options
402
+ _options.parseOptions = _parseOptions;
403
+ // save options
404
+ _options.proxyModeEnable = proxyModeEnable;
405
+ _options.isNullItemsAllowed = isNullItemsAllowed;
406
+ this.#_options = _options;
407
+ }
408
+
409
+ get count(){
410
+ return this.#_host.length;
411
+ }
412
+
413
+ get size(){
414
+ return this.#_host.length;
415
+ }
416
+
417
+ get maxIndex(){
418
+ return this.#_host.length - 1;
419
+ }
420
+
421
+ get isNullItemsAllowed(){
422
+ return readAsBool(this.#_options.isNullItemsAllowed);
423
+ }
424
+
425
+ clear(){
426
+ this.#_host.length = 0;
427
+ }
428
+
429
+ isEmpty(){
430
+ return this.count === 0;
431
+ }
432
+
433
+ isNotEmpty(){
434
+ return this.count > 0;
435
+ }
436
+
437
+ chkIndex(value){
438
+ const index = valueToIndex(value);
439
+ return index !== -1 && index < this.size;
440
+ }
441
+
442
+ isValidItem(obj){
443
+ return (
444
+ (this.#_options.isNullItemsAllowed && obj === null) || isPlainObject(obj)
445
+ );
446
+ }
447
+
448
+ _getItemRaw(index){
449
+ let item = this.#_host[valueToIndex(index)];
450
+ return item !== undefined ? item : null;
451
+ }
452
+
453
+ getItem(index){
454
+ let item = this.#_host[valueToIndex(index)];
455
+ // wrap item in container
456
+ return (
457
+ isPlainObject(item)
458
+ ? new TXmlElementController(item, this.#_options)
459
+ : null
460
+ );
461
+ }
462
+
463
+ _addItemRaw(item){
464
+ let index = -1;
465
+ if (this.isValidItem(item)) {
466
+ index = this.size;
467
+ this.#_host.push(item); // // TODO: correct count
468
+ };
469
+ return index;
470
+ }
471
+
472
+ addItem(item){
473
+ // unwrap item from container
474
+ if (item instanceof TXmlElementController) {
475
+ item = TXmlElementController.__unwrap(item);
476
+ };
477
+ return this._addItemRaw(item);
478
+ }
479
+
480
+ _setItemRaw(index, item){
481
+ let isSUCCEED = this.chkIndex(index) && this.isValidItem(item);
482
+ if (isSUCCEED) this.#_host[Number(index)] = item; // // TODO: correct count
483
+ return isSUCCEED;
484
+ }
485
+
486
+ setItem(index, item){
487
+ // unwrap item from container
488
+ if (item instanceof TXmlElementController) {
489
+ item = TXmlElementController.__unwrap(item);
490
+ };
491
+ return this._setItemRaw(index, item);
492
+ }
493
+
494
+ _insItemRaw(index, item){
495
+ index = valueToIndex(index);
496
+ let size = this.size;
497
+ let isSUCCEED = (
498
+ index !== -1 && size >= index && this.isValidItem(item)
499
+ );
500
+ if (isSUCCEED) {
501
+ if (size > index) {
502
+ if (this.#_host[index] === null) {
503
+ this.#_host[index] = item; // // TODO: correct count
504
+ } else {
505
+ this.#_host.splice(index, 0, item); // // TODO: correct count
506
+ };
507
+ } else {
508
+ this.#_host.push(item); // // TODO: correct count
509
+ };
510
+ };
511
+ return isSUCCEED;
512
+ }
513
+
514
+ insItem(index, item){
515
+ // unwrap item from container
516
+ if (item instanceof TXmlElementController) {
517
+ item = TXmlElementController.__unwrap(item);
518
+ };
519
+ return this._insItemRaw(index, item);
520
+ }
521
+
522
+ delItem(value){
523
+ let isSUCCEED = this.chkIndex(value);
524
+ if (isSUCCEED) {
525
+ let index = Number(value);
526
+ if (this.#_options.isNullItemsAllowed) {
527
+ this.#_host[index] = null; // // TODO: correct count
528
+ } else {
529
+ if (index === 0) {
530
+ this.#_host.shift(); // // TODO: correct count
531
+ } else if (index === this.maxIndex) {
532
+ this.#_host.pop(); // // TODO: correct count
533
+ } else {
534
+ this.#_host.splice(index, 1); // // TODO: correct count
535
+ };
536
+ };
537
+ };
538
+ return isSUCCEED;
539
+ }
540
+
541
+ loadItems(items, opt){
542
+ let act_as_new = true;
543
+ if (isPlainObject(opt)) {
544
+ act_as_new = readAsBool(opt.load_as_new, act_as_new);
545
+ };
546
+ if (act_as_new) this.clear();
547
+ let count = this.count;
548
+ valueToArray(items).forEach((item) => { this.addItem(item); });
549
+ return this.count - count;
550
+ }
551
+
552
+ };
553
+
554
+ class TXmlContentDeclaration {
555
+ #_host = null;
556
+ #_options = null;
557
+ #_ctrls = null;
558
+
559
+ constructor(obj, opt){
560
+ // check <obj>-param
561
+ if (!isPlainObject(obj)) {
562
+ /*let msg = [
563
+ 'TXmlContentDeclaration :', XML_TE_NOBJ_EMSG,
564
+ ].join(' ');*/
565
+ throw new TypeError(`TXmlContentDeclaration : ${XML_TE_NOBJ_EMSG}`);
566
+ };
567
+ // load options
568
+ let _options = isPlainObject(opt) ? opt : {};
569
+ // set parser options
570
+ let { parseOptions: _parseOptions } = _options;
571
+ if (!isPlainObject(_parseOptions)) _parseOptions = {};
572
+ let { settings: _settings } = _parseOptions;
573
+ if (!isPlainObject(_settings)) _parseOptions.settings = _settings = {};
574
+ let { declarationKey } = _settings;
575
+ if (
576
+ typeof declarationKey !== 'string'
577
+ || ((declarationKey = declarationKey.trim()) === '')
578
+ ) {
579
+ declarationKey = XML_DEF_PARSE_OPTIONS.declarationKey;
580
+ };
581
+ // // TODO: set defaults
582
+ // save parser options
583
+ _options.parseOptions = _parseOptions;
584
+ _settings.declarationKey = declarationKey;
585
+ // save options
586
+ this.#_options = _options;
587
+ // bind a documents content
588
+ this.#_host = obj;
589
+ // init a content declaration element
590
+ this.init();
591
+ }
592
+
593
+ get version(){
594
+ return this.#_ctrls.getAttribute('version');
595
+ }
596
+
597
+ set version(value){
598
+ this.#_ctrls.setAttribute('version', value);
599
+ }
600
+
601
+ get encoding(){
602
+ return this.#_ctrls.getAttribute('encoding');
603
+ }
604
+
605
+ set encoding(value){
606
+ this.#_ctrls.setAttribute('encoding', value);
607
+ }
608
+
609
+ init(){
610
+ let _options = this.#_options;
611
+ this.#_ctrls = new TXmlElementController(
612
+ xObj.insertXObjElement(
613
+ this.#_host,
614
+ _options.parseOptions.settings.declarationKey,
615
+ ),
616
+ this._options,
617
+ );
618
+ // // TODO: set version & encoding
619
+ if (!isNotEmptyString(this.version)) this.version = '1.0';
620
+ if (!isNotEmptyString(this.encoding)) this.encoding = 'UTF-8';
621
+ }
622
+
623
+ };
624
+
625
+ class TXmlContentRootElement extends TXmlElementController {
626
+ #_content = null;
627
+ #_options = null;
628
+
629
+ constructor(obj, opt){
630
+ // check <obj>-param
631
+ if (!isPlainObject(obj)) {
632
+ throw new TypeError(`TXmlContentRootElement : ${XML_TE_NOBJ_EMSG}`);
633
+ };
634
+ // load options
635
+ const _options = isPlainObject(opt) ? opt : {};
636
+ let { rootETagName, autoBindRoot, parseOptions } = _options;
637
+ if (
638
+ typeof rootETagName !== 'string'
639
+ || ((rootETagName = rootETagName.trim()) === '')
640
+ ) {
641
+ rootETagName = XML_DEF_ROOT_ETAG_NAME;
642
+ };
643
+ if (typeof autoBindRoot !== 'boolean') autoBindRoot = true;
644
+ if (!(parseOptions instanceof TXmlContentParseOptions)) {
645
+ parseOptions = new TXmlContentParseOptions(parseOptions);
646
+ };
647
+ // get root element
648
+ let rootItem = obj[rootETagName];
649
+ if (obj[rootETagName] === undefined) {
650
+ const entries = Object.keys(obj);
651
+ const len = entries.length;
652
+ if (len === 0) {
653
+ rootItem = xObj.insertXObjElement(obj, rootETagName, false);
654
+ } else if (autoBindRoot) {
655
+ const {
656
+ declarationKey,
657
+ attributesKey,
658
+ textKey,
659
+ commentKey,
660
+ cdataKey,
661
+ nameKey,
662
+ typeKey,
663
+ parentKey,
664
+ elementsKey,
665
+ } = parseOptions.settings;
666
+ const reservedKeys = new Set([
667
+ declarationKey,
668
+ attributesKey,
669
+ textKey,
670
+ commentKey,
671
+ cdataKey,
672
+ nameKey,
673
+ typeKey,
674
+ parentKey,
675
+ elementsKey,
676
+ ]);
677
+ let targetKey = '';
678
+ for (let name of entries) {
679
+ if (!reservedKeys.has(name)) {
680
+ targetKey = name;
681
+ break;
682
+ };
683
+ };
684
+ if (targetKey === '') {
685
+ rootItem = xObj.insertXObjElement(obj, rootETagName, false);
686
+ } else {
687
+ rootItem = obj[targetKey];
688
+ rootETagName = targetKey;
689
+ if (!isPlainObject(rootItem)) {
690
+ rootItem = xObj.insertXObjElement(obj, rootETagName, false);
691
+ };
692
+ };
693
+ } else {
694
+ throw new TypeError(`TXmlContentRootElement : ${XML_TE_NROOT_EMSG}`);
695
+ };
696
+ } else if (!isPlainObject(rootItem)) {
697
+ rootItem = xObj.insertXObjElement(obj, rootETagName, false);
698
+ };
699
+ // init parent class instance
700
+ super(rootItem, _options);
701
+ // save options
702
+ _options.rootETagName = rootETagName;
703
+ _options.autoBindRoot = autoBindRoot;
704
+ _options.parseOptions = parseOptions;
705
+ this.#_options = _options;
706
+ // bind a documents content
707
+ this.#_content = obj;
708
+ // init a content root element
709
+ //this.init(rootETagName);
710
+ }
711
+
712
+ get tagName(){
713
+ return this.#_options.rootETagName;
714
+ }
715
+
716
+ set tagName(value){
717
+ value = readAsString(value, '', true);
718
+ this.#_options.rootETagName = isNotEmptyString(
719
+ value
720
+ ) ? value : XML_DEF_ROOT_ETAG_NAME;
721
+ }
722
+
723
+ //* will deprecate */
724
+ /*init(name){
725
+ //console.log('CHECK: TXmlContentRootElement.init() => was called...');
726
+ //console.log('CHECK: TXmlContentRootElement.init() => (*1) name:['+name+']');
727
+ let _options = this.#_options;
728
+ name = readAsString(name, _options.rootETagName, true);
729
+ // // TODO: check if name is valid
730
+ //console.log('CHECK: TXmlContentRootElement.init() => (*2) name:['+name+']');
731
+ if (!isPlainObject(this.#_content[name])) this.#_content[name] = {};
732
+ this.#_ctrls = new TXmlElementController(this.#_content[name], _options);
733
+ //console.log('CHECK: TXmlContentRootElement.init() => was left...');
734
+ }*/
735
+
736
+ static __unwrap(item, opt){
737
+ return (
738
+ item instanceof TXmlContentRootElement
739
+ && (typeof opt === 'boolean' ? opt : false)
740
+ ? item.#_content :
741
+ TXmlElementController.__unwrap(item)
742
+ );
743
+ }
744
+
745
+ }
746
+
747
+ class TXmlContentContainer {
748
+ #_content = null;
749
+ #_options = null;
750
+ #_parseOptions = null;
751
+ #_docDecl = null;
752
+ #_docRoot = null;
753
+
754
+ constructor(opt){
755
+ // load options
756
+ const _options = isPlainObject(opt) ? opt : {};
757
+ let { parseOptions } = _options;
758
+ if (!(parseOptions instanceof TXmlContentParseOptions)) {
759
+ parseOptions = new TXmlContentParseOptions(parseOptions);
760
+ };
761
+ // save options
762
+ _options.parseOptions = parseOptions;
763
+ this.#_options = _options;
764
+ // save parser options
765
+ this.#_parseOptions = parseOptions;
766
+ // init a documents content
767
+ let _content = {};
768
+ this.#_content = _content;
769
+ // init a content declaration element
770
+ this.#_docDecl = new TXmlContentDeclaration(_content, _options);
771
+ // init a content root element
772
+ this.#_docRoot = new TXmlContentRootElement(_content, _options);
773
+ }
774
+
775
+ get rootElement(){
776
+ return this.#_docRoot;
777
+ }
778
+
779
+ clear(){
780
+ this.#_docDecl.init();
781
+ this.rootElement.clear();
782
+ }
783
+
784
+ _bindContent(obj, opt){
785
+ //console.log('CHECK: TXmlContentContainer._bindContent() => was called...');
786
+ const _options = isPlainObject(opt) ? opt : this.#_options;
787
+ let isSUCCEED = false;
788
+ if (isPlainObject(obj)) {
789
+ let { rootETagName, parseOptions } = _options;
790
+ rootETagName = (
791
+ typeof rootETagName === 'string' ? rootETagName.trim() : ''
792
+ );
793
+ //if (rootETagName !== '' && isPlainObject(obj[rootETagName])) {
794
+ //console.log('CHECK: TXmlContentContainer._bindContent() => rootETagName:['+rootETagName+']');
795
+ if (!(parseOptions instanceof TXmlContentParseOptions)) {
796
+ parseOptions = new TXmlContentParseOptions(parseOptions);
797
+ _options.parseOptions = parseOptions;
798
+ };
799
+ const _docDecl = new TXmlContentDeclaration(obj, _options);
800
+ const _docRoot = new TXmlContentRootElement(obj, _options);
801
+ // // TODO: check elements
802
+ isSUCCEED = true;
803
+ if (isSUCCEED) {
804
+ // save options
805
+ this.#_options = _options;
806
+ // save parser options
807
+ this.#_parseOptions = parseOptions;
808
+ // save content
809
+ this.#_content = obj;
810
+ // bind refs to controllers
811
+ this.#_docDecl = _docDecl;
812
+ this.#_docRoot = _docRoot;
813
+ };
814
+ //};
815
+ };
816
+ //console.log('CHECK: TXmlContentContainer.constructor() => _content => '+JSON.stringify(this.#_content, null, 2));
817
+ //console.log('CHECK: TXmlContentContainer._bindContent() => result:['+isSUCCEED+']');
818
+ //console.log('CHECK: TXmlContentContainer._bindContent() => was left...');
819
+ return isSUCCEED;
820
+ }
821
+
822
+ saveToXMLString(){
823
+ let result = '';
824
+ //let isERR = false;
825
+ try {
826
+ result = xmlParser.js2xml(this.#_content, this.#_parseOptions.js2xml);
827
+ } catch (err) {
828
+ console.log('CHECK: TXmlContentContainer.saveToXMLString() => Error => '+err);
829
+ //isERR = true;
830
+ throw err;
831
+ };
832
+ return result;
833
+ }
834
+
835
+ saveToFile(source){
836
+ /**/// main part that return promise as a result
837
+ return new Promise((resolve, reject) => {
838
+ let data = {
839
+ isSucceed: false,
840
+ source: typeof source === 'string' ? source.trim : '',
841
+ content: '',
842
+ isERR: false,
843
+ errEvent: '',
844
+ };
845
+ if (data.source !== '') {
846
+ data.content = this.saveToXMLString();
847
+ fse.writeFile(data.source, data.content, 'utf8').then(result => {
848
+ data.isSucceed = true;
849
+ data.errEvent = 'OPS_IS_SUCCEED';
850
+ resolve(data);
851
+ }).catch(err => {
852
+ console.log('CHECK: TXmlContentContainer.saveToFile() => Error => '+err);
853
+ console.log('CHECK: TXmlContentContainer.saveToFile() => Error => '+err.code);
854
+ reject(err);
855
+ });
856
+ } else {
857
+ data.isERR = true;
858
+ data.errEvent = 'EMPTY_SRCLINK';
859
+ resolve(data);
860
+ };
861
+ });
862
+ }
863
+
864
+ saveToFileSync(source){
865
+ //console.log('CHECK: TXmlContentContainer.saveToFileSync() => was called...');
866
+ let data = {
867
+ isSucceed: false,
868
+ source: typeof source === 'string' ? source.trim() : '',
869
+ content: '',
870
+ isERR: false,
871
+ errEvent: '',
872
+ };
873
+ if (data.source !== '') {
874
+ try {
875
+ data.content = this.saveToXMLString();
876
+ fs.writeFileSync(data.source, data.content, 'utf8');
877
+ } catch (err) {
878
+ console.log('CHECK: TXmlContentContainer.saveToFileSync() => Error => '+err);
879
+ console.log('CHECK: TXmlContentContainer.saveToFileSync() => Error => '+err.code);
880
+ throw err;
881
+ };
882
+ } else {
883
+ data.isERR = true;
884
+ data.errEvent = 'EMPTY_SRCLINK';
885
+ };
886
+ //console.log('CHECK: TXmlContentContainer.saveToFileSync() => was left...');
887
+ return data;
888
+ }
889
+
890
+ loadFromXMLString(xmlString){
891
+ let isSUCCEED = false;
892
+ if (typeof xmlString === 'string') {
893
+ let obj = null;
894
+ try {
895
+ obj = xmlParser.xml2js(xmlString, this.#_parseOptions.xml2js);
896
+ if (isPlainObject(obj)) {
897
+ isSUCCEED = this._bindContent(obj, this.#_options);
898
+ };
899
+ } catch (err) {
900
+ console.log('CHECK: TXmlContentContainer.loadFromXMLString() => Error => '+err);
901
+ console.log('CHECK: TXmlContentContainer.loadFromXMLString() => Error => '+err.code);
902
+ throw err;
903
+ };
904
+ };
905
+ return isSUCCEED;
906
+ }
907
+
908
+ loadFromFile(source){
909
+ /**/// main part that return promise as a result
910
+ return new Promise((resolve, reject) => {
911
+ let data = {
912
+ isLoaded: false,
913
+ source: typeof source === 'string' ? source.trim() : '',
914
+ content: '',
915
+ isERR: false,
916
+ errEvent: '',
917
+ };
918
+ if (data.source === '') {
919
+ data.isERR = true;
920
+ data.errEvent = (
921
+ typeof source === 'string' ? 'EMPTY_SRCLINK' : 'NO_SRCLINK'
922
+ );
923
+ resolve(data);
924
+ } else {
925
+ fse.readFile(data.source, 'utf8').then(result => {
926
+ data.content = result;
927
+ if (result !== '') {
928
+ if (this.loadFromXMLString(result)) {
929
+ data.isLoaded = true;
930
+ data.errEvent = 'OPS_IS_SUCCEED';
931
+ } else {
932
+ data.isERR = true;
933
+ data.errEvent = 'OPS_IS_FAILED';
934
+ };
935
+ } else {
936
+ data.isERR = true;
937
+ data.errEvent = 'NO_CONTENT';
938
+ };
939
+ resolve(data);
940
+ }).catch(err => {
941
+ switch (err.code) {
942
+ case 'ENOENT':
943
+ case 'EISDIR':
944
+ data.isERR = true;
945
+ data.errEvent = err.code;
946
+ resolve(data);
947
+ break;
948
+ default:
949
+ console.log('CHECK: TXmlContentContainer.loadFromFile() => Error => '+err);
950
+ console.log('CHECK: TXmlContentContainer.loadFromFile() => Error => '+err.code);
951
+ reject(err);
952
+ break;
953
+ };
954
+ });
955
+ };
956
+ });
957
+ }
958
+
959
+ loadFromFileSync(source){
960
+ let data = {
961
+ isLoaded: false,
962
+ source: typeof source === 'string' ? source.trim() : '',
963
+ content: '',
964
+ isERR: false,
965
+ errEvent: '',
966
+ };
967
+ if (data.source === '') {
968
+ data.isERR = true;
969
+ data.errEvent = (
970
+ typeof source === 'string' ? 'EMPTY_SRCLINK' : 'NO_SRCLINK'
971
+ );
972
+ } else {
973
+ try {
974
+ data.content = fs.readFileSync(data.source, 'utf8');
975
+ if (data.content !== '') {
976
+ if (this.loadFromXMLString(data.content)) {
977
+ data.isLoaded = true;
978
+ data.errEvent = 'OPS_IS_SUCCEED';
979
+ } else {
980
+ data.isERR = true;
981
+ data.errEvent = 'OPS_IS_FAILED';
982
+ };
983
+ } else {
984
+ data.isERR = true;
985
+ data.errEvent = 'NO_CONTENT';
986
+ };
987
+ } catch (err) {
988
+ switch (err.code) {
989
+ case 'ENOENT':
990
+ case 'EISDIR':
991
+ data.isERR = true;
992
+ data.errEvent = err.code;
993
+ break;
994
+ default:
995
+ console.log('CHECK: TXmlContentContainer.loadFromFileSync() => Error => '+err);
996
+ console.log('CHECK: TXmlContentContainer.loadFromFileSync() => Error => '+err.code);
997
+ throw err;
998
+ break;
999
+ };
1000
+ };
1001
+ };
1002
+ return data;
1003
+ }
1004
+
1005
+ asJSON(){
1006
+ return JSON.stringify(this.#_content, null, 2);
1007
+ }
1008
+
1009
+ };
1010
+
1011
+ // === module exports block ===
1012
+
1013
+ exports.DEF_XML_PARSE_OPTIONS = XML_DEF_PARSE_OPTIONS; // will deprecate
1014
+ exports.DEF_XML_ROOT_ETAG_NAME = XML_DEF_ROOT_ETAG_NAME; // will deprecate
1015
+
1016
+ exports.XML_DEF_PARSE_OPTIONS = XML_DEF_PARSE_OPTIONS;
1017
+ exports.XML_DEF_ROOT_ETAG_NAME = XML_DEF_ROOT_ETAG_NAME;
1018
+
1019
+ exports.TXmlAttributesMapper = TXmlAttributesMapper;
1020
+ exports.TXmlElementController = TXmlElementController;
1021
+ exports.TXmlElementsListController = TXmlElementsListController;
1022
+ exports.TXmlContentParseOptions = TXmlContentParseOptions;
1023
+ exports.TXmlContentDeclaration = TXmlContentDeclaration;
1024
+ exports.TXmlContentRootElement = TXmlContentRootElement;
1025
+ exports.TXmlContentContainer = TXmlContentContainer;