native-document 1.0.151 → 1.0.153

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.
@@ -43,6 +43,191 @@ var NativeDocument = (function (exports) {
43
43
  }
44
44
  }
45
45
 
46
+ const COMMON_NODE_TYPES = {
47
+ ELEMENT: 1,
48
+ TEXT: 3,
49
+ COMMENT: 8,
50
+ DOCUMENT_FRAGMENT: 11
51
+ };
52
+
53
+ const VALID_TYPES = [];
54
+ VALID_TYPES[COMMON_NODE_TYPES.ELEMENT] = true;
55
+ VALID_TYPES[COMMON_NODE_TYPES.TEXT] = true;
56
+ VALID_TYPES[COMMON_NODE_TYPES.DOCUMENT_FRAGMENT] = true;
57
+ VALID_TYPES[COMMON_NODE_TYPES.COMMENT] = true;
58
+
59
+ const Validator = {
60
+ isObservable(value) {
61
+ return value && (value.__$isObservable || value.__$Observable);
62
+ },
63
+ isTemplateBinding(value) {
64
+ return value?.__$isTemplateBinding;
65
+ },
66
+ isObservableWhenResult(value) {
67
+ return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
68
+ },
69
+ isArrayObservable(value) {
70
+ return value?.__$isObservableArray;
71
+ },
72
+ isProxy(value) {
73
+ return value?.__isProxy__
74
+ },
75
+ isObservableOrProxy(value) {
76
+ return Validator.isObservable(value) || Validator.isProxy(value);
77
+ },
78
+ isAnchor(value) {
79
+ return value?.__Anchor__
80
+ },
81
+ isObservableChecker(value) {
82
+ return value?.__$isObservableChecker;
83
+ },
84
+ isArray(value) {
85
+ return Array.isArray(value);
86
+ },
87
+ isString(value) {
88
+ return typeof value === 'string';
89
+ },
90
+ isNumber(value) {
91
+ return typeof value === 'number';
92
+ },
93
+ isBoolean(value) {
94
+ return typeof value === 'boolean';
95
+ },
96
+ isFunction(value) {
97
+ return typeof value === 'function';
98
+ },
99
+ isAsyncFunction(value) {
100
+ return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
101
+ },
102
+ isObject(value) {
103
+ return typeof value === 'object' && value !== null;
104
+ },
105
+ isJson(value) {
106
+ return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
107
+ },
108
+ isElement(value) {
109
+ return value && VALID_TYPES[value.nodeType];
110
+ },
111
+ isDOMNode(value) {
112
+ return VALID_TYPES[value.nodeType];
113
+ },
114
+ isFragment(value) {
115
+ return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
116
+ },
117
+ isStringOrObservable(value) {
118
+ return this.isString(value) || this.isObservable(value);
119
+ },
120
+ isValidChild(child) {
121
+ return child === null ||
122
+ this.isElement(child) ||
123
+ child.__$Observable ||
124
+ child?.__$isNDElement ||
125
+ ['string', 'number', 'boolean'].includes(typeof child);
126
+ },
127
+ isNDElement(child) {
128
+ return child?.__$isNDElement;
129
+ },
130
+ isValidChildren(children) {
131
+ if (!Array.isArray(children)) {
132
+ children = [children];
133
+ }
134
+
135
+ const invalid = children.filter(child => !this.isValidChild(child));
136
+ return invalid.length === 0;
137
+ },
138
+ validateChildren(children) {
139
+ if (!Array.isArray(children)) {
140
+ children = [children];
141
+ }
142
+
143
+ const invalid = children.filter(child => !this.isValidChild(child));
144
+ if (invalid.length > 0) {
145
+ throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
146
+ }
147
+
148
+ return children;
149
+ },
150
+ /**
151
+ * Check if the data contains observables.
152
+ * @param {Array|Object} data
153
+ * @returns {boolean}
154
+ */
155
+ containsObservables(data) {
156
+ if(!data) {
157
+ return false;
158
+ }
159
+ return Validator.isObject(data)
160
+ && Object.values(data).some(value => Validator.isObservable(value));
161
+ },
162
+ /**
163
+ * Check if the data contains an observable reference.
164
+ * @param {string} data
165
+ * @returns {boolean}
166
+ */
167
+ containsObservableReference(data) {
168
+ if(!data || typeof data !== 'string') {
169
+ return false;
170
+ }
171
+ return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
172
+ },
173
+ validateAttributes(attributes) {},
174
+
175
+ validateEventCallback(callback) {
176
+ if (typeof callback !== 'function') {
177
+ throw new NativeDocumentError('Event callback must be a function');
178
+ }
179
+ }
180
+ };
181
+ {
182
+ Validator.validateAttributes = function(attributes) {
183
+ if (!attributes || typeof attributes !== 'object') {
184
+ return attributes;
185
+ }
186
+
187
+ const reserved = [];
188
+ const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
189
+
190
+ if (foundReserved.length > 0) {
191
+ DebugManager$2.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
192
+ }
193
+
194
+ return attributes;
195
+ };
196
+ }
197
+
198
+ const BOOLEAN_ATTRIBUTES = new Set([
199
+ 'checked',
200
+ 'selected',
201
+ 'disabled',
202
+ 'readonly',
203
+ 'required',
204
+ 'autofocus',
205
+ 'multiple',
206
+ 'autocomplete',
207
+ 'hidden',
208
+ 'contenteditable',
209
+ 'spellcheck',
210
+ 'translate',
211
+ 'draggable',
212
+ 'async',
213
+ 'defer',
214
+ 'autoplay',
215
+ 'controls',
216
+ 'loop',
217
+ 'muted',
218
+ 'download',
219
+ 'reversed',
220
+ 'open',
221
+ 'default',
222
+ 'formnovalidate',
223
+ 'novalidate',
224
+ 'scoped',
225
+ 'itemscope',
226
+ 'allowfullscreen',
227
+ 'allowpaymentrequest',
228
+ 'playsinline'
229
+ ]);
230
+
46
231
  const MemoryManager = (function() {
47
232
 
48
233
  let $nextObserverId = 0;
@@ -176,60 +361,6 @@ var NativeDocument = (function (exports) {
176
361
 
177
362
  var PluginsManager$1 = PluginsManager;
178
363
 
179
- /**
180
- * Creates an ObservableWhen that tracks whether an observable equals a specific value.
181
- *
182
- * @param {ObservableItem} observer - The observable to watch
183
- * @param {*} value - The value to compare against
184
- * @class ObservableWhen
185
- */
186
- const ObservableWhen = function(observer, value) {
187
- this.$target = value;
188
- this.$observer = observer;
189
- };
190
-
191
- ObservableWhen.prototype.__$Observable = true;
192
- ObservableWhen.prototype.__$isObservableWhen = true;
193
-
194
- /**
195
- * Subscribes to changes in the match status (true when observable equals target value).
196
- *
197
- * @param {Function} callback - Function called with boolean indicating if values match
198
- * @returns {Function} Unsubscribe function
199
- * @example
200
- * const status = Observable('idle');
201
- * const isLoading = status.when('loading');
202
- * isLoading.subscribe(active => console.log('Loading:', active));
203
- */
204
- ObservableWhen.prototype.subscribe = function(callback) {
205
- return this.$observer.on(this.$target, callback);
206
- };
207
-
208
- /**
209
- * Returns true if the observable's current value equals the target value.
210
- *
211
- * @returns {boolean} True if observable value matches target value
212
- */
213
- ObservableWhen.prototype.val = function() {
214
- return this.$observer.$currentValue === this.$target;
215
- };
216
-
217
- /**
218
- * Returns true if the observable's current value equals the target value.
219
- * Alias for val().
220
- *
221
- * @returns {boolean} True if observable value matches target value
222
- */
223
- ObservableWhen.prototype.isMatch = ObservableWhen.prototype.val;
224
-
225
- /**
226
- * Returns true if the observable's current value equals the target value.
227
- * Alias for val().
228
- *
229
- * @returns {boolean} True if observable value matches target value
230
- */
231
- ObservableWhen.prototype.isActive = ObservableWhen.prototype.val;
232
-
233
364
  const nextTick = function(fn) {
234
365
  let pending = false;
235
366
  return function(...args) {
@@ -306,201 +437,40 @@ var NativeDocument = (function (exports) {
306
437
  return cloned;
307
438
  };
308
439
 
309
- const $parseDateParts = (value, locale) => {
310
- const d = new Date(value);
311
- return {
312
- d,
313
- parts: new Intl.DateTimeFormat(locale, {
314
- year: 'numeric',
315
- month: 'long',
316
- day: '2-digit',
317
- hour: '2-digit',
318
- minute: '2-digit',
319
- second: '2-digit',
320
- }).formatToParts(d).reduce((acc, { type, value }) => {
321
- acc[type] = value;
322
- return acc;
323
- }, {})
324
- };
325
- };
326
-
327
- const $applyDatePattern = (pattern, d, parts) => {
328
- const pad = n => String(n).padStart(2, '0');
329
- return pattern
330
- .replace('YYYY', parts.year)
331
- .replace('YY', parts.year.slice(-2))
332
- .replace('MMMM', parts.month)
333
- .replace('MMM', parts.month.slice(0, 3))
334
- .replace('MM', pad(d.getMonth() + 1))
335
- .replace('DD', pad(d.getDate()))
336
- .replace('D', d.getDate())
337
- .replace('HH', parts.hour)
338
- .replace('mm', parts.minute)
339
- .replace('ss', parts.second);
340
- };
341
-
342
- const Formatters = {
343
- currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
344
- new Intl.NumberFormat(locale, {
345
- style: 'currency',
346
- currency,
347
- notation,
348
- minimumFractionDigits,
349
- maximumFractionDigits
350
- }).format(value),
351
-
352
- number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
353
- new Intl.NumberFormat(locale, {
354
- notation,
355
- minimumFractionDigits,
356
- maximumFractionDigits
357
- }).format(value),
358
-
359
- percent: (value, locale, { decimals = 1 } = {}) =>
360
- new Intl.NumberFormat(locale, {
361
- style: 'percent',
362
- maximumFractionDigits: decimals
363
- }).format(value),
364
-
365
- date: (value, locale, { format, dateStyle = 'long' } = {}) => {
366
- if (format) {
367
- const { d, parts } = $parseDateParts(value, locale);
368
- return $applyDatePattern(format, d, parts);
369
- }
370
- return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
371
- },
372
-
373
- time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
374
- if (format) {
375
- const { d, parts } = $parseDateParts(value, locale);
376
- return $applyDatePattern(format, d, parts);
440
+ const LocalStorage = {
441
+ getJson(key) {
442
+ let value = localStorage.getItem(key);
443
+ try {
444
+ return JSON.parse(value);
445
+ } catch (e) {
446
+ throw new NativeDocumentError('invalid_json:'+key);
377
447
  }
378
- return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
379
448
  },
380
-
381
- datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
382
- if (format) {
383
- const { d, parts } = $parseDateParts(value, locale);
384
- return $applyDatePattern(format, d, parts);
385
- }
386
- return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
449
+ getNumber(key) {
450
+ return Number(this.get(key));
387
451
  },
388
-
389
- relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
390
- const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
391
- return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
452
+ getBool(key) {
453
+ const value = this.get(key);
454
+ return value === 'true' || value === '1';
392
455
  },
393
-
394
- plural: (value, locale, { singular, plural } = {}) => {
395
- const rule = new Intl.PluralRules(locale).select(value);
396
- return `${value} ${rule === 'one' ? singular : plural}`;
456
+ setJson(key, value) {
457
+ localStorage.setItem(key, JSON.stringify(value));
458
+ },
459
+ setBool(key, value) {
460
+ localStorage.setItem(key, value ? 'true' : 'false');
461
+ },
462
+ get(key, defaultValue = null) {
463
+ return localStorage.getItem(key) || defaultValue;
464
+ },
465
+ set(key, value) {
466
+ return localStorage.setItem(key, value);
467
+ },
468
+ remove(key) {
469
+ localStorage.removeItem(key);
397
470
  },
398
- };
399
-
400
- /**
401
- *
402
- * @param {*} value
403
- * @param {{ propagation: boolean, reset: boolean} | null} configs
404
- * @returns {ObservableItem}
405
- * @constructor
406
- */
407
- function Observable(value, configs = null) {
408
- return new ObservableItem(value, configs);
409
- }
410
-
411
- const $ = Observable;
412
- const obs = Observable;
413
-
414
- /**
415
- *
416
- * @param {string} propertyName
417
- */
418
- Observable.useValueProperty = function(propertyName = 'value') {
419
- Object.defineProperty(ObservableItem.prototype, propertyName, {
420
- get() {
421
- return this.$currentValue;
422
- },
423
- set(value) {
424
- this.set(value);
425
- },
426
- configurable: true,
427
- });
428
- };
429
-
430
-
431
- /**
432
- *
433
- * @param id
434
- * @returns {ObservableItem|null}
435
- */
436
- Observable.getById = function(id) {
437
- const item = MemoryManager.getObservableById(parseInt(id));
438
- if(!item) {
439
- throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
440
- }
441
- return item;
442
- };
443
-
444
- /**
445
- *
446
- * @param {ObservableItem} observable
447
- */
448
- Observable.cleanup = function(observable) {
449
- observable.cleanup();
450
- };
451
-
452
- /**
453
- * Enable auto cleanup of observables.
454
- * @param {Boolean} enable
455
- * @param {{interval:Boolean, threshold:number}} options
456
- */
457
- Observable.autoCleanup = function(enable = false, options = {}) {
458
- if(!enable) {
459
- return;
460
- }
461
- const { interval = 60000, threshold = 100 } = options;
462
-
463
- window.addEventListener('beforeunload', () => {
464
- MemoryManager.cleanup();
465
- });
466
-
467
- setInterval(() => MemoryManager.cleanObservables(threshold), interval);
468
- };
469
-
470
- const LocalStorage = {
471
- getJson(key) {
472
- let value = localStorage.getItem(key);
473
- try {
474
- return JSON.parse(value);
475
- } catch (e) {
476
- throw new NativeDocumentError('invalid_json:'+key);
477
- }
478
- },
479
- getNumber(key) {
480
- return Number(this.get(key));
481
- },
482
- getBool(key) {
483
- const value = this.get(key);
484
- return value === 'true' || value === '1';
485
- },
486
- setJson(key, value) {
487
- localStorage.setItem(key, JSON.stringify(value));
488
- },
489
- setBool(key, value) {
490
- localStorage.setItem(key, value ? 'true' : 'false');
491
- },
492
- get(key, defaultValue = null) {
493
- return localStorage.getItem(key) || defaultValue;
494
- },
495
- set(key, value) {
496
- return localStorage.setItem(key, value);
497
- },
498
- remove(key) {
499
- localStorage.removeItem(key);
500
- },
501
- has(key) {
502
- return localStorage.getItem(key) != null;
503
- }
471
+ has(key) {
472
+ return localStorage.getItem(key) != null;
473
+ }
504
474
  };
505
475
 
506
476
  const $getFromStorage = (key, value) => {
@@ -876,27 +846,6 @@ var NativeDocument = (function (exports) {
876
846
  }
877
847
  };
878
848
 
879
- /**
880
- * Create an Observable checker instance
881
- * @param callback
882
- * @returns {ObservableChecker}
883
- */
884
- ObservableItem.prototype.check = function(callback) {
885
- return new ObservableChecker(this, callback)
886
- };
887
-
888
- ObservableItem.prototype.transform = ObservableItem.prototype.check;
889
- ObservableItem.prototype.pluck = function(property) {
890
- return new ObservableChecker(this, (value) => value[property]);
891
- };
892
- ObservableItem.prototype.is = function(callbackOrValue) {
893
- if(typeof callbackOrValue === 'function') {
894
- return new ObservableChecker(this, callbackOrValue);
895
- }
896
- return new ObservableChecker(this, (value) => value === callbackOrValue);
897
- };
898
- ObservableItem.prototype.select = ObservableItem.prototype.check;
899
-
900
849
 
901
850
 
902
851
 
@@ -916,21 +865,6 @@ var NativeDocument = (function (exports) {
916
865
  return Validator.isObservable(item) ? item.val() : item;
917
866
  };
918
867
 
919
- /**
920
- * Creates an ObservableWhen that represents whether the observable equals a specific value.
921
- * Returns an object that can be subscribed to and will emit true/false.
922
- *
923
- * @param {*} value - The value to compare against
924
- * @returns {ObservableWhen} An ObservableWhen instance that tracks when the observable equals the value
925
- * @example
926
- * const status = Observable('idle');
927
- * const isLoading = status.when('loading');
928
- * isLoading.subscribe(active => console.log('Loading:', active));
929
- * status.set('loading'); // Logs: "Loading: true"
930
- */
931
- ObservableItem.prototype.when = function(value) {
932
- return new ObservableWhen(this, value);
933
- };
934
868
 
935
869
  /**
936
870
  * Compares the observable's current value with another value or observable.
@@ -1018,79 +952,6 @@ var NativeDocument = (function (exports) {
1018
952
  };
1019
953
 
1020
954
 
1021
- /**
1022
- * Creates a derived observable that formats the current value using Intl.
1023
- * Automatically reacts to both value changes and locale changes (Store.__nd.locale).
1024
- *
1025
- * @param {string | Function} type - Format type or custom formatter function
1026
- * @param {Object} [options={}] - Options passed to the formatter
1027
- * @returns {ObservableItem<string>}
1028
- *
1029
- * @example
1030
- * // Currency
1031
- * price.format('currency') // "15 000 FCFA"
1032
- * price.format('currency', { currency: 'EUR' }) // "15 000,00 €"
1033
- * price.format('currency', { notation: 'compact' }) // "15 K FCFA"
1034
- *
1035
- * // Number
1036
- * count.format('number') // "15 000"
1037
- *
1038
- * // Percent
1039
- * rate.format('percent') // "15,0 %"
1040
- * rate.format('percent', { decimals: 2 }) // "15,00 %"
1041
- *
1042
- * // Date
1043
- * date.format('date') // "3 mars 2026"
1044
- * date.format('date', { dateStyle: 'full' }) // "mardi 3 mars 2026"
1045
- * date.format('date', { format: 'DD/MM/YYYY' }) // "03/03/2026"
1046
- * date.format('date', { format: 'DD MMM YYYY' }) // "03 mar 2026"
1047
- * date.format('date', { format: 'DD MMMM YYYY' }) // "03 mars 2026"
1048
- *
1049
- * // Time
1050
- * date.format('time') // "20:30"
1051
- * date.format('time', { second: '2-digit' }) // "20:30:00"
1052
- * date.format('time', { format: 'HH:mm:ss' }) // "20:30:00"
1053
- *
1054
- * // Datetime
1055
- * date.format('datetime') // "3 mars 2026, 20:30"
1056
- * date.format('datetime', { dateStyle: 'full' }) // "mardi 3 mars 2026, 20:30"
1057
- * date.format('datetime', { format: 'DD/MM/YYYY HH:mm' }) // "03/03/2026 20:30"
1058
- *
1059
- * // Relative
1060
- * date.format('relative') // "dans 11 jours"
1061
- * date.format('relative', { unit: 'month' }) // "dans 1 mois"
1062
- *
1063
- * // Plural
1064
- * count.format('plural', { singular: 'billet', plural: 'billets' }) // "3 billets"
1065
- *
1066
- * // Custom formatter
1067
- * price.format(value => `${value.toLocaleString()} FCFA`)
1068
- *
1069
- * // Reacts to locale changes automatically
1070
- * Store.setLocale('en-US');
1071
- */
1072
- ObservableItem.prototype.format = function(type, options = {}) {
1073
- const self = this;
1074
-
1075
- if (typeof type === 'function') {
1076
- return new ObservableChecker(self, type);
1077
- }
1078
-
1079
- {
1080
- if (!Formatters[type]) {
1081
- throw new NativeDocumentError(
1082
- `Observable.format : unknown type '${type}'. Available : ${Object.keys(Formatters).join(', ')}.`
1083
- );
1084
- }
1085
- }
1086
-
1087
- const formatter = Formatters[type];
1088
- const localeObservable = Formatters.locale;
1089
-
1090
- return Observable.computed(() => formatter(self.val(), localeObservable.val(), options),
1091
- [self, localeObservable]
1092
- );
1093
- };
1094
955
 
1095
956
  ObservableItem.prototype.persist = function(key, options = {}) {
1096
957
  let value = $getFromStorage(key, this.$currentValue);
@@ -1121,279 +982,98 @@ var NativeDocument = (function (exports) {
1121
982
 
1122
983
  /**
1123
984
  *
1124
- * @param {ObservableItem} $observable
1125
- * @param {Function} $checker
1126
- * @class ObservableChecker
985
+ * @param {*} value
986
+ * @param {{ propagation: boolean, reset: boolean} | null} configs
987
+ * @returns {ObservableItem}
988
+ * @constructor
1127
989
  */
1128
- function ObservableChecker($observable, $checker) {
1129
- this.observable = $observable;
1130
-
1131
- ObservableItem.call(this);
1132
- {
1133
- PluginsManager$1.emit('CreateObservableChecker', this);
1134
- }
990
+ function Observable(value, configs = null) {
991
+ return new ObservableItem(value, configs);
992
+ }
1135
993
 
1136
- this.$mutation = $checker;
994
+ const $ = Observable;
995
+ const obs = Observable;
1137
996
 
1138
- $observable.subscribe((newValue) => {
1139
- this.$updateWithMutation(newValue);
997
+ /**
998
+ *
999
+ * @param {string} propertyName
1000
+ */
1001
+ Observable.useValueProperty = function(propertyName = 'value') {
1002
+ Object.defineProperty(ObservableItem.prototype, propertyName, {
1003
+ get() {
1004
+ return this.$currentValue;
1005
+ },
1006
+ set(value) {
1007
+ this.set(value);
1008
+ },
1009
+ configurable: true,
1140
1010
  });
1011
+ };
1141
1012
 
1142
- this.$updateWithMutation($observable.val());
1143
- }
1144
1013
 
1145
- ObservableChecker.prototype = Object.create(ObservableItem.prototype);
1146
- ObservableChecker.prototype.constructor = ObservableChecker;
1147
- ObservableChecker.prototype.__$Observable = true;
1148
- ObservableChecker.prototype.__$isObservableChecker = true;
1014
+ /**
1015
+ *
1016
+ * @param id
1017
+ * @returns {ObservableItem|null}
1018
+ */
1019
+ Observable.getById = function(id) {
1020
+ const item = MemoryManager.getObservableById(parseInt(id));
1021
+ if(!item) {
1022
+ throw new NativeDocumentError('Observable.getById : No observable found with id ' + id);
1023
+ }
1024
+ return item;
1025
+ };
1026
+
1027
+ /**
1028
+ *
1029
+ * @param {ObservableItem} observable
1030
+ */
1031
+ Observable.cleanup = function(observable) {
1032
+ observable.cleanup();
1033
+ };
1149
1034
 
1035
+ /**
1036
+ * Enable auto cleanup of observables.
1037
+ * @param {Boolean} enable
1038
+ * @param {{interval:Boolean, threshold:number}} options
1039
+ */
1040
+ Observable.autoCleanup = function(enable = false, options = {}) {
1041
+ if(!enable) {
1042
+ return;
1043
+ }
1044
+ const { interval = 60000, threshold = 100 } = options;
1150
1045
 
1151
- const ObservablePipe = ObservableChecker;
1152
- ObservablePipe.prototype.constructor = ObservablePipe;
1046
+ window.addEventListener('beforeunload', () => {
1047
+ MemoryManager.cleanup();
1048
+ });
1153
1049
 
1154
- ObservableChecker.prototype.$updateWithMutation = function(newValue) {
1155
- newValue = this.$mutation(newValue);
1156
- return this.set(newValue);
1050
+ setInterval(() => MemoryManager.cleanObservables(threshold), interval);
1157
1051
  };
1158
1052
 
1159
- const DocumentObserver = {
1160
- mounted: new WeakMap(),
1161
- beforeUnmount: new WeakMap(),
1162
- mountedSupposedSize: 0,
1163
- unmounted: new WeakMap(),
1164
- unmountedSupposedSize: 0,
1165
- observer: null,
1166
- initObserver: () => {
1167
- if(DocumentObserver.observer) {
1168
- return;
1169
- }
1170
- DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
1171
- DocumentObserver.observer.observe(document.body, {
1172
- childList: true,
1173
- subtree: true,
1174
- });
1175
- },
1176
-
1177
- executeMountedCallback(node) {
1178
- const data = DocumentObserver.mounted.get(node);
1179
- if(!data) {
1180
- return;
1181
- }
1182
- data.inDom = true;
1183
- if(!data.mounted) {
1184
- return;
1185
- }
1186
- if(Array.isArray(data.mounted)) {
1187
- for(const cb of data.mounted) {
1188
- cb(node);
1189
- }
1190
- return;
1191
- }
1192
- data.mounted(node);
1193
- },
1194
-
1195
- executeUnmountedCallback(node) {
1196
- const data = DocumentObserver.unmounted.get(node);
1197
- if(!data) {
1198
- return;
1199
- }
1200
- data.inDom = false;
1201
- if(!data.unmounted) {
1202
- return;
1203
- }
1204
-
1205
- let shouldRemove = false;
1206
- if(Array.isArray(data.unmounted)) {
1207
- for(const cb of data.unmounted) {
1208
- if(cb(node) === true) {
1209
- shouldRemove = true;
1210
- }
1211
- }
1212
- } else {
1213
- shouldRemove = data.unmounted(node) === true;
1214
- }
1215
-
1216
- if(shouldRemove) {
1217
- data.disconnect();
1218
- node.nd?.remove();
1219
- }
1220
- },
1221
-
1222
- checkMutation: function(mutationsList) {
1223
- for(const mutation of mutationsList) {
1224
- if(DocumentObserver.mountedSupposedSize > 0) {
1225
- for(const node of mutation.addedNodes) {
1226
- DocumentObserver.executeMountedCallback(node);
1227
- if(!node.querySelectorAll) {
1228
- continue;
1229
- }
1230
- const children = node.querySelectorAll('[data--nd-mounted]');
1231
- for(const child of children) {
1232
- DocumentObserver.executeMountedCallback(child);
1233
- }
1234
- }
1235
- }
1236
-
1237
- if (DocumentObserver.unmountedSupposedSize > 0) {
1238
- for (const node of mutation.removedNodes) {
1239
- DocumentObserver.executeUnmountedCallback(node);
1240
- if(!node.querySelectorAll) {
1241
- continue;
1242
- }
1243
- const children = node.querySelectorAll('[data--nd-unmounted]');
1244
- for(const child of children) {
1245
- DocumentObserver.executeUnmountedCallback(child);
1246
- }
1247
- }
1248
- }
1249
- }
1250
- },
1251
-
1252
- /**
1253
- * @param {HTMLElement} element
1254
- * @param {boolean} inDom
1255
- * @returns {{ disconnect: Function, mounted: Function, unmounted: Function, off: Function }}
1256
- */
1257
- watch: function(element, inDom = false) {
1258
- let mountedRegistered = false;
1259
- let unmountedRegistered = false;
1260
-
1261
- DocumentObserver.initObserver();
1262
-
1263
- let data = {
1264
- inDom,
1265
- mounted: null,
1266
- unmounted: null,
1267
- disconnect: () => {
1268
- if (mountedRegistered) {
1269
- DocumentObserver.mounted.delete(element);
1270
- DocumentObserver.mountedSupposedSize--;
1271
- }
1272
- if (unmountedRegistered) {
1273
- DocumentObserver.unmounted.delete(element);
1274
- DocumentObserver.unmountedSupposedSize--;
1275
- }
1276
- data = null;
1277
- }
1278
- };
1279
-
1280
- const addListener = (type, callback) => {
1281
- if (!data[type]) {
1282
- data[type] = callback;
1283
- return;
1284
- }
1285
- if (!Array.isArray(data[type])) {
1286
- data[type] = [data[type], callback];
1287
- return;
1288
- }
1289
- data[type].push(callback);
1290
- };
1291
-
1292
- const removeListener = (type, callback) => {
1293
- if(!data?.[type]) {
1294
- return;
1295
- }
1296
- if(Array.isArray(data[type])) {
1297
- const index = data[type].indexOf(callback);
1298
- if(index > -1) {
1299
- data[type].splice(index, 1);
1300
- }
1301
- if(data[type].length === 1) {
1302
- data[type] = data[type][0];
1303
- }
1304
- if(data[type].length === 0) {
1305
- data[type] = null;
1306
- }
1307
- return;
1308
- }
1309
- data[type] = null;
1310
- };
1311
-
1312
- return {
1313
- disconnect: () => data?.disconnect(),
1314
-
1315
- mounted: (callback) => {
1316
- addListener('mounted', callback);
1317
- DocumentObserver.mounted.set(element, data);
1318
- if (!mountedRegistered) {
1319
- DocumentObserver.mountedSupposedSize++;
1320
- mountedRegistered = true;
1321
- }
1322
- },
1323
-
1324
- unmounted: (callback) => {
1325
- addListener('unmounted', callback);
1326
- DocumentObserver.unmounted.set(element, data);
1327
- if (!unmountedRegistered) {
1328
- DocumentObserver.unmountedSupposedSize++;
1329
- unmountedRegistered = true;
1330
- }
1331
- },
1332
-
1333
- off: (type, callback) => {
1334
- removeListener(type, callback);
1335
- }
1336
- };
1337
- }
1338
- };
1339
-
1340
- const BOOLEAN_ATTRIBUTES = new Set([
1341
- 'checked',
1342
- 'selected',
1343
- 'disabled',
1344
- 'readonly',
1345
- 'required',
1346
- 'autofocus',
1347
- 'multiple',
1348
- 'autocomplete',
1349
- 'hidden',
1350
- 'contenteditable',
1351
- 'spellcheck',
1352
- 'translate',
1353
- 'draggable',
1354
- 'async',
1355
- 'defer',
1356
- 'autoplay',
1357
- 'controls',
1358
- 'loop',
1359
- 'muted',
1360
- 'download',
1361
- 'reversed',
1362
- 'open',
1363
- 'default',
1364
- 'formnovalidate',
1365
- 'novalidate',
1366
- 'scoped',
1367
- 'itemscope',
1368
- 'allowfullscreen',
1369
- 'allowpaymentrequest',
1370
- 'playsinline'
1371
- ]);
1372
-
1373
- /**
1374
- *
1375
- * @param {HTMLElement} element
1376
- * @param {Object} data
1377
- */
1378
- const bindClassAttribute = (element, data) => {
1379
- for(const className in data) {
1380
- const value = data[className];
1381
- if(value.__$Observable) {
1382
- if(value.__$isObservableChecker) {
1383
- let lastClass = value.val();
1384
- if(typeof lastClass === "string") {
1385
- element.classes.toggle(lastClass, true);
1386
- value.subscribe((currentValue) => {
1387
- element.classes.remove(lastClass);
1388
- element.classes.toggle(currentValue, true);
1389
- lastClass = currentValue;
1390
- });
1391
- continue;
1392
- }
1393
- }
1394
- element.classes.toggle(className, value.val());
1395
- value.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
1396
- continue;
1053
+ /**
1054
+ *
1055
+ * @param {HTMLElement} element
1056
+ * @param {Object} data
1057
+ */
1058
+ const bindClassAttribute = (element, data) => {
1059
+ for(const className in data) {
1060
+ const value = data[className];
1061
+ if(value.__$Observable) {
1062
+ if(value.__$isObservableChecker) {
1063
+ let lastClass = value.val();
1064
+ if(typeof lastClass === "string") {
1065
+ element.classes.toggle(lastClass, true);
1066
+ value.subscribe((currentValue) => {
1067
+ element.classes.remove(lastClass);
1068
+ element.classes.toggle(currentValue, true);
1069
+ lastClass = currentValue;
1070
+ });
1071
+ continue;
1072
+ }
1073
+ }
1074
+ element.classes.toggle(className, value.val());
1075
+ value.subscribe((shouldAdd) => element.classes.toggle(className, shouldAdd));
1076
+ continue;
1397
1077
  }
1398
1078
  if(value.$hydrate) {
1399
1079
  value.$hydrate(element, className);
@@ -1428,281 +1108,111 @@ var NativeDocument = (function (exports) {
1428
1108
  value.subscribe((newValue) => {
1429
1109
  if(newValue === false) {
1430
1110
  element.style.removeProperty(styleName);
1431
- return;
1432
- }
1433
- element.style[styleName] = newValue;
1434
- });
1435
- }
1436
- continue;
1437
- }
1438
-
1439
- if(isCustomProperty) {
1440
- element.style.setProperty(styleName, value);
1441
- continue;
1442
- }
1443
-
1444
- element.style[styleName] = value;
1445
- }
1446
- };
1447
-
1448
- /**
1449
- *
1450
- * @param {HTMLElement} element
1451
- * @param {string} attributeName
1452
- * @param {boolean|number|Observable} value
1453
- */
1454
- const bindBooleanAttribute = (element, attributeName, value) => {
1455
- const isObservable = value.__$isObservable;
1456
- const defaultValue = isObservable? value.val() : value;
1457
- if(Validator.isBoolean(defaultValue)) {
1458
- element[attributeName] = defaultValue;
1459
- }
1460
- else {
1461
- element[attributeName] = defaultValue === element.value;
1462
- }
1463
- if(isObservable) {
1464
- if(attributeName === 'checked') {
1465
- if(typeof defaultValue === 'boolean') {
1466
- element.addEventListener('input', () => value.set(element[attributeName]));
1467
- }
1468
- else {
1469
- element.addEventListener('input', () => value.set(element.value));
1470
- }
1471
- value.subscribe((newValue) => element[attributeName] = newValue);
1472
- return;
1473
- }
1474
- value.subscribe((newValue) => element[attributeName] = (newValue === element.value));
1475
- }
1476
- };
1477
-
1478
-
1479
- /**
1480
- *
1481
- * @param {HTMLElement} element
1482
- * @param {string} attributeName
1483
- * @param {Observable} value
1484
- */
1485
- const bindAttributeWithObservable = (element, attributeName, value) => {
1486
- const applyValue = attributeName === 'value' ? (newValue) => element.value = newValue : (newValue) => element.setAttribute(attributeName, newValue);
1487
- value.subscribe(applyValue);
1488
-
1489
- if(attributeName === 'value') {
1490
- element.value = value.val();
1491
- element.addEventListener('input', () => value.set(element.value));
1492
- return;
1493
- }
1494
- element.setAttribute(attributeName, value.val());
1495
- };
1496
-
1497
- /**
1498
- *
1499
- * @param {HTMLElement} element
1500
- * @param {Object} attributes
1501
- */
1502
- const AttributesWrapper = (element, attributes) => {
1503
-
1504
- {
1505
- Validator.validateAttributes(attributes);
1506
- }
1507
-
1508
- for(const originalAttributeName in attributes) {
1509
- const attributeName = originalAttributeName.toLowerCase();
1510
- let value = attributes[originalAttributeName];
1511
- if(value == null) {
1512
- continue;
1513
- }
1514
- if(value.handleNdAttribute) {
1515
- value.handleNdAttribute(element, attributeName, value);
1516
- continue;
1517
- }
1518
- if(typeof value === 'object') {
1519
- if(attributeName === 'class') {
1520
- bindClassAttribute(element, value);
1521
- continue;
1522
- }
1523
- if(attributeName === 'style') {
1524
- bindStyleAttribute(element, value);
1525
- continue;
1526
- }
1527
- }
1528
- if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
1529
- bindBooleanAttribute(element, attributeName, value);
1530
- continue;
1531
- }
1532
-
1533
- element.setAttribute(attributeName, value);
1534
- }
1535
- return element;
1536
- };
1537
-
1538
- function TemplateBinding(hydrate) {
1539
- this.$hydrate = hydrate;
1540
- }
1541
-
1542
- TemplateBinding.prototype.__$isTemplateBinding = true;
1543
-
1544
- String.prototype.toNdElement = function () {
1545
- return ElementCreator.createStaticTextNode(null, this);
1546
- };
1547
-
1548
- Number.prototype.toNdElement = function () {
1549
- return ElementCreator.createStaticTextNode(null, this.toString());
1550
- };
1551
-
1552
- Element.prototype.toNdElement = function () {
1553
- return this;
1554
- };
1555
- Text.prototype.toNdElement = function () {
1556
- return this;
1557
- };
1558
- Comment.prototype.toNdElement = function () {
1559
- return this;
1560
- };
1561
- Document.prototype.toNdElement = function () {
1562
- return this;
1563
- };
1564
- DocumentFragment.prototype.toNdElement = function () {
1565
- return this;
1566
- };
1567
-
1568
- ObservableItem.prototype.toNdElement = function () {
1569
- return ElementCreator.createObservableNode(null, this);
1570
- };
1571
-
1572
- ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
1573
-
1574
- NDElement.prototype.toNdElement = function () {
1575
- const element = this.$element ?? this.$build?.() ?? this.build?.() ?? null;
1576
- if(this.$attachements) {
1577
- if(!this.$attachements.contains(this.$element)) {
1578
- this.$attachements.append(this.$element);
1579
- }
1580
- return this.$attachements;
1581
- }
1582
- return element;
1583
- };
1584
-
1585
- Array.prototype.toNdElement = function () {
1586
- const fragment = document.createDocumentFragment();
1587
- for(let i = 0, length = this.length; i < length; i++) {
1588
- const child = ElementCreator.getChild(this[i]);
1589
- if(child === null) continue;
1590
- fragment.appendChild(child);
1591
- }
1592
- return fragment;
1593
- };
1594
-
1595
- Function.prototype.toNdElement = function () {
1596
- const child = this;
1597
- {
1598
- PluginsManager$1.emit('BeforeProcessComponent', child);
1599
- }
1600
- return ElementCreator.getChild(child());
1601
- };
1602
-
1603
- TemplateBinding.prototype.toNdElement = function () {
1604
- return ElementCreator.createHydratableNode(null, this);
1605
- };
1606
-
1607
- /**
1608
- * @param {HTMLElement} el
1609
- * @param {number} timeout
1610
- */
1611
- const waitForVisualEnd = (el, timeout = 1000) => {
1612
- return new Promise((resolve) => {
1613
- let isResolved = false;
1614
-
1615
- const cleanupAndResolve = (e) => {
1616
- if (e && e.target !== el) return;
1617
- if (isResolved) return;
1618
-
1619
- isResolved = true;
1620
- el.removeEventListener('transitionend', cleanupAndResolve);
1621
- el.removeEventListener('animationend', cleanupAndResolve);
1622
- clearTimeout(timer);
1623
- resolve();
1624
- };
1625
-
1626
- el.addEventListener('transitionend', cleanupAndResolve);
1627
- el.addEventListener('animationend', cleanupAndResolve);
1628
-
1629
- const timer = setTimeout(cleanupAndResolve, timeout);
1630
-
1631
- const style = window.getComputedStyle(el);
1632
- const hasTransition = style.transitionDuration !== '0s';
1633
- const hasAnimation = style.animationDuration !== '0s';
1634
-
1635
- if (!hasTransition && !hasAnimation) {
1636
- cleanupAndResolve();
1637
- }
1638
- });
1639
- };
1640
-
1641
- NDElement.prototype.transitionOut = function(transitionName) {
1642
- const exitClass = transitionName + '-exit';
1643
- const el = this.$element;
1644
- this.beforeUnmount('transition-exit', async function() {
1645
- el.classes.add(exitClass);
1646
- await waitForVisualEnd(el);
1647
- el.classes.remove(exitClass);
1648
- });
1649
- return this;
1650
- };
1651
-
1652
- NDElement.prototype.transitionIn = function(transitionName) {
1653
- const startClass = transitionName + '-enter-from';
1654
- const endClass = transitionName + '-enter-to';
1655
-
1656
- const el = this.$element;
1657
-
1658
- el.classes.add(startClass);
1659
-
1660
- this.mounted(() => {
1661
- requestAnimationFrame(() => {
1662
- requestAnimationFrame(() => {
1663
- el.classes.remove(startClass);
1664
- el.classes.add(endClass);
1665
-
1666
- waitForVisualEnd(el).then(() => {
1667
- el.classes.remove(endClass);
1668
- });
1669
- });
1670
- });
1671
- });
1672
- return this;
1673
- };
1111
+ return;
1112
+ }
1113
+ element.style[styleName] = newValue;
1114
+ });
1115
+ }
1116
+ continue;
1117
+ }
1674
1118
 
1119
+ if(isCustomProperty) {
1120
+ element.style.setProperty(styleName, value);
1121
+ continue;
1122
+ }
1675
1123
 
1676
- NDElement.prototype.transition = function (transitionName) {
1677
- this.transitionIn(transitionName);
1678
- this.transitionOut(transitionName);
1679
- return this;
1124
+ element.style[styleName] = value;
1125
+ }
1680
1126
  };
1681
1127
 
1682
- NDElement.prototype.animate = function(animationName) {
1683
- const el = this.$element;
1684
- el.classes.add(animationName);
1128
+ /**
1129
+ *
1130
+ * @param {HTMLElement} element
1131
+ * @param {string} attributeName
1132
+ * @param {boolean|number|Observable} value
1133
+ */
1134
+ const bindBooleanAttribute = (element, attributeName, value) => {
1135
+ const isObservable = value.__$isObservable;
1136
+ const defaultValue = isObservable? value.val() : value;
1137
+ if(Validator.isBoolean(defaultValue)) {
1138
+ element[attributeName] = defaultValue;
1139
+ }
1140
+ else {
1141
+ element[attributeName] = defaultValue === element.value;
1142
+ }
1143
+ if(isObservable) {
1144
+ if(attributeName === 'checked') {
1145
+ if(typeof defaultValue === 'boolean') {
1146
+ element.addEventListener('input', () => value.set(element[attributeName]));
1147
+ }
1148
+ else {
1149
+ element.addEventListener('input', () => value.set(element.value));
1150
+ }
1151
+ value.subscribe((newValue) => element[attributeName] = newValue);
1152
+ return;
1153
+ }
1154
+ value.subscribe((newValue) => element[attributeName] = (newValue === element.value));
1155
+ }
1156
+ };
1685
1157
 
1686
- waitForVisualEnd(el).then(() => {
1687
- el.classes.remove(animationName);
1688
- });
1689
1158
 
1690
- return this;
1691
- };
1159
+ /**
1160
+ *
1161
+ * @param {HTMLElement} element
1162
+ * @param {string} attributeName
1163
+ * @param {Observable} value
1164
+ */
1165
+ const bindAttributeWithObservable = (element, attributeName, value) => {
1166
+ const applyValue = attributeName === 'value' ? (newValue) => element.value = newValue : (newValue) => element.setAttribute(attributeName, newValue);
1167
+ value.subscribe(applyValue);
1692
1168
 
1693
- ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
1694
- if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
1695
- bindBooleanAttribute(element, attributeName, this);
1169
+ if(attributeName === 'value') {
1170
+ element.value = value.val();
1171
+ element.addEventListener('input', () => value.set(element.value));
1696
1172
  return;
1697
1173
  }
1698
-
1699
- bindAttributeWithObservable(element, attributeName, this);
1174
+ element.setAttribute(attributeName, value.val());
1700
1175
  };
1701
1176
 
1702
- ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
1177
+ /**
1178
+ *
1179
+ * @param {HTMLElement} element
1180
+ * @param {Object} attributes
1181
+ */
1182
+ const AttributesWrapper = (element, attributes) => {
1703
1183
 
1704
- TemplateBinding.prototype.handleNdAttribute = function(element, attributeName) {
1705
- this.$hydrate(element, attributeName);
1184
+ {
1185
+ Validator.validateAttributes(attributes);
1186
+ }
1187
+
1188
+ for(const originalAttributeName in attributes) {
1189
+ const attributeName = originalAttributeName.toLowerCase();
1190
+ let value = attributes[originalAttributeName];
1191
+ if(value == null) {
1192
+ continue;
1193
+ }
1194
+ if(value.handleNdAttribute) {
1195
+ value.handleNdAttribute(element, attributeName, value);
1196
+ continue;
1197
+ }
1198
+ if(typeof value === 'object') {
1199
+ if(attributeName === 'class') {
1200
+ bindClassAttribute(element, value);
1201
+ continue;
1202
+ }
1203
+ if(attributeName === 'style') {
1204
+ bindStyleAttribute(element, value);
1205
+ continue;
1206
+ }
1207
+ }
1208
+ if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
1209
+ bindBooleanAttribute(element, attributeName, value);
1210
+ continue;
1211
+ }
1212
+
1213
+ element.setAttribute(attributeName, value);
1214
+ }
1215
+ return element;
1706
1216
  };
1707
1217
 
1708
1218
  let $textNodeCache = null;
@@ -1761,9 +1271,6 @@ var NativeDocument = (function (exports) {
1761
1271
  const node = document.createElement(name);
1762
1272
  return node.cloneNode();
1763
1273
  },
1764
- createFragment: (name) => {
1765
- return Anchor('Fragment');
1766
- },
1767
1274
  bindTextNode: (textNode, value) => {
1768
1275
  if(value?.__$isObservable) {
1769
1276
  value.subscribe(newValue => textNode.nodeValue = newValue);
@@ -2099,37 +1606,218 @@ var NativeDocument = (function (exports) {
2099
1606
  anchorFragment.appendChild(anchorFragment);
2100
1607
  };
2101
1608
 
2102
- anchorFragment.clear = anchorFragment.remove;
2103
- anchorFragment.detach = anchorFragment.remove;
1609
+ anchorFragment.clear = anchorFragment.remove;
1610
+ anchorFragment.detach = anchorFragment.remove;
1611
+
1612
+ anchorFragment.getByIndex = function(index) {
1613
+ let currentNode = anchorStart;
1614
+ for(let i = 0; i <= index; i++) {
1615
+ if(!currentNode.nextSibling) {
1616
+ return null;
1617
+ }
1618
+ currentNode = currentNode.nextSibling;
1619
+ }
1620
+ return currentNode !== anchorStart ? currentNode : null;
1621
+ };
1622
+
1623
+ return anchorFragment;
1624
+ }
1625
+ /**
1626
+ *
1627
+ * @param {HTMLElement|DocumentFragment|Text|String|Array} children
1628
+ * @param {{ parent?: HTMLElement, name?: String}} configs
1629
+ * @returns {DocumentFragment}
1630
+ */
1631
+ function createPortal(children, { parent, name = 'unnamed' } = {}) {
1632
+ const anchor = Anchor('Portal '+name);
1633
+ anchor.appendChild(ElementCreator.getChild(children));
1634
+
1635
+ (parent || document.body).appendChild(anchor);
1636
+ return anchor;
1637
+ }
1638
+
1639
+ DocumentFragment.prototype.setAttribute = () => {};
1640
+
1641
+ const DocumentObserver = {
1642
+ mounted: new WeakMap(),
1643
+ beforeUnmount: new WeakMap(),
1644
+ mountedSupposedSize: 0,
1645
+ unmounted: new WeakMap(),
1646
+ unmountedSupposedSize: 0,
1647
+ observer: null,
1648
+ initObserver: () => {
1649
+ if(DocumentObserver.observer) {
1650
+ return;
1651
+ }
1652
+ DocumentObserver.observer = new MutationObserver(DocumentObserver.checkMutation);
1653
+ DocumentObserver.observer.observe(document.body, {
1654
+ childList: true,
1655
+ subtree: true,
1656
+ });
1657
+ },
1658
+
1659
+ executeMountedCallback(node) {
1660
+ const data = DocumentObserver.mounted.get(node);
1661
+ if(!data) {
1662
+ return;
1663
+ }
1664
+ data.inDom = true;
1665
+ if(!data.mounted) {
1666
+ return;
1667
+ }
1668
+ if(Array.isArray(data.mounted)) {
1669
+ for(const cb of data.mounted) {
1670
+ cb(node);
1671
+ }
1672
+ return;
1673
+ }
1674
+ data.mounted(node);
1675
+ },
1676
+
1677
+ executeUnmountedCallback(node) {
1678
+ const data = DocumentObserver.unmounted.get(node);
1679
+ if(!data) {
1680
+ return;
1681
+ }
1682
+ data.inDom = false;
1683
+ if(!data.unmounted) {
1684
+ return;
1685
+ }
1686
+
1687
+ let shouldRemove = false;
1688
+ if(Array.isArray(data.unmounted)) {
1689
+ for(const cb of data.unmounted) {
1690
+ if(cb(node) === true) {
1691
+ shouldRemove = true;
1692
+ }
1693
+ }
1694
+ } else {
1695
+ shouldRemove = data.unmounted(node) === true;
1696
+ }
1697
+
1698
+ if(shouldRemove) {
1699
+ data.disconnect();
1700
+ node.nd?.remove();
1701
+ }
1702
+ },
1703
+
1704
+ checkMutation: function(mutationsList) {
1705
+ for(const mutation of mutationsList) {
1706
+ if(DocumentObserver.mountedSupposedSize > 0) {
1707
+ for(const node of mutation.addedNodes) {
1708
+ DocumentObserver.executeMountedCallback(node);
1709
+ if(!node.querySelectorAll) {
1710
+ continue;
1711
+ }
1712
+ const children = node.querySelectorAll('[data--nd-mounted]');
1713
+ for(const child of children) {
1714
+ DocumentObserver.executeMountedCallback(child);
1715
+ }
1716
+ }
1717
+ }
1718
+
1719
+ if (DocumentObserver.unmountedSupposedSize > 0) {
1720
+ for (const node of mutation.removedNodes) {
1721
+ DocumentObserver.executeUnmountedCallback(node);
1722
+ if(!node.querySelectorAll) {
1723
+ continue;
1724
+ }
1725
+ const children = node.querySelectorAll('[data--nd-unmounted]');
1726
+ for(const child of children) {
1727
+ DocumentObserver.executeUnmountedCallback(child);
1728
+ }
1729
+ }
1730
+ }
1731
+ }
1732
+ },
1733
+
1734
+ /**
1735
+ * @param {HTMLElement} element
1736
+ * @param {boolean} inDom
1737
+ * @returns {{ disconnect: Function, mounted: Function, unmounted: Function, off: Function }}
1738
+ */
1739
+ watch: function(element, inDom = false) {
1740
+ let mountedRegistered = false;
1741
+ let unmountedRegistered = false;
1742
+
1743
+ DocumentObserver.initObserver();
1744
+
1745
+ let data = {
1746
+ inDom,
1747
+ mounted: null,
1748
+ unmounted: null,
1749
+ disconnect: () => {
1750
+ if (mountedRegistered) {
1751
+ DocumentObserver.mounted.delete(element);
1752
+ DocumentObserver.mountedSupposedSize--;
1753
+ }
1754
+ if (unmountedRegistered) {
1755
+ DocumentObserver.unmounted.delete(element);
1756
+ DocumentObserver.unmountedSupposedSize--;
1757
+ }
1758
+ data = null;
1759
+ }
1760
+ };
1761
+
1762
+ const addListener = (type, callback) => {
1763
+ if (!data[type]) {
1764
+ data[type] = callback;
1765
+ return;
1766
+ }
1767
+ if (!Array.isArray(data[type])) {
1768
+ data[type] = [data[type], callback];
1769
+ return;
1770
+ }
1771
+ data[type].push(callback);
1772
+ };
2104
1773
 
2105
- anchorFragment.getByIndex = function(index) {
2106
- let currentNode = anchorStart;
2107
- for(let i = 0; i <= index; i++) {
2108
- if(!currentNode.nextSibling) {
2109
- return null;
1774
+ const removeListener = (type, callback) => {
1775
+ if(!data?.[type]) {
1776
+ return;
2110
1777
  }
2111
- currentNode = currentNode.nextSibling;
2112
- }
2113
- return currentNode !== anchorStart ? currentNode : null;
2114
- };
1778
+ if(Array.isArray(data[type])) {
1779
+ const index = data[type].indexOf(callback);
1780
+ if(index > -1) {
1781
+ data[type].splice(index, 1);
1782
+ }
1783
+ if(data[type].length === 1) {
1784
+ data[type] = data[type][0];
1785
+ }
1786
+ if(data[type].length === 0) {
1787
+ data[type] = null;
1788
+ }
1789
+ return;
1790
+ }
1791
+ data[type] = null;
1792
+ };
2115
1793
 
2116
- return anchorFragment;
2117
- }
2118
- /**
2119
- *
2120
- * @param {HTMLElement|DocumentFragment|Text|String|Array} children
2121
- * @param {{ parent?: HTMLElement, name?: String}} configs
2122
- * @returns {DocumentFragment}
2123
- */
2124
- function createPortal(children, { parent, name = 'unnamed' } = {}) {
2125
- const anchor = Anchor('Portal '+name);
2126
- anchor.appendChild(ElementCreator.getChild(children));
1794
+ return {
1795
+ disconnect: () => data?.disconnect(),
2127
1796
 
2128
- (parent || document.body).appendChild(anchor);
2129
- return anchor;
2130
- }
1797
+ mounted: (callback) => {
1798
+ addListener('mounted', callback);
1799
+ DocumentObserver.mounted.set(element, data);
1800
+ if (!mountedRegistered) {
1801
+ DocumentObserver.mountedSupposedSize++;
1802
+ mountedRegistered = true;
1803
+ }
1804
+ },
2131
1805
 
2132
- DocumentFragment.prototype.setAttribute = () => {};
1806
+ unmounted: (callback) => {
1807
+ addListener('unmounted', callback);
1808
+ DocumentObserver.unmounted.set(element, data);
1809
+ if (!unmountedRegistered) {
1810
+ DocumentObserver.unmountedSupposedSize++;
1811
+ unmountedRegistered = true;
1812
+ }
1813
+ },
1814
+
1815
+ off: (type, callback) => {
1816
+ removeListener(type, callback);
1817
+ }
1818
+ };
1819
+ }
1820
+ };
2133
1821
 
2134
1822
  function NDElement(element) {
2135
1823
  this.$element = element;
@@ -2141,11 +1829,13 @@ var NativeDocument = (function (exports) {
2141
1829
 
2142
1830
  NDElement.prototype.__$isNDElement = true;
2143
1831
 
1832
+ NDElement.$getChild = (el) => el;
1833
+
2144
1834
  NDElement.prototype.ghostDom = function(element) {
2145
1835
  if(!this.$attachements) {
2146
1836
  this.$attachements = document.createDocumentFragment();
2147
1837
  }
2148
- this.$attachements.appendChild(ElementCreator.getChild(element));
1838
+ this.$attachements.appendChild(NDElement.$getChild(element));
2149
1839
  return this;
2150
1840
  };
2151
1841
 
@@ -2343,197 +2033,45 @@ var NativeDocument = (function (exports) {
2343
2033
  throw new NativeDocumentError('NDElement.extend() requires an object of methods');
2344
2034
  }
2345
2035
 
2346
- if (Array.isArray(methods)) {
2347
- throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
2348
- }
2349
-
2350
- const protectedMethods = new Set([
2351
- 'constructor', 'valueOf', '$element', '$observer',
2352
- 'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
2353
- 'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
2354
- ]);
2355
-
2356
- for (const name in methods) {
2357
- if (!Object.hasOwn(methods, name)) {
2358
- continue;
2359
- }
2360
-
2361
- const method = methods[name];
2362
-
2363
- if (typeof method !== 'function') {
2364
- DebugManager$2.warn('NDElement.extend', `"${name}" is not a function, skipping`);
2365
- continue;
2366
- }
2367
-
2368
- if (protectedMethods.has(name)) {
2369
- DebugManager$2.error('NDElement.extend', `Cannot override protected method "${name}"`);
2370
- throw new NativeDocumentError(`Cannot override protected method "${name}"`);
2371
- }
2372
-
2373
- if (NDElement.prototype[name]) {
2374
- DebugManager$2.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
2375
- }
2376
-
2377
- NDElement.prototype[name] = method;
2378
- }
2379
- {
2380
- PluginsManager$1.emit('NDElementExtended', methods);
2381
- }
2382
-
2383
- return NDElement;
2384
- };
2385
-
2386
- const COMMON_NODE_TYPES = {
2387
- ELEMENT: 1,
2388
- TEXT: 3,
2389
- COMMENT: 8,
2390
- DOCUMENT_FRAGMENT: 11
2391
- };
2392
-
2393
- const VALID_TYPES = [];
2394
- VALID_TYPES[COMMON_NODE_TYPES.ELEMENT] = true;
2395
- VALID_TYPES[COMMON_NODE_TYPES.TEXT] = true;
2396
- VALID_TYPES[COMMON_NODE_TYPES.DOCUMENT_FRAGMENT] = true;
2397
- VALID_TYPES[COMMON_NODE_TYPES.COMMENT] = true;
2398
-
2399
- const Validator = {
2400
- isObservable(value) {
2401
- return value && (value.__$isObservable || value.__$Observable);
2402
- },
2403
- isTemplateBinding(value) {
2404
- return value?.__$isTemplateBinding;
2405
- },
2406
- isObservableWhenResult(value) {
2407
- return value && (value.__$isObservableWhen || (typeof value === 'object' && '$target' in value && '$observer' in value));
2408
- },
2409
- isArrayObservable(value) {
2410
- return value?.__$isObservableArray;
2411
- },
2412
- isProxy(value) {
2413
- return value?.__isProxy__
2414
- },
2415
- isObservableOrProxy(value) {
2416
- return Validator.isObservable(value) || Validator.isProxy(value);
2417
- },
2418
- isAnchor(value) {
2419
- return value?.__Anchor__
2420
- },
2421
- isObservableChecker(value) {
2422
- return value?.__$isObservableChecker || value instanceof ObservableChecker;
2423
- },
2424
- isArray(value) {
2425
- return Array.isArray(value);
2426
- },
2427
- isString(value) {
2428
- return typeof value === 'string';
2429
- },
2430
- isNumber(value) {
2431
- return typeof value === 'number';
2432
- },
2433
- isBoolean(value) {
2434
- return typeof value === 'boolean';
2435
- },
2436
- isFunction(value) {
2437
- return typeof value === 'function';
2438
- },
2439
- isAsyncFunction(value) {
2440
- return typeof value === 'function' && value.constructor.name === 'AsyncFunction';
2441
- },
2442
- isObject(value) {
2443
- return typeof value === 'object' && value !== null;
2444
- },
2445
- isJson(value) {
2446
- return !(typeof value !== 'object' || value === null || Array.isArray(value) || value.constructor.name !== 'Object')
2447
- },
2448
- isElement(value) {
2449
- return value && VALID_TYPES[value.nodeType];
2450
- },
2451
- isDOMNode(value) {
2452
- return VALID_TYPES[value.nodeType];
2453
- },
2454
- isFragment(value) {
2455
- return value?.nodeType === COMMON_NODE_TYPES.DOCUMENT_FRAGMENT;
2456
- },
2457
- isStringOrObservable(value) {
2458
- return this.isString(value) || this.isObservable(value);
2459
- },
2460
- isValidChild(child) {
2461
- return child === null ||
2462
- this.isElement(child) ||
2463
- this.isObservable(child) ||
2464
- this.isNDElement(child) ||
2465
- ['string', 'number', 'boolean'].includes(typeof child);
2466
- },
2467
- isNDElement(child) {
2468
- return child?.__$isNDElement || child instanceof NDElement;
2469
- },
2470
- isValidChildren(children) {
2471
- if (!Array.isArray(children)) {
2472
- children = [children];
2473
- }
2474
-
2475
- const invalid = children.filter(child => !this.isValidChild(child));
2476
- return invalid.length === 0;
2477
- },
2478
- validateChildren(children) {
2479
- if (!Array.isArray(children)) {
2480
- children = [children];
2481
- }
2482
-
2483
- const invalid = children.filter(child => !this.isValidChild(child));
2484
- if (invalid.length > 0) {
2485
- throw new NativeDocumentError(`Invalid children detected: ${invalid.map(i => typeof i).join(', ')}`);
2486
- }
2487
-
2488
- return children;
2489
- },
2490
- /**
2491
- * Check if the data contains observables.
2492
- * @param {Array|Object} data
2493
- * @returns {boolean}
2494
- */
2495
- containsObservables(data) {
2496
- if(!data) {
2497
- return false;
2498
- }
2499
- return Validator.isObject(data)
2500
- && Object.values(data).some(value => Validator.isObservable(value));
2501
- },
2502
- /**
2503
- * Check if the data contains an observable reference.
2504
- * @param {string} data
2505
- * @returns {boolean}
2506
- */
2507
- containsObservableReference(data) {
2508
- if(!data || typeof data !== 'string') {
2509
- return false;
2510
- }
2511
- return /\{\{#ObItem::\([0-9]+\)\}\}/.test(data);
2512
- },
2513
- validateAttributes(attributes) {},
2036
+ if (Array.isArray(methods)) {
2037
+ throw new NativeDocumentError('NDElement.extend() requires an object, not an array');
2038
+ }
2514
2039
 
2515
- validateEventCallback(callback) {
2516
- if (typeof callback !== 'function') {
2517
- throw new NativeDocumentError('Event callback must be a function');
2040
+ const protectedMethods = new Set([
2041
+ 'constructor', 'valueOf', '$element', '$observer',
2042
+ 'ref', 'remove', 'cleanup', 'with', 'extend', 'attach',
2043
+ 'lifecycle', 'mounted', 'unmounted', 'unmountChildren'
2044
+ ]);
2045
+
2046
+ for (const name in methods) {
2047
+ if (!Object.hasOwn(methods, name)) {
2048
+ continue;
2518
2049
  }
2519
- }
2520
- };
2521
- {
2522
- Validator.validateAttributes = function(attributes) {
2523
- if (!attributes || typeof attributes !== 'object') {
2524
- return attributes;
2050
+
2051
+ const method = methods[name];
2052
+
2053
+ if (typeof method !== 'function') {
2054
+ DebugManager$2.warn('NDElement.extend', `"${name}" is not a function, skipping`);
2055
+ continue;
2525
2056
  }
2526
2057
 
2527
- const reserved = [];
2528
- const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
2058
+ if (protectedMethods.has(name)) {
2059
+ DebugManager$2.error('NDElement.extend', `Cannot override protected method "${name}"`);
2060
+ throw new NativeDocumentError(`Cannot override protected method "${name}"`);
2061
+ }
2529
2062
 
2530
- if (foundReserved.length > 0) {
2531
- DebugManager$2.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
2063
+ if (NDElement.prototype[name]) {
2064
+ DebugManager$2.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
2532
2065
  }
2533
2066
 
2534
- return attributes;
2535
- };
2536
- }
2067
+ NDElement.prototype[name] = method;
2068
+ }
2069
+ {
2070
+ PluginsManager$1.emit('NDElementExtended', methods);
2071
+ }
2072
+
2073
+ return NDElement;
2074
+ };
2537
2075
 
2538
2076
  const EVENTS = [
2539
2077
  "Click",
@@ -3006,6 +2544,12 @@ var NativeDocument = (function (exports) {
3006
2544
  };
3007
2545
  }
3008
2546
 
2547
+ function TemplateBinding(hydrate) {
2548
+ this.$hydrate = hydrate;
2549
+ }
2550
+
2551
+ TemplateBinding.prototype.__$isTemplateBinding = true;
2552
+
3009
2553
  function NodeCloner($element) {
3010
2554
  this.$element = $element;
3011
2555
  this.$classes = null;
@@ -3361,6 +2905,209 @@ var NativeDocument = (function (exports) {
3361
2905
  return handler;
3362
2906
  };
3363
2907
 
2908
+ /**
2909
+ *
2910
+ * @param {ObservableItem} $observable
2911
+ * @param {Function} $checker
2912
+ * @class ObservableChecker
2913
+ */
2914
+ function ObservableChecker($observable, $checker) {
2915
+ this.observable = $observable;
2916
+
2917
+ ObservableItem.call(this);
2918
+ {
2919
+ PluginsManager$1.emit('CreateObservableChecker', this);
2920
+ }
2921
+
2922
+ this.$mutation = $checker;
2923
+
2924
+ $observable.subscribe((newValue) => {
2925
+ this.$updateWithMutation(newValue);
2926
+ });
2927
+
2928
+ this.$updateWithMutation($observable.val());
2929
+ }
2930
+
2931
+ ObservableChecker.prototype = Object.create(ObservableItem.prototype);
2932
+ ObservableChecker.prototype.constructor = ObservableChecker;
2933
+ ObservableChecker.prototype.__$Observable = true;
2934
+ ObservableChecker.prototype.__$isObservableChecker = true;
2935
+
2936
+
2937
+ const ObservablePipe = ObservableChecker;
2938
+ ObservablePipe.prototype.constructor = ObservablePipe;
2939
+
2940
+ ObservableChecker.prototype.$updateWithMutation = function(newValue) {
2941
+ newValue = this.$mutation(newValue);
2942
+ return this.set(newValue);
2943
+ };
2944
+
2945
+ NDElement.$getChild = ElementCreator.getChild;
2946
+
2947
+ String.prototype.toNdElement = function () {
2948
+ return ElementCreator.createStaticTextNode(null, this);
2949
+ };
2950
+
2951
+ Number.prototype.toNdElement = function () {
2952
+ return ElementCreator.createStaticTextNode(null, this.toString());
2953
+ };
2954
+
2955
+ Element.prototype.toNdElement = function () {
2956
+ return this;
2957
+ };
2958
+ Text.prototype.toNdElement = function () {
2959
+ return this;
2960
+ };
2961
+ Comment.prototype.toNdElement = function () {
2962
+ return this;
2963
+ };
2964
+ Document.prototype.toNdElement = function () {
2965
+ return this;
2966
+ };
2967
+ DocumentFragment.prototype.toNdElement = function () {
2968
+ return this;
2969
+ };
2970
+
2971
+ ObservableItem.prototype.toNdElement = function () {
2972
+ return ElementCreator.createObservableNode(null, this);
2973
+ };
2974
+
2975
+ ObservableChecker.prototype.toNdElement = ObservableItem.prototype.toNdElement;
2976
+
2977
+ NDElement.prototype.toNdElement = function () {
2978
+ const element = this.$element ?? this.$build?.() ?? this.build?.() ?? null;
2979
+ if(this.$attachements) {
2980
+ if(!this.$attachements.contains(this.$element)) {
2981
+ this.$attachements.append(this.$element);
2982
+ }
2983
+ return this.$attachements;
2984
+ }
2985
+ return element;
2986
+ };
2987
+
2988
+ Array.prototype.toNdElement = function () {
2989
+ const fragment = document.createDocumentFragment();
2990
+ for(let i = 0, length = this.length; i < length; i++) {
2991
+ const child = ElementCreator.getChild(this[i]);
2992
+ if(child === null) continue;
2993
+ fragment.appendChild(child);
2994
+ }
2995
+ return fragment;
2996
+ };
2997
+
2998
+ Function.prototype.toNdElement = function () {
2999
+ const child = this;
3000
+ {
3001
+ PluginsManager$1.emit('BeforeProcessComponent', child);
3002
+ }
3003
+ return ElementCreator.getChild(child());
3004
+ };
3005
+
3006
+ TemplateBinding.prototype.toNdElement = function () {
3007
+ return ElementCreator.createHydratableNode(null, this);
3008
+ };
3009
+
3010
+ /**
3011
+ * @param {HTMLElement} el
3012
+ * @param {number} timeout
3013
+ */
3014
+ const waitForVisualEnd = (el, timeout = 1000) => {
3015
+ return new Promise((resolve) => {
3016
+ let isResolved = false;
3017
+
3018
+ const cleanupAndResolve = (e) => {
3019
+ if (e && e.target !== el) return;
3020
+ if (isResolved) return;
3021
+
3022
+ isResolved = true;
3023
+ el.removeEventListener('transitionend', cleanupAndResolve);
3024
+ el.removeEventListener('animationend', cleanupAndResolve);
3025
+ clearTimeout(timer);
3026
+ resolve();
3027
+ };
3028
+
3029
+ el.addEventListener('transitionend', cleanupAndResolve);
3030
+ el.addEventListener('animationend', cleanupAndResolve);
3031
+
3032
+ const timer = setTimeout(cleanupAndResolve, timeout);
3033
+
3034
+ const style = window.getComputedStyle(el);
3035
+ const hasTransition = style.transitionDuration !== '0s';
3036
+ const hasAnimation = style.animationDuration !== '0s';
3037
+
3038
+ if (!hasTransition && !hasAnimation) {
3039
+ cleanupAndResolve();
3040
+ }
3041
+ });
3042
+ };
3043
+
3044
+ NDElement.prototype.transitionOut = function(transitionName) {
3045
+ const exitClass = transitionName + '-exit';
3046
+ const el = this.$element;
3047
+ this.beforeUnmount('transition-exit', async function() {
3048
+ el.classes.add(exitClass);
3049
+ await waitForVisualEnd(el);
3050
+ el.classes.remove(exitClass);
3051
+ });
3052
+ return this;
3053
+ };
3054
+
3055
+ NDElement.prototype.transitionIn = function(transitionName) {
3056
+ const startClass = transitionName + '-enter-from';
3057
+ const endClass = transitionName + '-enter-to';
3058
+
3059
+ const el = this.$element;
3060
+
3061
+ el.classes.add(startClass);
3062
+
3063
+ this.mounted(() => {
3064
+ requestAnimationFrame(() => {
3065
+ requestAnimationFrame(() => {
3066
+ el.classes.remove(startClass);
3067
+ el.classes.add(endClass);
3068
+
3069
+ waitForVisualEnd(el).then(() => {
3070
+ el.classes.remove(endClass);
3071
+ });
3072
+ });
3073
+ });
3074
+ });
3075
+ return this;
3076
+ };
3077
+
3078
+
3079
+ NDElement.prototype.transition = function (transitionName) {
3080
+ this.transitionIn(transitionName);
3081
+ this.transitionOut(transitionName);
3082
+ return this;
3083
+ };
3084
+
3085
+ NDElement.prototype.animate = function(animationName) {
3086
+ const el = this.$element;
3087
+ el.classes.add(animationName);
3088
+
3089
+ waitForVisualEnd(el).then(() => {
3090
+ el.classes.remove(animationName);
3091
+ });
3092
+
3093
+ return this;
3094
+ };
3095
+
3096
+ ObservableItem.prototype.handleNdAttribute = function(element, attributeName) {
3097
+ if(BOOLEAN_ATTRIBUTES.has(attributeName)) {
3098
+ bindBooleanAttribute(element, attributeName, this);
3099
+ return;
3100
+ }
3101
+
3102
+ bindAttributeWithObservable(element, attributeName, this);
3103
+ };
3104
+
3105
+ ObservableChecker.prototype.handleNdAttribute = ObservableItem.prototype.handleNdAttribute;
3106
+
3107
+ TemplateBinding.prototype.handleNdAttribute = function(element, attributeName) {
3108
+ this.$hydrate(element, attributeName);
3109
+ };
3110
+
3364
3111
  const cssPropertyAccumulator = function(initialValue = {}) {
3365
3112
  let data = Validator.isString(initialValue) ? initialValue.split(';').filter(Boolean) : initialValue;
3366
3113