native-document 1.0.117 → 1.0.119
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/native-document.components.min.js +1154 -1011
- package/dist/native-document.dev.js +1322 -1179
- package/dist/native-document.dev.js.map +1 -1
- package/dist/native-document.min.js +1 -1
- package/docs/lifecycle-events.md +1 -1
- package/elements.js +2 -2
- package/package.json +1 -1
- package/src/core/data/ObservableItem.js +1 -2
- package/src/core/elements/anchor/anchor-with-sentinel.js +41 -0
- package/src/core/elements/{anchor.js → anchor/anchor.js} +29 -13
- package/src/core/elements/anchor/one-child-anchor-overwriting.js +44 -0
- package/src/core/elements/control/for-each-array.js +33 -31
- package/src/core/elements/control/for-each.js +1 -1
- package/src/core/elements/control/show-if.js +1 -1
- package/src/core/elements/control/switch.js +1 -1
- package/src/core/wrappers/AttributesWrapper.js +0 -1
- package/src/core/wrappers/ElementCreator.js +1 -1
- package/src/core/wrappers/HtmlElementWrapper.js +5 -4
- package/src/core/wrappers/SingletonView.js +1 -1
- package/src/core/wrappers/prototypes/nd-element-extensions.js +4 -0
- package/src/core/wrappers/template-cloner/NodeCloner.js +57 -14
- package/src/core/wrappers/template-cloner/TemplateCloner.js +2 -4
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
var NativeDocument = (function (exports) {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
-
let DebugManager$
|
|
4
|
+
let DebugManager$2 = {};
|
|
5
5
|
|
|
6
6
|
{
|
|
7
|
-
DebugManager$
|
|
7
|
+
DebugManager$2 = {
|
|
8
8
|
enabled: false,
|
|
9
9
|
|
|
10
10
|
enable() {
|
|
@@ -35,7 +35,7 @@ var NativeDocument = (function (exports) {
|
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
}
|
|
38
|
-
var DebugManager = DebugManager$
|
|
38
|
+
var DebugManager$1 = DebugManager$2;
|
|
39
39
|
|
|
40
40
|
class NativeDocumentError extends Error {
|
|
41
41
|
constructor(message, context = {}) {
|
|
@@ -382,7 +382,7 @@ var NativeDocument = (function (exports) {
|
|
|
382
382
|
try{
|
|
383
383
|
callback.call(plugin, ...data);
|
|
384
384
|
} catch (error) {
|
|
385
|
-
DebugManager.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
|
|
385
|
+
DebugManager$1.error('Plugin Manager', `Error in plugin ${plugin.$name} for event ${eventName}`, error);
|
|
386
386
|
}
|
|
387
387
|
}
|
|
388
388
|
}
|
|
@@ -561,7 +561,7 @@ var NativeDocument = (function (exports) {
|
|
|
561
561
|
}
|
|
562
562
|
{
|
|
563
563
|
if (this[name] && !this.$localExtensions.has(name)) {
|
|
564
|
-
DebugManager.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
|
|
564
|
+
DebugManager$1.warn('NDElement.extend', `Method "${name}" already exists and will be overwritten`);
|
|
565
565
|
}
|
|
566
566
|
this.$localExtensions.set(name, method);
|
|
567
567
|
}
|
|
@@ -612,17 +612,17 @@ var NativeDocument = (function (exports) {
|
|
|
612
612
|
const method = methods[name];
|
|
613
613
|
|
|
614
614
|
if (typeof method !== 'function') {
|
|
615
|
-
DebugManager.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
615
|
+
DebugManager$1.warn('NDElement.extend', `"${name}" is not a function, skipping`);
|
|
616
616
|
continue;
|
|
617
617
|
}
|
|
618
618
|
|
|
619
619
|
if (protectedMethods.has(name)) {
|
|
620
|
-
DebugManager.error('NDElement.extend', `Cannot override protected method "${name}"`);
|
|
620
|
+
DebugManager$1.error('NDElement.extend', `Cannot override protected method "${name}"`);
|
|
621
621
|
throw new NativeDocumentError(`Cannot override protected method "${name}"`);
|
|
622
622
|
}
|
|
623
623
|
|
|
624
624
|
if (NDElement.prototype[name]) {
|
|
625
|
-
DebugManager.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
|
|
625
|
+
DebugManager$1.warn('NDElement.extend', `Overwriting existing prototype method "${name}"`);
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
NDElement.prototype[name] = method;
|
|
@@ -779,7 +779,7 @@ var NativeDocument = (function (exports) {
|
|
|
779
779
|
const foundReserved = Object.keys(attributes).filter(key => reserved.includes(key));
|
|
780
780
|
|
|
781
781
|
if (foundReserved.length > 0) {
|
|
782
|
-
DebugManager.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
|
|
782
|
+
DebugManager$1.warn('Validator', `Reserved attributes found: ${foundReserved.join(', ')}`);
|
|
783
783
|
}
|
|
784
784
|
|
|
785
785
|
return attributes;
|
|
@@ -866,7 +866,7 @@ var NativeDocument = (function (exports) {
|
|
|
866
866
|
}
|
|
867
867
|
}
|
|
868
868
|
if (cleanedCount > 0) {
|
|
869
|
-
DebugManager.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
|
|
869
|
+
DebugManager$1.log('Memory Auto Clean', `🧹 Cleaned ${cleanedCount} orphaned observables`);
|
|
870
870
|
}
|
|
871
871
|
}
|
|
872
872
|
};
|
|
@@ -1001,6 +1001,97 @@ var NativeDocument = (function (exports) {
|
|
|
1001
1001
|
return cloned;
|
|
1002
1002
|
};
|
|
1003
1003
|
|
|
1004
|
+
const $parseDateParts = (value, locale) => {
|
|
1005
|
+
const d = new Date(value);
|
|
1006
|
+
return {
|
|
1007
|
+
d,
|
|
1008
|
+
parts: new Intl.DateTimeFormat(locale, {
|
|
1009
|
+
year: 'numeric',
|
|
1010
|
+
month: 'long',
|
|
1011
|
+
day: '2-digit',
|
|
1012
|
+
hour: '2-digit',
|
|
1013
|
+
minute: '2-digit',
|
|
1014
|
+
second: '2-digit',
|
|
1015
|
+
}).formatToParts(d).reduce((acc, { type, value }) => {
|
|
1016
|
+
acc[type] = value;
|
|
1017
|
+
return acc;
|
|
1018
|
+
}, {})
|
|
1019
|
+
};
|
|
1020
|
+
};
|
|
1021
|
+
|
|
1022
|
+
const $applyDatePattern = (pattern, d, parts) => {
|
|
1023
|
+
const pad = n => String(n).padStart(2, '0');
|
|
1024
|
+
return pattern
|
|
1025
|
+
.replace('YYYY', parts.year)
|
|
1026
|
+
.replace('YY', parts.year.slice(-2))
|
|
1027
|
+
.replace('MMMM', parts.month)
|
|
1028
|
+
.replace('MMM', parts.month.slice(0, 3))
|
|
1029
|
+
.replace('MM', pad(d.getMonth() + 1))
|
|
1030
|
+
.replace('DD', pad(d.getDate()))
|
|
1031
|
+
.replace('D', d.getDate())
|
|
1032
|
+
.replace('HH', parts.hour)
|
|
1033
|
+
.replace('mm', parts.minute)
|
|
1034
|
+
.replace('ss', parts.second);
|
|
1035
|
+
};
|
|
1036
|
+
|
|
1037
|
+
const Formatters = {
|
|
1038
|
+
currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1039
|
+
new Intl.NumberFormat(locale, {
|
|
1040
|
+
style: 'currency',
|
|
1041
|
+
currency,
|
|
1042
|
+
notation,
|
|
1043
|
+
minimumFractionDigits,
|
|
1044
|
+
maximumFractionDigits
|
|
1045
|
+
}).format(value),
|
|
1046
|
+
|
|
1047
|
+
number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1048
|
+
new Intl.NumberFormat(locale, {
|
|
1049
|
+
notation,
|
|
1050
|
+
minimumFractionDigits,
|
|
1051
|
+
maximumFractionDigits
|
|
1052
|
+
}).format(value),
|
|
1053
|
+
|
|
1054
|
+
percent: (value, locale, { decimals = 1 } = {}) =>
|
|
1055
|
+
new Intl.NumberFormat(locale, {
|
|
1056
|
+
style: 'percent',
|
|
1057
|
+
maximumFractionDigits: decimals
|
|
1058
|
+
}).format(value),
|
|
1059
|
+
|
|
1060
|
+
date: (value, locale, { format, dateStyle = 'long' } = {}) => {
|
|
1061
|
+
if (format) {
|
|
1062
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1063
|
+
return $applyDatePattern(format, d, parts);
|
|
1064
|
+
}
|
|
1065
|
+
return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
|
|
1066
|
+
},
|
|
1067
|
+
|
|
1068
|
+
time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1069
|
+
if (format) {
|
|
1070
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1071
|
+
return $applyDatePattern(format, d, parts);
|
|
1072
|
+
}
|
|
1073
|
+
return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
|
|
1074
|
+
},
|
|
1075
|
+
|
|
1076
|
+
datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1077
|
+
if (format) {
|
|
1078
|
+
const { d, parts } = $parseDateParts(value, locale);
|
|
1079
|
+
return $applyDatePattern(format, d, parts);
|
|
1080
|
+
}
|
|
1081
|
+
return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
|
|
1082
|
+
},
|
|
1083
|
+
|
|
1084
|
+
relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
|
|
1085
|
+
const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
|
|
1086
|
+
return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
|
|
1087
|
+
},
|
|
1088
|
+
|
|
1089
|
+
plural: (value, locale, { singular, plural } = {}) => {
|
|
1090
|
+
const rule = new Intl.PluralRules(locale).select(value);
|
|
1091
|
+
return `${value} ${rule === 'one' ? singular : plural}`;
|
|
1092
|
+
},
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1004
1095
|
const LocalStorage = {
|
|
1005
1096
|
getJson(key) {
|
|
1006
1097
|
let value = localStorage.getItem(key);
|
|
@@ -1057,703 +1148,212 @@ var NativeDocument = (function (exports) {
|
|
|
1057
1148
|
}
|
|
1058
1149
|
};
|
|
1059
1150
|
|
|
1060
|
-
|
|
1151
|
+
/**
|
|
1152
|
+
*
|
|
1153
|
+
* @param {*} value
|
|
1154
|
+
* @param {{ propagation: boolean, reset: boolean} | null} configs
|
|
1155
|
+
* @class ObservableItem
|
|
1156
|
+
*/
|
|
1157
|
+
function ObservableItem(value, configs = null) {
|
|
1158
|
+
value = Validator.isObservable(value) ? value.val() : value;
|
|
1061
1159
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1160
|
+
this.$previousValue = null;
|
|
1161
|
+
this.$currentValue = value;
|
|
1162
|
+
{
|
|
1163
|
+
this.$isCleanedUp = false;
|
|
1164
|
+
}
|
|
1064
1165
|
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
const $getStoreOrThrow = (method, name) => {
|
|
1069
|
-
const item = $stores.get(name);
|
|
1070
|
-
if (!item) {
|
|
1071
|
-
DebugManager.error('Store', `Store.${method}('${name}') : store not found. Did you call Store.create('${name}') first?`);
|
|
1072
|
-
throw new NativeDocumentError(
|
|
1073
|
-
`Store.${method}('${name}') : store not found.`
|
|
1074
|
-
);
|
|
1075
|
-
}
|
|
1076
|
-
return item;
|
|
1077
|
-
};
|
|
1166
|
+
this.$firstListener = null;
|
|
1167
|
+
this.$listeners = null;
|
|
1168
|
+
this.$watchers = null;
|
|
1078
1169
|
|
|
1079
|
-
|
|
1080
|
-
* Internal helper — blocks write operations on a read-only observer.
|
|
1081
|
-
*/
|
|
1082
|
-
const $applyReadOnly = (observer, name, context) => {
|
|
1083
|
-
const readOnlyError = (method) => () => {
|
|
1084
|
-
DebugManager.error('Store', `Store.${context}('${name}') is read-only. '${method}()' is not allowed.`);
|
|
1085
|
-
throw new NativeDocumentError(
|
|
1086
|
-
`Store.${context}('${name}') is read-only.`
|
|
1087
|
-
);
|
|
1088
|
-
};
|
|
1089
|
-
observer.set = readOnlyError('set');
|
|
1090
|
-
observer.toggle = readOnlyError('toggle');
|
|
1091
|
-
observer.reset = readOnlyError('reset');
|
|
1092
|
-
};
|
|
1170
|
+
this.$memoryId = null;
|
|
1093
1171
|
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
if(typeof value === 'object') {
|
|
1099
|
-
return Observable.object(value, options);
|
|
1172
|
+
if(configs) {
|
|
1173
|
+
this.configs = configs;
|
|
1174
|
+
if(configs.reset) {
|
|
1175
|
+
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
1100
1176
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
* Create a new state and return the observer.
|
|
1107
|
-
* Throws if a store with the same name already exists.
|
|
1108
|
-
*
|
|
1109
|
-
* @param {string} name
|
|
1110
|
-
* @param {*} value
|
|
1111
|
-
* @returns {ObservableItem}
|
|
1112
|
-
*/
|
|
1113
|
-
create(name, value) {
|
|
1114
|
-
if ($stores.has(name)) {
|
|
1115
|
-
DebugManager.warn('Store', `Store.create('${name}') : a store with this name already exists. Use Store.get('${name}') to retrieve it.`);
|
|
1116
|
-
throw new NativeDocumentError(
|
|
1117
|
-
`Store.create('${name}') : a store with this name already exists.`
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
const observer = $createObservable(value);
|
|
1121
|
-
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
|
|
1122
|
-
return observer;
|
|
1123
|
-
},
|
|
1177
|
+
}
|
|
1178
|
+
{
|
|
1179
|
+
PluginsManager.emit('CreateObservable', this);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1124
1182
|
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
createResettable(name, value) {
|
|
1135
|
-
if ($stores.has(name)) {
|
|
1136
|
-
DebugManager.warn('Store', `Store.createResettable('${name}') : a store with this name already exists.`);
|
|
1137
|
-
throw new NativeDocumentError(
|
|
1138
|
-
`Store.createResettable('${name}') : a store with this name already exists.`
|
|
1139
|
-
);
|
|
1140
|
-
}
|
|
1141
|
-
const observer = $createObservable(value, { reset: true });
|
|
1142
|
-
$stores.set(name, { observer, subscribers: new Set(), resettable: true, composed: false });
|
|
1143
|
-
return observer;
|
|
1144
|
-
},
|
|
1183
|
+
Object.defineProperty(ObservableItem.prototype, '$value', {
|
|
1184
|
+
get() {
|
|
1185
|
+
return this.$currentValue;
|
|
1186
|
+
},
|
|
1187
|
+
set(value) {
|
|
1188
|
+
this.set(value);
|
|
1189
|
+
},
|
|
1190
|
+
configurable: true,
|
|
1191
|
+
});
|
|
1145
1192
|
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
* The value is automatically recalculated when any dependency changes.
|
|
1149
|
-
* This store is read-only — Store.use() and Store.set() will throw.
|
|
1150
|
-
* Throws if a store with the same name already exists.
|
|
1151
|
-
*
|
|
1152
|
-
* @param {string} name
|
|
1153
|
-
* @param {() => *} computation - Function that returns the computed value
|
|
1154
|
-
* @param {string[]} dependencies - Names of the stores to watch
|
|
1155
|
-
* @returns {ObservableItem}
|
|
1156
|
-
*
|
|
1157
|
-
* @example
|
|
1158
|
-
* Store.create('products', [{ id: 1, price: 10 }]);
|
|
1159
|
-
* Store.create('cart', [{ productId: 1, quantity: 2 }]);
|
|
1160
|
-
*
|
|
1161
|
-
* Store.createComposed('total', () => {
|
|
1162
|
-
* const products = Store.get('products').val();
|
|
1163
|
-
* const cart = Store.get('cart').val();
|
|
1164
|
-
* return cart.reduce((sum, item) => {
|
|
1165
|
-
* const product = products.find(p => p.id === item.productId);
|
|
1166
|
-
* return sum + (product.price * item.quantity);
|
|
1167
|
-
* }, 0);
|
|
1168
|
-
* }, ['products', 'cart']);
|
|
1169
|
-
*/
|
|
1170
|
-
createComposed(name, computation, dependencies) {
|
|
1171
|
-
if ($stores.has(name)) {
|
|
1172
|
-
DebugManager.warn('Store', `Store.createComposed('${name}') : a store with this name already exists.`);
|
|
1173
|
-
throw new NativeDocumentError(
|
|
1174
|
-
`Store.createComposed('${name}') : a store with this name already exists.`
|
|
1175
|
-
);
|
|
1176
|
-
}
|
|
1177
|
-
if (typeof computation !== 'function') {
|
|
1178
|
-
throw new NativeDocumentError(
|
|
1179
|
-
`Store.createComposed('${name}') : computation must be a function.`
|
|
1180
|
-
);
|
|
1181
|
-
}
|
|
1182
|
-
if (!Array.isArray(dependencies) || dependencies.length === 0) {
|
|
1183
|
-
throw new NativeDocumentError(
|
|
1184
|
-
`Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`
|
|
1185
|
-
);
|
|
1186
|
-
}
|
|
1193
|
+
ObservableItem.prototype.__$isObservable = true;
|
|
1194
|
+
const noneTrigger = function() {};
|
|
1187
1195
|
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1196
|
+
/**
|
|
1197
|
+
* Intercepts and transforms values before they are set on the observable.
|
|
1198
|
+
* The interceptor can modify the value or return undefined to use the original value.
|
|
1199
|
+
*
|
|
1200
|
+
* @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
|
|
1201
|
+
* @returns {ObservableItem} The observable instance for chaining
|
|
1202
|
+
* @example
|
|
1203
|
+
* const count = Observable(0);
|
|
1204
|
+
* count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
|
|
1205
|
+
*/
|
|
1206
|
+
ObservableItem.prototype.intercept = function(callback) {
|
|
1207
|
+
this.$interceptor = callback;
|
|
1208
|
+
this.set = this.$setWithInterceptor;
|
|
1209
|
+
return this;
|
|
1210
|
+
};
|
|
1202
1211
|
|
|
1203
|
-
|
|
1204
|
-
|
|
1212
|
+
ObservableItem.prototype.triggerFirstListener = function(operations) {
|
|
1213
|
+
this.$firstListener(this.$currentValue, this.$previousValue, operations);
|
|
1214
|
+
};
|
|
1205
1215
|
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1216
|
+
ObservableItem.prototype.triggerListeners = function(operations) {
|
|
1217
|
+
const $listeners = this.$listeners;
|
|
1218
|
+
const $previousValue = this.$previousValue;
|
|
1219
|
+
const $currentValue = this.$currentValue;
|
|
1209
1220
|
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
* @returns {boolean}
|
|
1215
|
-
*/
|
|
1216
|
-
has(name) {
|
|
1217
|
-
return $stores.has(name);
|
|
1218
|
-
},
|
|
1221
|
+
for(let i = 0, length = $listeners.length; i < length; i++) {
|
|
1222
|
+
$listeners[i]($currentValue, $previousValue, operations);
|
|
1223
|
+
}
|
|
1224
|
+
};
|
|
1219
1225
|
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
* @param {string} name
|
|
1225
|
-
*/
|
|
1226
|
-
reset(name) {
|
|
1227
|
-
const item = $getStoreOrThrow('reset', name);
|
|
1228
|
-
if (item.composed) {
|
|
1229
|
-
DebugManager.error('Store', `Store.reset('${name}') : composed stores cannot be reset. Their value is derived from dependencies.`);
|
|
1230
|
-
throw new NativeDocumentError(
|
|
1231
|
-
`Store.reset('${name}') : composed stores cannot be reset.`
|
|
1232
|
-
);
|
|
1233
|
-
}
|
|
1234
|
-
if (!item.resettable) {
|
|
1235
|
-
DebugManager.error('Store', `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`);
|
|
1236
|
-
throw new NativeDocumentError(
|
|
1237
|
-
`Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`
|
|
1238
|
-
);
|
|
1239
|
-
}
|
|
1240
|
-
item.observer.reset();
|
|
1241
|
-
},
|
|
1226
|
+
ObservableItem.prototype.triggerWatchers = function(operations) {
|
|
1227
|
+
const $watchers = this.$watchers;
|
|
1228
|
+
const $previousValue = this.$previousValue;
|
|
1229
|
+
const $currentValue = this.$currentValue;
|
|
1242
1230
|
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
use(name) {
|
|
1253
|
-
const item = $getStoreOrThrow('use', name);
|
|
1231
|
+
const $currentValueCallbacks = $watchers.get($currentValue);
|
|
1232
|
+
const $previousValueCallbacks = $watchers.get($previousValue);
|
|
1233
|
+
if($currentValueCallbacks) {
|
|
1234
|
+
$currentValueCallbacks(true, $previousValue, operations);
|
|
1235
|
+
}
|
|
1236
|
+
if($previousValueCallbacks) {
|
|
1237
|
+
$previousValueCallbacks(false, $currentValue, operations);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1254
1240
|
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
);
|
|
1260
|
-
}
|
|
1241
|
+
ObservableItem.prototype.triggerAll = function(operations) {
|
|
1242
|
+
this.triggerWatchers(operations);
|
|
1243
|
+
this.triggerListeners(operations);
|
|
1244
|
+
};
|
|
1261
1245
|
|
|
1262
|
-
|
|
1263
|
-
|
|
1246
|
+
ObservableItem.prototype.triggerWatchersAndFirstListener = function(operations) {
|
|
1247
|
+
this.triggerWatchers(operations);
|
|
1248
|
+
this.triggerFirstListener(operations);
|
|
1249
|
+
};
|
|
1264
1250
|
|
|
1265
|
-
|
|
1266
|
-
|
|
1251
|
+
ObservableItem.prototype.assocTrigger = function() {
|
|
1252
|
+
this.$firstListener = null;
|
|
1253
|
+
if(this.$watchers?.size && this.$listeners?.length) {
|
|
1254
|
+
this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
if(this.$listeners?.length) {
|
|
1258
|
+
if(this.$listeners.length === 1) {
|
|
1259
|
+
this.$firstListener = this.$listeners[0];
|
|
1260
|
+
this.trigger = this.$firstListener.length === 0 ? this.$firstListener : this.triggerFirstListener;
|
|
1261
|
+
}
|
|
1262
|
+
else {
|
|
1263
|
+
this.trigger = this.triggerListeners;
|
|
1264
|
+
}
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if(this.$watchers?.size) {
|
|
1268
|
+
this.trigger = this.triggerWatchers;
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
this.trigger = noneTrigger;
|
|
1272
|
+
};
|
|
1273
|
+
ObservableItem.prototype.trigger = noneTrigger;
|
|
1267
1274
|
|
|
1268
|
-
|
|
1269
|
-
|
|
1275
|
+
ObservableItem.prototype.$updateWithNewValue = function(newValue) {
|
|
1276
|
+
newValue = newValue?.__$isObservable ? newValue.val() : newValue;
|
|
1277
|
+
if(this.$currentValue === newValue) {
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
this.$previousValue = this.$currentValue;
|
|
1281
|
+
this.$currentValue = newValue;
|
|
1282
|
+
{
|
|
1283
|
+
PluginsManager.emit('ObservableBeforeChange', this);
|
|
1284
|
+
}
|
|
1285
|
+
this.trigger();
|
|
1286
|
+
this.$previousValue = null;
|
|
1287
|
+
{
|
|
1288
|
+
PluginsManager.emit('ObservableAfterChange', this);
|
|
1289
|
+
}
|
|
1290
|
+
};
|
|
1270
1291
|
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
observerFollower.dispose = observerFollower.destroy;
|
|
1292
|
+
/**
|
|
1293
|
+
* @param {*} data
|
|
1294
|
+
*/
|
|
1295
|
+
ObservableItem.prototype.$setWithInterceptor = function(data) {
|
|
1296
|
+
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
1297
|
+
const result = this.$interceptor(newValue, this.$currentValue);
|
|
1278
1298
|
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1299
|
+
if (result !== undefined) {
|
|
1300
|
+
newValue = result;
|
|
1301
|
+
}
|
|
1282
1302
|
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
* The follower reflects store changes but cannot write back to the store.
|
|
1286
|
-
* Any attempt to call .set(), .toggle() or .reset() will throw.
|
|
1287
|
-
* Call follower.destroy() or follower.dispose() to unsubscribe.
|
|
1288
|
-
*
|
|
1289
|
-
* @param {string} name
|
|
1290
|
-
* @returns {ObservableItem}
|
|
1291
|
-
*/
|
|
1292
|
-
follow(name) {
|
|
1293
|
-
const { observer: originalObserver, subscribers } = $getStoreOrThrow('follow', name);
|
|
1294
|
-
const observerFollower = $createObservable(originalObserver.val());
|
|
1303
|
+
this.$updateWithNewValue(newValue);
|
|
1304
|
+
};
|
|
1295
1305
|
|
|
1296
|
-
|
|
1297
|
-
|
|
1306
|
+
/**
|
|
1307
|
+
* @param {*} data
|
|
1308
|
+
*/
|
|
1309
|
+
ObservableItem.prototype.$basicSet = function(data) {
|
|
1310
|
+
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
1311
|
+
this.$updateWithNewValue(newValue);
|
|
1312
|
+
};
|
|
1298
1313
|
|
|
1299
|
-
|
|
1314
|
+
ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
|
|
1300
1315
|
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
observerFollower.cleanup();
|
|
1305
|
-
};
|
|
1306
|
-
observerFollower.dispose = observerFollower.destroy;
|
|
1316
|
+
ObservableItem.prototype.val = function() {
|
|
1317
|
+
return this.$currentValue;
|
|
1318
|
+
};
|
|
1307
1319
|
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1320
|
+
ObservableItem.prototype.disconnectAll = function() {
|
|
1321
|
+
this.$previousValue = null;
|
|
1322
|
+
this.$currentValue = null;
|
|
1323
|
+
this.$listeners = null;
|
|
1324
|
+
this.$watchers = null;
|
|
1325
|
+
this.trigger = noneTrigger;
|
|
1326
|
+
};
|
|
1311
1327
|
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
return item.observer;
|
|
1327
|
-
},
|
|
1328
|
+
/**
|
|
1329
|
+
* Registers a cleanup callback that will be executed when the observable is cleaned up.
|
|
1330
|
+
* Useful for disposing resources, removing event listeners, or other cleanup tasks.
|
|
1331
|
+
*
|
|
1332
|
+
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
1333
|
+
* @example
|
|
1334
|
+
* const obs = Observable(0);
|
|
1335
|
+
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
1336
|
+
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
1337
|
+
*/
|
|
1338
|
+
ObservableItem.prototype.onCleanup = function(callback) {
|
|
1339
|
+
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
1340
|
+
this.$cleanupListeners.push(callback);
|
|
1341
|
+
};
|
|
1328
1342
|
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const item = $stores.get(name);
|
|
1344
|
-
if (!item) {
|
|
1345
|
-
DebugManager.warn('Store', `Store.delete('${name}') : store not found, nothing to delete.`);
|
|
1346
|
-
return;
|
|
1347
|
-
}
|
|
1348
|
-
item.subscribers.forEach(follower => follower.destroy());
|
|
1349
|
-
item.subscribers.clear();
|
|
1350
|
-
item.observer.cleanup();
|
|
1351
|
-
$stores.delete(name);
|
|
1352
|
-
},
|
|
1353
|
-
/**
|
|
1354
|
-
* Creates an isolated store group with its own state namespace.
|
|
1355
|
-
* Each group is a fully independent StoreFactory instance —
|
|
1356
|
-
* no key conflicts, no shared state with the parent store.
|
|
1357
|
-
*
|
|
1358
|
-
* @param {string | ((group: ReturnType<typeof StoreFactory>) => void)} name - Group name for debugging, or setup callback if no name is provided
|
|
1359
|
-
* @param {((group: ReturnType<typeof StoreFactory>) => void)} [callback] - Setup function receiving the isolated store instance
|
|
1360
|
-
* @returns {ReturnType<typeof StoreFactory>}
|
|
1361
|
-
*
|
|
1362
|
-
* @example
|
|
1363
|
-
* // With name (recommended)
|
|
1364
|
-
* const EventStore = Store.group('events', (group) => {
|
|
1365
|
-
* group.create('catalog', []);
|
|
1366
|
-
* group.create('filters', { category: null, date: null });
|
|
1367
|
-
* group.createResettable('selected', null);
|
|
1368
|
-
* group.createComposed('filtered', () => {
|
|
1369
|
-
* const catalog = EventStore.get('catalog').val();
|
|
1370
|
-
* const filters = EventStore.get('filters').val();
|
|
1371
|
-
* return catalog.filter(event => {
|
|
1372
|
-
* if (filters.category && event.category !== filters.category) return false;
|
|
1373
|
-
* return true;
|
|
1374
|
-
* });
|
|
1375
|
-
* }, ['catalog', 'filters']);
|
|
1376
|
-
* });
|
|
1377
|
-
*
|
|
1378
|
-
* // Without name
|
|
1379
|
-
* const CartStore = Store.group((group) => {
|
|
1380
|
-
* group.create('items', []);
|
|
1381
|
-
* });
|
|
1382
|
-
*
|
|
1383
|
-
* // Usage
|
|
1384
|
-
* EventStore.use('catalog'); // two-way follower
|
|
1385
|
-
* EventStore.follow('filtered'); // read-only follower
|
|
1386
|
-
* EventStore.get('filters'); // raw observable
|
|
1387
|
-
*
|
|
1388
|
-
* // Cross-group composed
|
|
1389
|
-
* const OrderStore = Store.group('orders', (group) => {
|
|
1390
|
-
* group.createComposed('summary', () => {
|
|
1391
|
-
* const items = CartStore.get('items').val();
|
|
1392
|
-
* const events = EventStore.get('catalog').val();
|
|
1393
|
-
* return { items, events };
|
|
1394
|
-
* }, [CartStore.get('items'), EventStore.get('catalog')]);
|
|
1395
|
-
* });
|
|
1396
|
-
*/
|
|
1397
|
-
group(name, callback) {
|
|
1398
|
-
if (typeof name === 'function') {
|
|
1399
|
-
callback = name;
|
|
1400
|
-
name = 'anonymous';
|
|
1401
|
-
}
|
|
1402
|
-
const store = StoreFactory();
|
|
1403
|
-
callback && callback(store);
|
|
1404
|
-
return store;
|
|
1405
|
-
},
|
|
1406
|
-
createPersistent(name, value, localstorage_key) {
|
|
1407
|
-
localstorage_key = localstorage_key || name;
|
|
1408
|
-
const observer = this.create(name, $getFromStorage(localstorage_key, value));
|
|
1409
|
-
const saver = $saveToStorage(value);
|
|
1410
|
-
|
|
1411
|
-
observer.subscribe((val) => saver(localstorage_key, val));
|
|
1412
|
-
return observer;
|
|
1413
|
-
},
|
|
1414
|
-
createPersistentResettable(name, value, localstorage_key) {
|
|
1415
|
-
localstorage_key = localstorage_key || name;
|
|
1416
|
-
const observer = this.createResettable(name, $getFromStorage(localstorage_key, value));
|
|
1417
|
-
const saver = $saveToStorage(value);
|
|
1418
|
-
observer.subscribe((val) => saver(localstorage_key, val));
|
|
1419
|
-
|
|
1420
|
-
const originalReset = observer.reset.bind(observer);
|
|
1421
|
-
observer.reset = () => {
|
|
1422
|
-
LocalStorage.remove(localstorage_key);
|
|
1423
|
-
originalReset();
|
|
1424
|
-
};
|
|
1425
|
-
|
|
1426
|
-
return observer;
|
|
1427
|
-
}
|
|
1428
|
-
};
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
return new Proxy($api, {
|
|
1432
|
-
get(target, prop) {
|
|
1433
|
-
if (typeof prop === 'symbol' || prop.startsWith('$') || prop in target) {
|
|
1434
|
-
return target[prop];
|
|
1435
|
-
}
|
|
1436
|
-
if (target.has(prop)) {
|
|
1437
|
-
if ($followersCache.has(prop)) {
|
|
1438
|
-
return $followersCache.get(prop);
|
|
1439
|
-
}
|
|
1440
|
-
const follower = target.follow(prop);
|
|
1441
|
-
$followersCache.set(prop, follower);
|
|
1442
|
-
return follower;
|
|
1443
|
-
}
|
|
1444
|
-
return undefined;
|
|
1445
|
-
},
|
|
1446
|
-
set(target, prop, value) {
|
|
1447
|
-
DebugManager.error('Store', `Forbidden: You cannot overwrite the store key '${String(prop)}'. Use .use('${String(prop)}').set(value) instead.`);
|
|
1448
|
-
throw new NativeDocumentError(`Store structure is immutable. Use .set() on the observable.`);
|
|
1449
|
-
},
|
|
1450
|
-
deleteProperty(target, prop) {
|
|
1451
|
-
throw new NativeDocumentError(`Store keys cannot be deleted.`);
|
|
1452
|
-
}
|
|
1453
|
-
});
|
|
1454
|
-
};
|
|
1455
|
-
|
|
1456
|
-
const Store = StoreFactory();
|
|
1457
|
-
|
|
1458
|
-
Store.create('locale', navigator.language.split('-')[0] || 'en');
|
|
1459
|
-
|
|
1460
|
-
const $parseDateParts = (value, locale) => {
|
|
1461
|
-
const d = new Date(value);
|
|
1462
|
-
return {
|
|
1463
|
-
d,
|
|
1464
|
-
parts: new Intl.DateTimeFormat(locale, {
|
|
1465
|
-
year: 'numeric',
|
|
1466
|
-
month: 'long',
|
|
1467
|
-
day: '2-digit',
|
|
1468
|
-
hour: '2-digit',
|
|
1469
|
-
minute: '2-digit',
|
|
1470
|
-
second: '2-digit',
|
|
1471
|
-
}).formatToParts(d).reduce((acc, { type, value }) => {
|
|
1472
|
-
acc[type] = value;
|
|
1473
|
-
return acc;
|
|
1474
|
-
}, {})
|
|
1475
|
-
};
|
|
1476
|
-
};
|
|
1477
|
-
|
|
1478
|
-
const $applyDatePattern = (pattern, d, parts) => {
|
|
1479
|
-
const pad = n => String(n).padStart(2, '0');
|
|
1480
|
-
return pattern
|
|
1481
|
-
.replace('YYYY', parts.year)
|
|
1482
|
-
.replace('YY', parts.year.slice(-2))
|
|
1483
|
-
.replace('MMMM', parts.month)
|
|
1484
|
-
.replace('MMM', parts.month.slice(0, 3))
|
|
1485
|
-
.replace('MM', pad(d.getMonth() + 1))
|
|
1486
|
-
.replace('DD', pad(d.getDate()))
|
|
1487
|
-
.replace('D', d.getDate())
|
|
1488
|
-
.replace('HH', parts.hour)
|
|
1489
|
-
.replace('mm', parts.minute)
|
|
1490
|
-
.replace('ss', parts.second);
|
|
1491
|
-
};
|
|
1492
|
-
|
|
1493
|
-
const Formatters = {
|
|
1494
|
-
currency: (value, locale, { currency = 'XOF', notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1495
|
-
new Intl.NumberFormat(locale, {
|
|
1496
|
-
style: 'currency',
|
|
1497
|
-
currency,
|
|
1498
|
-
notation,
|
|
1499
|
-
minimumFractionDigits,
|
|
1500
|
-
maximumFractionDigits
|
|
1501
|
-
}).format(value),
|
|
1502
|
-
|
|
1503
|
-
number: (value, locale, { notation, minimumFractionDigits, maximumFractionDigits } = {}) =>
|
|
1504
|
-
new Intl.NumberFormat(locale, {
|
|
1505
|
-
notation,
|
|
1506
|
-
minimumFractionDigits,
|
|
1507
|
-
maximumFractionDigits
|
|
1508
|
-
}).format(value),
|
|
1509
|
-
|
|
1510
|
-
percent: (value, locale, { decimals = 1 } = {}) =>
|
|
1511
|
-
new Intl.NumberFormat(locale, {
|
|
1512
|
-
style: 'percent',
|
|
1513
|
-
maximumFractionDigits: decimals
|
|
1514
|
-
}).format(value),
|
|
1515
|
-
|
|
1516
|
-
date: (value, locale, { format, dateStyle = 'long' } = {}) => {
|
|
1517
|
-
if (format) {
|
|
1518
|
-
const { d, parts } = $parseDateParts(value, locale);
|
|
1519
|
-
return $applyDatePattern(format, d, parts);
|
|
1520
|
-
}
|
|
1521
|
-
return new Intl.DateTimeFormat(locale, { dateStyle }).format(new Date(value));
|
|
1522
|
-
},
|
|
1523
|
-
|
|
1524
|
-
time: (value, locale, { format, hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1525
|
-
if (format) {
|
|
1526
|
-
const { d, parts } = $parseDateParts(value, locale);
|
|
1527
|
-
return $applyDatePattern(format, d, parts);
|
|
1528
|
-
}
|
|
1529
|
-
return new Intl.DateTimeFormat(locale, { hour, minute, second }).format(new Date(value));
|
|
1530
|
-
},
|
|
1531
|
-
|
|
1532
|
-
datetime: (value, locale, { format, dateStyle = 'long', hour = '2-digit', minute = '2-digit', second } = {}) => {
|
|
1533
|
-
if (format) {
|
|
1534
|
-
const { d, parts } = $parseDateParts(value, locale);
|
|
1535
|
-
return $applyDatePattern(format, d, parts);
|
|
1536
|
-
}
|
|
1537
|
-
return new Intl.DateTimeFormat(locale, { dateStyle, hour, minute, second }).format(new Date(value));
|
|
1538
|
-
},
|
|
1539
|
-
|
|
1540
|
-
relative: (value, locale, { unit = 'day', numeric = 'auto' } = {}) => {
|
|
1541
|
-
const diff = Math.round((value - Date.now()) / (1000 * 60 * 60 * 24));
|
|
1542
|
-
return new Intl.RelativeTimeFormat(locale, { numeric }).format(diff, unit);
|
|
1543
|
-
},
|
|
1544
|
-
|
|
1545
|
-
plural: (value, locale, { singular, plural } = {}) => {
|
|
1546
|
-
const rule = new Intl.PluralRules(locale).select(value);
|
|
1547
|
-
return `${value} ${rule === 'one' ? singular : plural}`;
|
|
1548
|
-
},
|
|
1549
|
-
};
|
|
1550
|
-
|
|
1551
|
-
/**
|
|
1552
|
-
*
|
|
1553
|
-
* @param {*} value
|
|
1554
|
-
* @param {{ propagation: boolean, reset: boolean} | null} configs
|
|
1555
|
-
* @class ObservableItem
|
|
1556
|
-
*/
|
|
1557
|
-
function ObservableItem(value, configs = null) {
|
|
1558
|
-
value = Validator.isObservable(value) ? value.val() : value;
|
|
1559
|
-
|
|
1560
|
-
this.$previousValue = null;
|
|
1561
|
-
this.$currentValue = value;
|
|
1562
|
-
{
|
|
1563
|
-
this.$isCleanedUp = false;
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
|
-
this.$firstListener = null;
|
|
1567
|
-
this.$listeners = null;
|
|
1568
|
-
this.$watchers = null;
|
|
1569
|
-
|
|
1570
|
-
this.$memoryId = null;
|
|
1571
|
-
|
|
1572
|
-
if(configs) {
|
|
1573
|
-
this.configs = configs;
|
|
1574
|
-
if(configs.reset) {
|
|
1575
|
-
this.$initialValue = Validator.isObject(value) ? deepClone(value) : value;
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
{
|
|
1579
|
-
PluginsManager.emit('CreateObservable', this);
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1582
|
-
|
|
1583
|
-
Object.defineProperty(ObservableItem.prototype, '$value', {
|
|
1584
|
-
get() {
|
|
1585
|
-
return this.$currentValue;
|
|
1586
|
-
},
|
|
1587
|
-
set(value) {
|
|
1588
|
-
this.set(value);
|
|
1589
|
-
},
|
|
1590
|
-
configurable: true,
|
|
1591
|
-
});
|
|
1592
|
-
|
|
1593
|
-
ObservableItem.prototype.__$isObservable = true;
|
|
1594
|
-
const noneTrigger = function() {};
|
|
1595
|
-
|
|
1596
|
-
/**
|
|
1597
|
-
* Intercepts and transforms values before they are set on the observable.
|
|
1598
|
-
* The interceptor can modify the value or return undefined to use the original value.
|
|
1599
|
-
*
|
|
1600
|
-
* @param {(value) => any} callback - Interceptor function that receives (newValue, currentValue) and returns the transformed value or undefined
|
|
1601
|
-
* @returns {ObservableItem} The observable instance for chaining
|
|
1602
|
-
* @example
|
|
1603
|
-
* const count = Observable(0);
|
|
1604
|
-
* count.intercept((newVal, oldVal) => Math.max(0, newVal)); // Prevent negative values
|
|
1605
|
-
*/
|
|
1606
|
-
ObservableItem.prototype.intercept = function(callback) {
|
|
1607
|
-
this.$interceptor = callback;
|
|
1608
|
-
this.set = this.$setWithInterceptor;
|
|
1609
|
-
return this;
|
|
1610
|
-
};
|
|
1611
|
-
|
|
1612
|
-
ObservableItem.prototype.triggerFirstListener = function(operations) {
|
|
1613
|
-
this.$firstListener(this.$currentValue, this.$previousValue, operations);
|
|
1614
|
-
};
|
|
1615
|
-
|
|
1616
|
-
ObservableItem.prototype.triggerListeners = function(operations) {
|
|
1617
|
-
const $listeners = this.$listeners;
|
|
1618
|
-
const $previousValue = this.$previousValue;
|
|
1619
|
-
const $currentValue = this.$currentValue;
|
|
1620
|
-
|
|
1621
|
-
for(let i = 0, length = $listeners.length; i < length; i++) {
|
|
1622
|
-
$listeners[i]($currentValue, $previousValue, operations);
|
|
1623
|
-
}
|
|
1624
|
-
};
|
|
1625
|
-
|
|
1626
|
-
ObservableItem.prototype.triggerWatchers = function(operations) {
|
|
1627
|
-
const $watchers = this.$watchers;
|
|
1628
|
-
const $previousValue = this.$previousValue;
|
|
1629
|
-
const $currentValue = this.$currentValue;
|
|
1630
|
-
|
|
1631
|
-
const $currentValueCallbacks = $watchers.get($currentValue);
|
|
1632
|
-
const $previousValueCallbacks = $watchers.get($previousValue);
|
|
1633
|
-
if($currentValueCallbacks) {
|
|
1634
|
-
$currentValueCallbacks(true, $previousValue, operations);
|
|
1635
|
-
}
|
|
1636
|
-
if($previousValueCallbacks) {
|
|
1637
|
-
$previousValueCallbacks(false, $currentValue, operations);
|
|
1638
|
-
}
|
|
1639
|
-
};
|
|
1640
|
-
|
|
1641
|
-
ObservableItem.prototype.triggerAll = function(operations) {
|
|
1642
|
-
this.triggerWatchers(operations);
|
|
1643
|
-
this.triggerListeners(operations);
|
|
1644
|
-
};
|
|
1645
|
-
|
|
1646
|
-
ObservableItem.prototype.triggerWatchersAndFirstListener = function(operations) {
|
|
1647
|
-
this.triggerWatchers(operations);
|
|
1648
|
-
this.triggerFirstListener(operations);
|
|
1649
|
-
};
|
|
1650
|
-
|
|
1651
|
-
ObservableItem.prototype.assocTrigger = function() {
|
|
1652
|
-
this.$firstListener = null;
|
|
1653
|
-
if(this.$watchers?.size && this.$listeners?.length) {
|
|
1654
|
-
this.trigger = (this.$listeners.length === 1) ? this.triggerWatchersAndFirstListener : this.triggerAll;
|
|
1655
|
-
return;
|
|
1656
|
-
}
|
|
1657
|
-
if(this.$listeners?.length) {
|
|
1658
|
-
if(this.$listeners.length === 1) {
|
|
1659
|
-
this.$firstListener = this.$listeners[0];
|
|
1660
|
-
this.trigger = this.triggerFirstListener;
|
|
1661
|
-
}
|
|
1662
|
-
else {
|
|
1663
|
-
this.trigger = this.triggerListeners;
|
|
1664
|
-
}
|
|
1665
|
-
return;
|
|
1666
|
-
}
|
|
1667
|
-
if(this.$watchers?.size) {
|
|
1668
|
-
this.trigger = this.triggerWatchers;
|
|
1669
|
-
return;
|
|
1670
|
-
}
|
|
1671
|
-
this.trigger = noneTrigger;
|
|
1672
|
-
};
|
|
1673
|
-
ObservableItem.prototype.trigger = noneTrigger;
|
|
1674
|
-
|
|
1675
|
-
ObservableItem.prototype.$updateWithNewValue = function(newValue) {
|
|
1676
|
-
newValue = newValue?.__$isObservable ? newValue.val() : newValue;
|
|
1677
|
-
if(this.$currentValue === newValue) {
|
|
1678
|
-
return;
|
|
1679
|
-
}
|
|
1680
|
-
this.$previousValue = this.$currentValue;
|
|
1681
|
-
this.$currentValue = newValue;
|
|
1682
|
-
{
|
|
1683
|
-
PluginsManager.emit('ObservableBeforeChange', this);
|
|
1684
|
-
}
|
|
1685
|
-
this.trigger();
|
|
1686
|
-
this.$previousValue = null;
|
|
1687
|
-
{
|
|
1688
|
-
PluginsManager.emit('ObservableAfterChange', this);
|
|
1689
|
-
}
|
|
1690
|
-
};
|
|
1691
|
-
|
|
1692
|
-
/**
|
|
1693
|
-
* @param {*} data
|
|
1694
|
-
*/
|
|
1695
|
-
ObservableItem.prototype.$setWithInterceptor = function(data) {
|
|
1696
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
1697
|
-
const result = this.$interceptor(newValue, this.$currentValue);
|
|
1698
|
-
|
|
1699
|
-
if (result !== undefined) {
|
|
1700
|
-
newValue = result;
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
this.$updateWithNewValue(newValue);
|
|
1704
|
-
};
|
|
1705
|
-
|
|
1706
|
-
/**
|
|
1707
|
-
* @param {*} data
|
|
1708
|
-
*/
|
|
1709
|
-
ObservableItem.prototype.$basicSet = function(data) {
|
|
1710
|
-
let newValue = (typeof data === 'function') ? data(this.$currentValue) : data;
|
|
1711
|
-
this.$updateWithNewValue(newValue);
|
|
1712
|
-
};
|
|
1713
|
-
|
|
1714
|
-
ObservableItem.prototype.set = ObservableItem.prototype.$basicSet;
|
|
1715
|
-
|
|
1716
|
-
ObservableItem.prototype.val = function() {
|
|
1717
|
-
return this.$currentValue;
|
|
1718
|
-
};
|
|
1719
|
-
|
|
1720
|
-
ObservableItem.prototype.disconnectAll = function() {
|
|
1721
|
-
this.$previousValue = null;
|
|
1722
|
-
this.$currentValue = null;
|
|
1723
|
-
this.$listeners = null;
|
|
1724
|
-
this.$watchers = null;
|
|
1725
|
-
this.trigger = noneTrigger;
|
|
1726
|
-
};
|
|
1727
|
-
|
|
1728
|
-
/**
|
|
1729
|
-
* Registers a cleanup callback that will be executed when the observable is cleaned up.
|
|
1730
|
-
* Useful for disposing resources, removing event listeners, or other cleanup tasks.
|
|
1731
|
-
*
|
|
1732
|
-
* @param {Function} callback - Cleanup function to execute on observable disposal
|
|
1733
|
-
* @example
|
|
1734
|
-
* const obs = Observable(0);
|
|
1735
|
-
* obs.onCleanup(() => console.log('Cleaned up!'));
|
|
1736
|
-
* obs.cleanup(); // Logs: "Cleaned up!"
|
|
1737
|
-
*/
|
|
1738
|
-
ObservableItem.prototype.onCleanup = function(callback) {
|
|
1739
|
-
this.$cleanupListeners = this.$cleanupListeners ?? [];
|
|
1740
|
-
this.$cleanupListeners.push(callback);
|
|
1741
|
-
};
|
|
1742
|
-
|
|
1743
|
-
ObservableItem.prototype.cleanup = function() {
|
|
1744
|
-
if (this.$cleanupListeners) {
|
|
1745
|
-
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
1746
|
-
this.$cleanupListeners[i]();
|
|
1747
|
-
}
|
|
1748
|
-
this.$cleanupListeners = null;
|
|
1749
|
-
}
|
|
1750
|
-
MemoryManager.unregister(this.$memoryId);
|
|
1751
|
-
this.disconnectAll();
|
|
1752
|
-
{
|
|
1753
|
-
this.$isCleanedUp = true;
|
|
1754
|
-
}
|
|
1755
|
-
delete this.$value;
|
|
1756
|
-
};
|
|
1343
|
+
ObservableItem.prototype.cleanup = function() {
|
|
1344
|
+
if (this.$cleanupListeners) {
|
|
1345
|
+
for (let i = 0; i < this.$cleanupListeners.length; i++) {
|
|
1346
|
+
this.$cleanupListeners[i]();
|
|
1347
|
+
}
|
|
1348
|
+
this.$cleanupListeners = null;
|
|
1349
|
+
}
|
|
1350
|
+
MemoryManager.unregister(this.$memoryId);
|
|
1351
|
+
this.disconnectAll();
|
|
1352
|
+
{
|
|
1353
|
+
this.$isCleanedUp = true;
|
|
1354
|
+
}
|
|
1355
|
+
delete this.$value;
|
|
1356
|
+
};
|
|
1757
1357
|
|
|
1758
1358
|
/**
|
|
1759
1359
|
*
|
|
@@ -1763,7 +1363,7 @@ var NativeDocument = (function (exports) {
|
|
|
1763
1363
|
ObservableItem.prototype.subscribe = function(callback) {
|
|
1764
1364
|
{
|
|
1765
1365
|
if (this.$isCleanedUp) {
|
|
1766
|
-
DebugManager.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
|
|
1366
|
+
DebugManager$1.warn('Observable subscription', '⚠️ Attempted to subscribe to a cleaned up observable.');
|
|
1767
1367
|
return;
|
|
1768
1368
|
}
|
|
1769
1369
|
if (typeof callback !== 'function') {
|
|
@@ -2203,7 +1803,6 @@ var NativeDocument = (function (exports) {
|
|
|
2203
1803
|
}
|
|
2204
1804
|
element.classes.toggle(className, value);
|
|
2205
1805
|
}
|
|
2206
|
-
data = null;
|
|
2207
1806
|
};
|
|
2208
1807
|
|
|
2209
1808
|
/**
|
|
@@ -2323,6 +1922,10 @@ var NativeDocument = (function (exports) {
|
|
|
2323
1922
|
return ElementCreator.createStaticTextNode(null, this);
|
|
2324
1923
|
};
|
|
2325
1924
|
|
|
1925
|
+
Number.prototype.toNdElement = function () {
|
|
1926
|
+
return ElementCreator.createStaticTextNode(null, this.toString());
|
|
1927
|
+
};
|
|
1928
|
+
|
|
2326
1929
|
Element.prototype.toNdElement = function () {
|
|
2327
1930
|
return this;
|
|
2328
1931
|
};
|
|
@@ -2576,30 +2179,125 @@ var NativeDocument = (function (exports) {
|
|
|
2576
2179
|
} while (child.toNdElement);
|
|
2577
2180
|
}
|
|
2578
2181
|
|
|
2579
|
-
return ElementCreator.createStaticTextNode(null, child);
|
|
2580
|
-
},
|
|
2182
|
+
return ElementCreator.createStaticTextNode(null, child);
|
|
2183
|
+
},
|
|
2184
|
+
/**
|
|
2185
|
+
*
|
|
2186
|
+
* @param {HTMLElement} element
|
|
2187
|
+
* @param {Object} attributes
|
|
2188
|
+
*/
|
|
2189
|
+
processAttributes: (element, attributes) => {
|
|
2190
|
+
if (attributes) {
|
|
2191
|
+
AttributesWrapper(element, attributes);
|
|
2192
|
+
}
|
|
2193
|
+
},
|
|
2194
|
+
/**
|
|
2195
|
+
*
|
|
2196
|
+
* @param {HTMLElement} element
|
|
2197
|
+
* @param {Object} attributes
|
|
2198
|
+
*/
|
|
2199
|
+
processAttributesDirect: AttributesWrapper,
|
|
2200
|
+
processClassAttribute: bindClassAttribute,
|
|
2201
|
+
processStyleAttribute: bindStyleAttribute,
|
|
2202
|
+
};
|
|
2203
|
+
|
|
2204
|
+
function AnchorWithSentinel(name) {
|
|
2205
|
+
const instance = Reflect.construct(DocumentFragment, [], AnchorWithSentinel);
|
|
2206
|
+
const sentinel = document.createComment((name || '') + ' Anchor Sentinel');
|
|
2207
|
+
const events = {};
|
|
2208
|
+
|
|
2209
|
+
instance.appendChild(sentinel);
|
|
2210
|
+
|
|
2211
|
+
const observer = new MutationObserver(() => {
|
|
2212
|
+
if (sentinel.parentNode !== instance && !(sentinel.parentNode instanceof DocumentFragment)) {
|
|
2213
|
+
events.connected && events.connected(sentinel.parentNode);
|
|
2214
|
+
}
|
|
2215
|
+
});
|
|
2216
|
+
|
|
2217
|
+
observer.observe(document, { childList: true, subtree: true });
|
|
2218
|
+
|
|
2219
|
+
|
|
2220
|
+
instance.$sentinel = sentinel;
|
|
2221
|
+
instance.$observer = observer;
|
|
2222
|
+
instance.$events = events;
|
|
2223
|
+
|
|
2224
|
+
return instance;
|
|
2225
|
+
}
|
|
2226
|
+
|
|
2227
|
+
AnchorWithSentinel.prototype = Object.create(DocumentFragment.prototype);
|
|
2228
|
+
AnchorWithSentinel.prototype.constructor = AnchorWithSentinel;
|
|
2229
|
+
|
|
2230
|
+
AnchorWithSentinel.prototype.onConnected = function(callback) {
|
|
2231
|
+
this.$events.connected = callback;
|
|
2232
|
+
return this;
|
|
2233
|
+
};
|
|
2234
|
+
|
|
2235
|
+
AnchorWithSentinel.prototype.onConnectedOnce = function(callback) {
|
|
2236
|
+
this.$events.connected = (parent) => {
|
|
2237
|
+
callback(parent);
|
|
2238
|
+
this.$observer.disconnect();
|
|
2239
|
+
this.$events.connectedOnce = null;
|
|
2240
|
+
};
|
|
2241
|
+
};
|
|
2242
|
+
|
|
2243
|
+
function oneChildAnchorOverwriting(anchor, parent) {
|
|
2244
|
+
|
|
2245
|
+
anchor.remove = () => {
|
|
2246
|
+
anchor.append.apply(anchor, parent.childNodes);
|
|
2247
|
+
};
|
|
2248
|
+
|
|
2249
|
+
anchor.appendChild = (child) => {
|
|
2250
|
+
child = Validator.isElement(child) ? child : ElementCreator.getChild(child);
|
|
2251
|
+
parent.appendChild(child);
|
|
2252
|
+
};
|
|
2253
|
+
|
|
2254
|
+
anchor.appendElement = anchor.appendChild;
|
|
2255
|
+
|
|
2256
|
+
anchor.removeChildren = () => {
|
|
2257
|
+
parent.replaceChildren();
|
|
2258
|
+
};
|
|
2259
|
+
|
|
2260
|
+
anchor.replaceContent = function(content) {
|
|
2261
|
+
const child = Validator.isElement(content) ? content : ElementCreator.getChild(content);
|
|
2262
|
+
parent.replaceChildren(child);
|
|
2263
|
+
};
|
|
2264
|
+
anchor.setContent = anchor.replaceContent;
|
|
2265
|
+
|
|
2266
|
+
anchor.insertBefore = (child, anchor) => {
|
|
2267
|
+
child = Validator.isElement(child) ? child : ElementCreator.getChild(child);
|
|
2268
|
+
parent.insertBefore(child, anchor);
|
|
2269
|
+
};
|
|
2270
|
+
anchor.appendChildBefore = anchor.insertBefore;
|
|
2271
|
+
|
|
2272
|
+
anchor.clear = anchor.remove;
|
|
2273
|
+
anchor.detach = anchor.remove;
|
|
2274
|
+
|
|
2275
|
+
anchor.replaceChildren = function() {
|
|
2276
|
+
parent.replaceChildren(...arguments);
|
|
2277
|
+
};
|
|
2278
|
+
|
|
2279
|
+
anchor.getByIndex = (index) => {
|
|
2280
|
+
return parent.childNodes[index];
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
function Anchor(name, isUniqueChild = false) {
|
|
2285
|
+
const anchorFragment = new AnchorWithSentinel(name);
|
|
2286
|
+
|
|
2581
2287
|
/**
|
|
2582
|
-
*
|
|
2583
|
-
*
|
|
2584
|
-
*
|
|
2288
|
+
* State :
|
|
2289
|
+
* 1. Not injected in the DOM
|
|
2290
|
+
* 2. Injected in the DOM and should be the only child of parent
|
|
2291
|
+
* 3. Injected in the DOM and the parent may have other children
|
|
2585
2292
|
*/
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2293
|
+
|
|
2294
|
+
anchorFragment.onConnectedOnce((parent) => {
|
|
2295
|
+
if(isUniqueChild) {
|
|
2296
|
+
console.log('Lets overwrite some functions with parent ', parent);
|
|
2297
|
+
oneChildAnchorOverwriting(anchorFragment, parent);
|
|
2589
2298
|
}
|
|
2590
|
-
}
|
|
2591
|
-
/**
|
|
2592
|
-
*
|
|
2593
|
-
* @param {HTMLElement} element
|
|
2594
|
-
* @param {Object} attributes
|
|
2595
|
-
*/
|
|
2596
|
-
processAttributesDirect: AttributesWrapper,
|
|
2597
|
-
processClassAttribute: bindClassAttribute,
|
|
2598
|
-
processStyleAttribute: bindStyleAttribute,
|
|
2599
|
-
};
|
|
2299
|
+
});
|
|
2600
2300
|
|
|
2601
|
-
function Anchor(name, isUniqueChild = false) {
|
|
2602
|
-
const anchorFragment = document.createDocumentFragment();
|
|
2603
2301
|
anchorFragment.__Anchor__ = true;
|
|
2604
2302
|
|
|
2605
2303
|
const anchorStart = document.createComment('Anchor Start : '+name);
|
|
@@ -2613,8 +2311,7 @@ var NativeDocument = (function (exports) {
|
|
|
2613
2311
|
anchorFragment.nativeAppend = anchorFragment.append;
|
|
2614
2312
|
|
|
2615
2313
|
const isParentUniqueChild = isUniqueChild
|
|
2616
|
-
? () => true
|
|
2617
|
-
: (parent) => (parent.firstChild === anchorStart && parent.lastChild === anchorEnd);
|
|
2314
|
+
? () => true: (parent) => (parent.firstChild === anchorStart && parent.lastChild === anchorEnd);
|
|
2618
2315
|
|
|
2619
2316
|
const insertBefore = function(parent, child, target) {
|
|
2620
2317
|
const childElement = Validator.isElement(child) ? child : ElementCreator.getChild(child);
|
|
@@ -2629,14 +2326,13 @@ var NativeDocument = (function (exports) {
|
|
|
2629
2326
|
parent.insertBefore(childElement, target);
|
|
2630
2327
|
};
|
|
2631
2328
|
|
|
2632
|
-
anchorFragment.appendElement = function(child
|
|
2329
|
+
anchorFragment.appendElement = function(child) {
|
|
2633
2330
|
const parentNode = anchorStart.parentNode;
|
|
2634
|
-
const targetBefore = before || anchorEnd;
|
|
2635
2331
|
if(parentNode === anchorFragment) {
|
|
2636
|
-
parentNode.nativeInsertBefore(child,
|
|
2332
|
+
parentNode.nativeInsertBefore(child, anchorEnd);
|
|
2637
2333
|
return;
|
|
2638
2334
|
}
|
|
2639
|
-
parentNode
|
|
2335
|
+
parentNode.insertBefore(child, anchorEnd);
|
|
2640
2336
|
};
|
|
2641
2337
|
|
|
2642
2338
|
anchorFragment.appendChild = function(child, before = null) {
|
|
@@ -2649,8 +2345,8 @@ var NativeDocument = (function (exports) {
|
|
|
2649
2345
|
insertBefore(parent, child, before);
|
|
2650
2346
|
};
|
|
2651
2347
|
|
|
2652
|
-
anchorFragment.append = function(
|
|
2653
|
-
return anchorFragment.appendChild(
|
|
2348
|
+
anchorFragment.append = function() {
|
|
2349
|
+
return anchorFragment.appendChild(Array.from(arguments));
|
|
2654
2350
|
};
|
|
2655
2351
|
|
|
2656
2352
|
anchorFragment.removeChildren = function() {
|
|
@@ -2677,6 +2373,7 @@ var NativeDocument = (function (exports) {
|
|
|
2677
2373
|
return;
|
|
2678
2374
|
}
|
|
2679
2375
|
if(isParentUniqueChild(parent)) {
|
|
2376
|
+
anchorFragment.append.apply(anchorFragment, parent.childNodes);
|
|
2680
2377
|
parent.replaceChildren(anchorStart, anchorEnd);
|
|
2681
2378
|
return;
|
|
2682
2379
|
}
|
|
@@ -2721,9 +2418,11 @@ var NativeDocument = (function (exports) {
|
|
|
2721
2418
|
anchorFragment.startElement = function() {
|
|
2722
2419
|
return anchorStart;
|
|
2723
2420
|
};
|
|
2421
|
+
|
|
2724
2422
|
anchorFragment.restore = function() {
|
|
2725
2423
|
anchorFragment.appendChild(anchorFragment);
|
|
2726
2424
|
};
|
|
2425
|
+
|
|
2727
2426
|
anchorFragment.clear = anchorFragment.remove;
|
|
2728
2427
|
anchorFragment.detach = anchorFragment.remove;
|
|
2729
2428
|
|
|
@@ -3174,9 +2873,10 @@ var NativeDocument = (function (exports) {
|
|
|
3174
2873
|
* @returns {Text}
|
|
3175
2874
|
*/
|
|
3176
2875
|
const createTextNode = (value) => {
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
2876
|
+
if(value) {
|
|
2877
|
+
return value.toNdElement();
|
|
2878
|
+
}
|
|
2879
|
+
return ElementCreator.createTextNode();
|
|
3180
2880
|
};
|
|
3181
2881
|
|
|
3182
2882
|
|
|
@@ -3267,29 +2967,72 @@ var NativeDocument = (function (exports) {
|
|
|
3267
2967
|
}
|
|
3268
2968
|
const steps = [];
|
|
3269
2969
|
if(this.$ndMethods) {
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
2970
|
+
const methods = Object.keys(this.$ndMethods);
|
|
2971
|
+
if(methods.length === 1) {
|
|
2972
|
+
const methodName = methods[0];
|
|
2973
|
+
const callback = this.$ndMethods[methodName];
|
|
2974
|
+
steps.push((clonedNode, data) => {
|
|
2975
|
+
clonedNode.nd[methodName](callback.bind(clonedNode, ...data));
|
|
2976
|
+
});
|
|
2977
|
+
} else {
|
|
2978
|
+
steps.push((clonedNode, data) => {
|
|
2979
|
+
const nd = clonedNode.nd;
|
|
2980
|
+
for(const methodName in this.$ndMethods) {
|
|
2981
|
+
nd[methodName](this.$ndMethods[methodName].bind(clonedNode, ...data));
|
|
2982
|
+
}
|
|
2983
|
+
});
|
|
2984
|
+
}
|
|
3275
2985
|
}
|
|
3276
2986
|
if(this.$classes) {
|
|
3277
2987
|
const cache = {};
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
2988
|
+
const keys = Object.keys(this.$classes);
|
|
2989
|
+
|
|
2990
|
+
if(keys.length === 1) {
|
|
2991
|
+
const key = keys[0];
|
|
2992
|
+
const callback = this.$classes[key];
|
|
2993
|
+
steps.push((clonedNode, data) => {
|
|
2994
|
+
cache[key] = callback.apply(null, data);
|
|
2995
|
+
ElementCreator.processClassAttribute(clonedNode, cache);
|
|
2996
|
+
});
|
|
2997
|
+
} else {
|
|
2998
|
+
steps.push((clonedNode, data) => {
|
|
2999
|
+
ElementCreator.processClassAttribute(clonedNode, buildProperties(cache, this.$classes, data));
|
|
3000
|
+
});
|
|
3001
|
+
}
|
|
3281
3002
|
}
|
|
3282
3003
|
if(this.$styles) {
|
|
3283
3004
|
const cache = {};
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3005
|
+
const keys = Object.keys(this.$styles);
|
|
3006
|
+
|
|
3007
|
+
if(keys.length === 1) {
|
|
3008
|
+
const key = keys[0];
|
|
3009
|
+
const callback = this.$styles[key];
|
|
3010
|
+
steps.push((clonedNode, data) => {
|
|
3011
|
+
cache[key] = callback.apply(null, data);
|
|
3012
|
+
ElementCreator.processStyleAttribute(clonedNode, cache);
|
|
3013
|
+
});
|
|
3014
|
+
} else {
|
|
3015
|
+
steps.push((clonedNode, data) => {
|
|
3016
|
+
ElementCreator.processStyleAttribute(clonedNode, buildProperties(cache, this.$styles, data));
|
|
3017
|
+
});
|
|
3018
|
+
}
|
|
3287
3019
|
}
|
|
3288
3020
|
if(this.$attrs) {
|
|
3289
3021
|
const cache = {};
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3022
|
+
const keys = Object.keys(this.$attrs);
|
|
3023
|
+
|
|
3024
|
+
if(keys.length === 1) {
|
|
3025
|
+
const key = keys[0];
|
|
3026
|
+
const callback = this.$attrs[key];
|
|
3027
|
+
steps.push((clonedNode, data) => {
|
|
3028
|
+
cache[key] = callback.apply(null, data);
|
|
3029
|
+
ElementCreator.processAttributes(clonedNode, cache);
|
|
3030
|
+
});
|
|
3031
|
+
} else {
|
|
3032
|
+
steps.push((clonedNode, data) => {
|
|
3033
|
+
ElementCreator.processAttributes(clonedNode, buildProperties(cache, this.$attrs, data));
|
|
3034
|
+
});
|
|
3035
|
+
}
|
|
3293
3036
|
}
|
|
3294
3037
|
|
|
3295
3038
|
const stepsCount = steps.length;
|
|
@@ -3379,8 +3122,7 @@ var NativeDocument = (function (exports) {
|
|
|
3379
3122
|
$node.dynamicCloneNode = (data) => {
|
|
3380
3123
|
const clonedNode = $node.nodeCloner.cloneNode(data);
|
|
3381
3124
|
for(let i = 0; i < childNodesLength; i++) {
|
|
3382
|
-
|
|
3383
|
-
clonedNode.appendChild(child);
|
|
3125
|
+
clonedNode.appendChild(childNodes[i].dynamicCloneNode(data));
|
|
3384
3126
|
}
|
|
3385
3127
|
return clonedNode;
|
|
3386
3128
|
};
|
|
@@ -3388,8 +3130,7 @@ var NativeDocument = (function (exports) {
|
|
|
3388
3130
|
$node.dynamicCloneNode = (data) => {
|
|
3389
3131
|
const clonedNode = $node.cloneNode();
|
|
3390
3132
|
for(let i = 0; i < childNodesLength; i++) {
|
|
3391
|
-
|
|
3392
|
-
clonedNode.appendChild(child);
|
|
3133
|
+
clonedNode.appendChild(childNodes[i].dynamicCloneNode(data));
|
|
3393
3134
|
}
|
|
3394
3135
|
return clonedNode;
|
|
3395
3136
|
};
|
|
@@ -4221,491 +3962,891 @@ var NativeDocument = (function (exports) {
|
|
|
4221
3962
|
};
|
|
4222
3963
|
|
|
4223
3964
|
/**
|
|
4224
|
-
* Triggers a populate operation with the current array, iteration count, and callback.
|
|
4225
|
-
* Used internally for rendering optimizations.
|
|
3965
|
+
* Triggers a populate operation with the current array, iteration count, and callback.
|
|
3966
|
+
* Used internally for rendering optimizations.
|
|
3967
|
+
*
|
|
3968
|
+
* @param {number} iteration - Iteration count for rendering
|
|
3969
|
+
* @param {Function} callback - Callback function for rendering items
|
|
3970
|
+
*/
|
|
3971
|
+
ObservableArray.prototype.populateAndRender = function(iteration, callback) {
|
|
3972
|
+
this.trigger({ action: 'populate', args: [this.$currentValue, iteration, callback] });
|
|
3973
|
+
};
|
|
3974
|
+
|
|
3975
|
+
|
|
3976
|
+
/**
|
|
3977
|
+
* Creates a filtered view of the array based on predicates.
|
|
3978
|
+
* The filtered array updates automatically when source data or predicates change.
|
|
3979
|
+
*
|
|
3980
|
+
* @param {Object} predicates - Object mapping property names to filter conditions or functions
|
|
3981
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
3982
|
+
* @example
|
|
3983
|
+
* const users = Observable.array([
|
|
3984
|
+
* { name: 'John', age: 25 },
|
|
3985
|
+
* { name: 'Jane', age: 30 }
|
|
3986
|
+
* ]);
|
|
3987
|
+
* const adults = users.where({ age: (val) => val >= 18 });
|
|
3988
|
+
*/
|
|
3989
|
+
ObservableArray.prototype.where = function(predicates) {
|
|
3990
|
+
const sourceArray = this;
|
|
3991
|
+
const observableDependencies = [sourceArray];
|
|
3992
|
+
const filterCallbacks = {};
|
|
3993
|
+
|
|
3994
|
+
for (const [key, rawPredicate] of Object.entries(predicates)) {
|
|
3995
|
+
const predicate = Validator.isObservable(rawPredicate) ? match(rawPredicate, false) : rawPredicate;
|
|
3996
|
+
if (predicate && typeof predicate === 'object' && 'callback' in predicate) {
|
|
3997
|
+
filterCallbacks[key] = predicate.callback;
|
|
3998
|
+
|
|
3999
|
+
if (predicate.dependencies) {
|
|
4000
|
+
const deps = Array.isArray(predicate.dependencies)
|
|
4001
|
+
? predicate.dependencies
|
|
4002
|
+
: [predicate.dependencies];
|
|
4003
|
+
observableDependencies.push.apply(observableDependencies, deps);
|
|
4004
|
+
}
|
|
4005
|
+
} else if(typeof predicate === 'function') {
|
|
4006
|
+
filterCallbacks[key] = predicate;
|
|
4007
|
+
} else {
|
|
4008
|
+
filterCallbacks[key] = (value) => value === predicate;
|
|
4009
|
+
}
|
|
4010
|
+
}
|
|
4011
|
+
|
|
4012
|
+
const viewArray = Observable.array();
|
|
4013
|
+
|
|
4014
|
+
const filters = Object.entries(filterCallbacks);
|
|
4015
|
+
const updateView = () => {
|
|
4016
|
+
const filtered = sourceArray.val().filter(item => {
|
|
4017
|
+
for (const [key, callback] of filters) {
|
|
4018
|
+
if(key === '_') {
|
|
4019
|
+
if (!callback(item)) return false;
|
|
4020
|
+
} else {
|
|
4021
|
+
if (!callback(item[key])) return false;
|
|
4022
|
+
}
|
|
4023
|
+
}
|
|
4024
|
+
return true;
|
|
4025
|
+
});
|
|
4026
|
+
|
|
4027
|
+
viewArray.set(filtered);
|
|
4028
|
+
};
|
|
4029
|
+
|
|
4030
|
+
observableDependencies.forEach(dep => dep.subscribe(updateView));
|
|
4031
|
+
|
|
4032
|
+
updateView();
|
|
4033
|
+
|
|
4034
|
+
return viewArray;
|
|
4035
|
+
};
|
|
4036
|
+
|
|
4037
|
+
/**
|
|
4038
|
+
* Creates a filtered view where at least one of the specified fields matches the filter.
|
|
4039
|
+
*
|
|
4040
|
+
* @param {Array<string>} fields - Array of field names to check
|
|
4041
|
+
* @param {FilterResult} filter - Filter condition with callback and dependencies
|
|
4042
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
4043
|
+
* @example
|
|
4044
|
+
* const products = Observable.array([
|
|
4045
|
+
* { name: 'Apple', category: 'Fruit' },
|
|
4046
|
+
* { name: 'Carrot', category: 'Vegetable' }
|
|
4047
|
+
* ]);
|
|
4048
|
+
* const searchTerm = Observable('App');
|
|
4049
|
+
* const filtered = products.whereSome(['name', 'category'], match(searchTerm));
|
|
4050
|
+
*/
|
|
4051
|
+
ObservableArray.prototype.whereSome = function(fields, filter) {
|
|
4052
|
+
return this.where({
|
|
4053
|
+
_: {
|
|
4054
|
+
dependencies: filter.dependencies,
|
|
4055
|
+
callback: (item) => fields.some(field => filter.callback(item[field]))
|
|
4056
|
+
}
|
|
4057
|
+
});
|
|
4058
|
+
};
|
|
4059
|
+
|
|
4060
|
+
/**
|
|
4061
|
+
* Creates a filtered view where all specified fields match the filter.
|
|
4062
|
+
*
|
|
4063
|
+
* @param {Array<string>} fields - Array of field names to check
|
|
4064
|
+
* @param {FilterResult} filter - Filter condition with callback and dependencies
|
|
4065
|
+
* @returns {ObservableArray} A new observable array containing filtered items
|
|
4066
|
+
* @example
|
|
4067
|
+
* const items = Observable.array([
|
|
4068
|
+
* { status: 'active', verified: true },
|
|
4069
|
+
* { status: 'active', verified: false }
|
|
4070
|
+
* ]);
|
|
4071
|
+
* const activeFilter = equals('active');
|
|
4072
|
+
* const filtered = items.whereEvery(['status', 'verified'], activeFilter);
|
|
4073
|
+
*/
|
|
4074
|
+
ObservableArray.prototype.whereEvery = function(fields, filter) {
|
|
4075
|
+
return this.where({
|
|
4076
|
+
_: {
|
|
4077
|
+
dependencies: filter.dependencies,
|
|
4078
|
+
callback: (item) => fields.every(field => filter.callback(item[field]))
|
|
4079
|
+
}
|
|
4080
|
+
});
|
|
4081
|
+
};
|
|
4082
|
+
|
|
4083
|
+
ObservableArray.prototype.deepSubscribe = function(callback) {
|
|
4084
|
+
const updatedValue = nextTick(() => callback(this.val()));
|
|
4085
|
+
const $listeners = new WeakMap();
|
|
4086
|
+
|
|
4087
|
+
const bindItem = (item) => {
|
|
4088
|
+
if ($listeners.has(item)) {
|
|
4089
|
+
return;
|
|
4090
|
+
}
|
|
4091
|
+
if (item?.__$isObservableArray) {
|
|
4092
|
+
$listeners.set(item, item.deepSubscribe(updatedValue));
|
|
4093
|
+
return;
|
|
4094
|
+
}
|
|
4095
|
+
if (item?.__$isObservable) {
|
|
4096
|
+
item.subscribe(updatedValue);
|
|
4097
|
+
$listeners.set(item, () => item.unsubscribe(updatedValue));
|
|
4098
|
+
}
|
|
4099
|
+
};
|
|
4100
|
+
|
|
4101
|
+
const unbindItem = (item) => {
|
|
4102
|
+
const unsub = $listeners.get(item);
|
|
4103
|
+
if (unsub) {
|
|
4104
|
+
unsub();
|
|
4105
|
+
$listeners.delete(item);
|
|
4106
|
+
}
|
|
4107
|
+
};
|
|
4108
|
+
|
|
4109
|
+
this.$currentValue.forEach(bindItem);
|
|
4110
|
+
this.subscribe(updatedValue);
|
|
4111
|
+
|
|
4112
|
+
this.subscribe((items, _, operations) => {
|
|
4113
|
+
switch (operations?.action) {
|
|
4114
|
+
case 'push':
|
|
4115
|
+
case 'unshift':
|
|
4116
|
+
operations.args.forEach(bindItem);
|
|
4117
|
+
break;
|
|
4118
|
+
|
|
4119
|
+
case 'splice': {
|
|
4120
|
+
const [start, deleteCount, ...newItems] = operations.args;
|
|
4121
|
+
operations.result?.forEach(unbindItem);
|
|
4122
|
+
newItems.forEach(bindItem);
|
|
4123
|
+
break;
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
case 'remove':
|
|
4127
|
+
unbindItem(operations.result);
|
|
4128
|
+
break;
|
|
4129
|
+
|
|
4130
|
+
case 'merge':
|
|
4131
|
+
operations.args.forEach(bindItem);
|
|
4132
|
+
break;
|
|
4133
|
+
|
|
4134
|
+
case 'clear':
|
|
4135
|
+
this.$currentValue.forEach(unbindItem);
|
|
4136
|
+
break;
|
|
4137
|
+
}
|
|
4138
|
+
});
|
|
4139
|
+
|
|
4140
|
+
return () => {
|
|
4141
|
+
this.$currentValue.forEach(unbindItem);
|
|
4142
|
+
};
|
|
4143
|
+
};
|
|
4144
|
+
|
|
4145
|
+
/**
|
|
4146
|
+
* Creates an observable array with reactive array methods.
|
|
4147
|
+
* All mutations trigger updates automatically.
|
|
4148
|
+
*
|
|
4149
|
+
* @param {Array} [target=[]] - Initial array value
|
|
4150
|
+
* @param {Object|null} [configs=null] - Configuration options
|
|
4151
|
+
* // @param {boolean} [configs.propagation=true] - Whether to propagate changes to parent observables
|
|
4152
|
+
* // @param {boolean} [configs.deep=false] - Whether to make nested objects observable
|
|
4153
|
+
* @param {boolean} [configs.reset=false] - Whether to store initial value for reset()
|
|
4154
|
+
* @returns {ObservableArray} An observable array with reactive methods
|
|
4155
|
+
* @example
|
|
4156
|
+
* const items = Observable.array([1, 2, 3]);
|
|
4157
|
+
* items.push(4); // Triggers update
|
|
4158
|
+
* items.subscribe((arr) => console.log(arr));
|
|
4159
|
+
*/
|
|
4160
|
+
Observable.array = function(target = [], configs = null) {
|
|
4161
|
+
return new ObservableArray(target, configs);
|
|
4162
|
+
};
|
|
4163
|
+
|
|
4164
|
+
/**
|
|
4226
4165
|
*
|
|
4227
|
-
* @param {
|
|
4228
|
-
* @
|
|
4166
|
+
* @param {Function} callback
|
|
4167
|
+
* @returns {Function}
|
|
4229
4168
|
*/
|
|
4230
|
-
|
|
4231
|
-
|
|
4169
|
+
Observable.batch = function(callback) {
|
|
4170
|
+
const $observer = Observable(0);
|
|
4171
|
+
const batch = function() {
|
|
4172
|
+
if(Validator.isAsyncFunction(callback)) {
|
|
4173
|
+
return (callback(...arguments)).then(() => {
|
|
4174
|
+
$observer.trigger();
|
|
4175
|
+
}).catch(error => { throw error; });
|
|
4176
|
+
}
|
|
4177
|
+
callback(...arguments);
|
|
4178
|
+
$observer.trigger();
|
|
4179
|
+
};
|
|
4180
|
+
batch.$observer = $observer;
|
|
4181
|
+
return batch;
|
|
4232
4182
|
};
|
|
4233
4183
|
|
|
4184
|
+
const ObservableObject = function(target, configs) {
|
|
4185
|
+
ObservableItem.call(this, target);
|
|
4186
|
+
this.$observables = {};
|
|
4187
|
+
this.configs = configs;
|
|
4234
4188
|
|
|
4235
|
-
|
|
4236
|
-
* Creates a filtered view of the array based on predicates.
|
|
4237
|
-
* The filtered array updates automatically when source data or predicates change.
|
|
4238
|
-
*
|
|
4239
|
-
* @param {Object} predicates - Object mapping property names to filter conditions or functions
|
|
4240
|
-
* @returns {ObservableArray} A new observable array containing filtered items
|
|
4241
|
-
* @example
|
|
4242
|
-
* const users = Observable.array([
|
|
4243
|
-
* { name: 'John', age: 25 },
|
|
4244
|
-
* { name: 'Jane', age: 30 }
|
|
4245
|
-
* ]);
|
|
4246
|
-
* const adults = users.where({ age: (val) => val >= 18 });
|
|
4247
|
-
*/
|
|
4248
|
-
ObservableArray.prototype.where = function(predicates) {
|
|
4249
|
-
const sourceArray = this;
|
|
4250
|
-
const observableDependencies = [sourceArray];
|
|
4251
|
-
const filterCallbacks = {};
|
|
4189
|
+
this.$load(target);
|
|
4252
4190
|
|
|
4253
|
-
for
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4191
|
+
for(const name in target) {
|
|
4192
|
+
if(!Object.hasOwn(this, name)) {
|
|
4193
|
+
Object.defineProperty(this, name, {
|
|
4194
|
+
get: () => this.$observables[name],
|
|
4195
|
+
set: (value) => this.$observables[name].set(value)
|
|
4196
|
+
});
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4257
4199
|
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4200
|
+
};
|
|
4201
|
+
|
|
4202
|
+
ObservableObject.prototype = Object.create(ObservableItem.prototype);
|
|
4203
|
+
|
|
4204
|
+
Object.defineProperty(ObservableObject, '$value', {
|
|
4205
|
+
get() {
|
|
4206
|
+
return this.val();
|
|
4207
|
+
},
|
|
4208
|
+
set(value) {
|
|
4209
|
+
this.set(value);
|
|
4210
|
+
}
|
|
4211
|
+
});
|
|
4212
|
+
|
|
4213
|
+
ObservableObject.prototype.__$isObservableObject = true;
|
|
4214
|
+
ObservableObject.prototype.__isProxy__ = true;
|
|
4215
|
+
|
|
4216
|
+
ObservableObject.prototype.$load = function(initialValue) {
|
|
4217
|
+
const configs = this.configs;
|
|
4218
|
+
for(const key in initialValue) {
|
|
4219
|
+
const itemValue = initialValue[key];
|
|
4220
|
+
if(Array.isArray(itemValue)) {
|
|
4221
|
+
if(configs?.deep !== false) {
|
|
4222
|
+
const mappedItemValue = itemValue.map(item => {
|
|
4223
|
+
if(Validator.isJson(item)) {
|
|
4224
|
+
return Observable.json(item, configs);
|
|
4225
|
+
}
|
|
4226
|
+
if(Validator.isArray(item)) {
|
|
4227
|
+
return Observable.array(item, configs);
|
|
4228
|
+
}
|
|
4229
|
+
return Observable(item, configs);
|
|
4230
|
+
});
|
|
4231
|
+
this.$observables[key] = Observable.array(mappedItemValue, configs);
|
|
4232
|
+
continue;
|
|
4263
4233
|
}
|
|
4264
|
-
|
|
4265
|
-
|
|
4234
|
+
this.$observables[key] = Observable.array(itemValue, configs);
|
|
4235
|
+
continue;
|
|
4236
|
+
}
|
|
4237
|
+
if(Validator.isObservable(itemValue) || Validator.isProxy(itemValue)) {
|
|
4238
|
+
this.$observables[key] = itemValue;
|
|
4239
|
+
continue;
|
|
4240
|
+
}
|
|
4241
|
+
this.$observables[key] = (typeof itemValue === 'object') ? Observable.object(itemValue, configs) : Observable(itemValue, configs);
|
|
4242
|
+
}
|
|
4243
|
+
};
|
|
4244
|
+
|
|
4245
|
+
ObservableObject.prototype.val = function() {
|
|
4246
|
+
const result = {};
|
|
4247
|
+
for(const key in this.$observables) {
|
|
4248
|
+
const dataItem = this.$observables[key];
|
|
4249
|
+
if(Validator.isObservable(dataItem)) {
|
|
4250
|
+
let value = dataItem.val();
|
|
4251
|
+
if(Array.isArray(value)) {
|
|
4252
|
+
value = value.map(item => {
|
|
4253
|
+
if(Validator.isObservable(item)) {
|
|
4254
|
+
return item.val();
|
|
4255
|
+
}
|
|
4256
|
+
if(Validator.isProxy(item)) {
|
|
4257
|
+
return item.$value;
|
|
4258
|
+
}
|
|
4259
|
+
return item;
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
result[key] = value;
|
|
4263
|
+
} else if(Validator.isProxy(dataItem)) {
|
|
4264
|
+
result[key] = dataItem.$value;
|
|
4266
4265
|
} else {
|
|
4267
|
-
|
|
4266
|
+
result[key] = dataItem;
|
|
4268
4267
|
}
|
|
4269
4268
|
}
|
|
4269
|
+
return result;
|
|
4270
|
+
};
|
|
4271
|
+
ObservableObject.prototype.$val = ObservableObject.prototype.val;
|
|
4270
4272
|
|
|
4271
|
-
|
|
4273
|
+
ObservableObject.prototype.get = function(property) {
|
|
4274
|
+
const item = this.$observables[property];
|
|
4275
|
+
if(Validator.isObservable(item)) {
|
|
4276
|
+
return item.val();
|
|
4277
|
+
}
|
|
4278
|
+
if(Validator.isProxy(item)) {
|
|
4279
|
+
return item.$value;
|
|
4280
|
+
}
|
|
4281
|
+
return item;
|
|
4282
|
+
};
|
|
4283
|
+
ObservableObject.prototype.$get = ObservableObject.prototype.get;
|
|
4272
4284
|
|
|
4273
|
-
|
|
4274
|
-
const
|
|
4275
|
-
|
|
4276
|
-
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4285
|
+
ObservableObject.prototype.set = function(newData) {
|
|
4286
|
+
const data = Validator.isProxy(newData) ? newData.$value : newData;
|
|
4287
|
+
const configs = this.configs;
|
|
4288
|
+
|
|
4289
|
+
for(const key in data) {
|
|
4290
|
+
const targetItem = this.$observables[key];
|
|
4291
|
+
const newValueOrigin = newData[key];
|
|
4292
|
+
const newValue = data[key];
|
|
4293
|
+
|
|
4294
|
+
if(Validator.isObservable(targetItem)) {
|
|
4295
|
+
if(!Validator.isArray(newValue)) {
|
|
4296
|
+
targetItem.set(newValue);
|
|
4297
|
+
continue;
|
|
4282
4298
|
}
|
|
4283
|
-
|
|
4284
|
-
|
|
4299
|
+
const firstElementFromOriginalValue = newValueOrigin.at(0);
|
|
4300
|
+
if(Validator.isObservable(firstElementFromOriginalValue) || Validator.isProxy(firstElementFromOriginalValue)) {
|
|
4301
|
+
const newValues = newValue.map(item => {
|
|
4302
|
+
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
4303
|
+
return Observable.init(item, configs);
|
|
4304
|
+
}
|
|
4305
|
+
return Observable(item, configs);
|
|
4306
|
+
});
|
|
4307
|
+
targetItem.set(newValues);
|
|
4308
|
+
continue;
|
|
4309
|
+
}
|
|
4310
|
+
targetItem.set([...newValue]);
|
|
4311
|
+
continue;
|
|
4312
|
+
}
|
|
4313
|
+
if(Validator.isProxy(targetItem)) {
|
|
4314
|
+
targetItem.update(newValue);
|
|
4315
|
+
continue;
|
|
4316
|
+
}
|
|
4317
|
+
this[key] = newValue;
|
|
4318
|
+
}
|
|
4319
|
+
};
|
|
4320
|
+
ObservableObject.prototype.$set = ObservableObject.prototype.set;
|
|
4321
|
+
ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
|
|
4285
4322
|
|
|
4286
|
-
|
|
4287
|
-
|
|
4323
|
+
ObservableObject.prototype.observables = function() {
|
|
4324
|
+
return Object.values(this.$observables);
|
|
4325
|
+
};
|
|
4326
|
+
ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
|
|
4288
4327
|
|
|
4289
|
-
|
|
4328
|
+
ObservableObject.prototype.keys = function() {
|
|
4329
|
+
return Object.keys(this.$observables);
|
|
4330
|
+
};
|
|
4331
|
+
ObservableObject.prototype.$keys = ObservableObject.prototype.keys;
|
|
4332
|
+
ObservableObject.prototype.clone = function() {
|
|
4333
|
+
return Observable.init(this.val(), this.configs);
|
|
4334
|
+
};
|
|
4335
|
+
ObservableObject.prototype.$clone = ObservableObject.prototype.clone;
|
|
4336
|
+
ObservableObject.prototype.reset = function() {
|
|
4337
|
+
for(const key in this.$observables) {
|
|
4338
|
+
this.$observables[key].reset();
|
|
4339
|
+
}
|
|
4340
|
+
};
|
|
4341
|
+
ObservableObject.prototype.originalSubscribe = ObservableObject.prototype.subscribe;
|
|
4342
|
+
ObservableObject.prototype.subscribe = function(callback) {
|
|
4343
|
+
const observables = this.observables();
|
|
4344
|
+
const updatedValue = nextTick(() => this.trigger());
|
|
4290
4345
|
|
|
4291
|
-
|
|
4346
|
+
this.originalSubscribe(callback);
|
|
4292
4347
|
|
|
4293
|
-
|
|
4348
|
+
for (let i = 0, length = observables.length; i < length; i++) {
|
|
4349
|
+
const observable = observables[i];
|
|
4350
|
+
if (observable.__$isObservableArray) {
|
|
4351
|
+
observable.deepSubscribe(updatedValue);
|
|
4352
|
+
continue
|
|
4353
|
+
}
|
|
4354
|
+
observable.subscribe(updatedValue);
|
|
4355
|
+
}
|
|
4356
|
+
};
|
|
4357
|
+
ObservableObject.prototype.configs = function() {
|
|
4358
|
+
return this.configs;
|
|
4359
|
+
};
|
|
4360
|
+
|
|
4361
|
+
ObservableObject.prototype.update = ObservableObject.prototype.set;
|
|
4362
|
+
|
|
4363
|
+
Observable.init = function(initialValue, configs = null) {
|
|
4364
|
+
return new ObservableObject(initialValue, configs)
|
|
4294
4365
|
};
|
|
4295
4366
|
|
|
4296
4367
|
/**
|
|
4297
|
-
* Creates a filtered view where at least one of the specified fields matches the filter.
|
|
4298
4368
|
*
|
|
4299
|
-
* @param {
|
|
4300
|
-
* @
|
|
4301
|
-
|
|
4302
|
-
|
|
4303
|
-
|
|
4304
|
-
* { name: 'Apple', category: 'Fruit' },
|
|
4305
|
-
* { name: 'Carrot', category: 'Vegetable' }
|
|
4306
|
-
* ]);
|
|
4307
|
-
* const searchTerm = Observable('App');
|
|
4308
|
-
* const filtered = products.whereSome(['name', 'category'], match(searchTerm));
|
|
4309
|
-
*/
|
|
4310
|
-
ObservableArray.prototype.whereSome = function(fields, filter) {
|
|
4311
|
-
return this.where({
|
|
4312
|
-
_: {
|
|
4313
|
-
dependencies: filter.dependencies,
|
|
4314
|
-
callback: (item) => fields.some(field => filter.callback(item[field]))
|
|
4315
|
-
}
|
|
4316
|
-
});
|
|
4369
|
+
* @param {any[]} data
|
|
4370
|
+
* @return Proxy[]
|
|
4371
|
+
*/
|
|
4372
|
+
Observable.arrayOfObject = function(data) {
|
|
4373
|
+
return data.map(item => Observable.object(item));
|
|
4317
4374
|
};
|
|
4318
4375
|
|
|
4319
4376
|
/**
|
|
4320
|
-
*
|
|
4321
|
-
*
|
|
4322
|
-
* @
|
|
4323
|
-
* @param {FilterResult} filter - Filter condition with callback and dependencies
|
|
4324
|
-
* @returns {ObservableArray} A new observable array containing filtered items
|
|
4325
|
-
* @example
|
|
4326
|
-
* const items = Observable.array([
|
|
4327
|
-
* { status: 'active', verified: true },
|
|
4328
|
-
* { status: 'active', verified: false }
|
|
4329
|
-
* ]);
|
|
4330
|
-
* const activeFilter = equals('active');
|
|
4331
|
-
* const filtered = items.whereEvery(['status', 'verified'], activeFilter);
|
|
4377
|
+
* Get the value of an observable or an object of observables.
|
|
4378
|
+
* @param {ObservableItem|Object<ObservableItem>} data
|
|
4379
|
+
* @returns {{}|*|null}
|
|
4332
4380
|
*/
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4381
|
+
Observable.value = function(data) {
|
|
4382
|
+
if(Validator.isObservable(data)) {
|
|
4383
|
+
return data.val();
|
|
4384
|
+
}
|
|
4385
|
+
if(Validator.isProxy(data)) {
|
|
4386
|
+
return data.$value;
|
|
4387
|
+
}
|
|
4388
|
+
if(Validator.isArray(data)) {
|
|
4389
|
+
const result = [];
|
|
4390
|
+
for(let i = 0, length = data.length; i < length; i++) {
|
|
4391
|
+
const item = data[i];
|
|
4392
|
+
result.push(Observable.value(item));
|
|
4338
4393
|
}
|
|
4339
|
-
|
|
4394
|
+
return result;
|
|
4395
|
+
}
|
|
4396
|
+
return data;
|
|
4340
4397
|
};
|
|
4341
4398
|
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
const $listeners = new WeakMap();
|
|
4399
|
+
Observable.object = Observable.init;
|
|
4400
|
+
Observable.json = Observable.init;
|
|
4345
4401
|
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
|
|
4402
|
+
/**
|
|
4403
|
+
* Creates a computed observable that automatically updates when its dependencies change.
|
|
4404
|
+
* The callback is re-executed whenever any dependency observable changes.
|
|
4405
|
+
*
|
|
4406
|
+
* @param {Function} callback - Function that returns the computed value
|
|
4407
|
+
* @param {Array<ObservableItem|ObservableChecker|ObservableProxy>|Function} [dependencies=[]] - Array of observables to watch, or batch function
|
|
4408
|
+
* @returns {ObservableItem} A new observable that updates automatically
|
|
4409
|
+
* @example
|
|
4410
|
+
* const firstName = Observable('John');
|
|
4411
|
+
* const lastName = Observable('Doe');
|
|
4412
|
+
* const fullName = Observable.computed(
|
|
4413
|
+
* () => `${firstName.val()} ${lastName.val()}`,
|
|
4414
|
+
* [firstName, lastName]
|
|
4415
|
+
* );
|
|
4416
|
+
*
|
|
4417
|
+
* // With batch function
|
|
4418
|
+
* const batch = Observable.batch(() => { ... });
|
|
4419
|
+
* const computed = Observable.computed(() => { ... }, batch);
|
|
4420
|
+
*/
|
|
4421
|
+
Observable.computed = function(callback, dependencies = []) {
|
|
4422
|
+
const initialValue = callback();
|
|
4423
|
+
const observable = new ObservableItem(initialValue);
|
|
4424
|
+
const updatedValue = nextTick(() => observable.set(callback()));
|
|
4425
|
+
{
|
|
4426
|
+
PluginsManager.emit('CreateObservableComputed', observable, dependencies);
|
|
4427
|
+
}
|
|
4359
4428
|
|
|
4360
|
-
|
|
4361
|
-
|
|
4362
|
-
|
|
4363
|
-
unsub();
|
|
4364
|
-
$listeners.delete(item);
|
|
4429
|
+
if(Validator.isFunction(dependencies)) {
|
|
4430
|
+
if(!Validator.isObservable(dependencies.$observer)) {
|
|
4431
|
+
throw new NativeDocumentError('Observable.computed : dependencies must be valid batch function');
|
|
4365
4432
|
}
|
|
4366
|
-
|
|
4367
|
-
|
|
4368
|
-
|
|
4369
|
-
this.subscribe(updatedValue);
|
|
4433
|
+
dependencies.$observer.subscribe(updatedValue);
|
|
4434
|
+
return observable;
|
|
4435
|
+
}
|
|
4370
4436
|
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4437
|
+
dependencies.forEach(dependency => {
|
|
4438
|
+
if(Validator.isProxy(dependency)) {
|
|
4439
|
+
dependency.$observables.forEach((observable) => {
|
|
4440
|
+
observable.subscribe(updatedValue);
|
|
4441
|
+
});
|
|
4442
|
+
return;
|
|
4443
|
+
}
|
|
4444
|
+
dependency.subscribe(updatedValue);
|
|
4445
|
+
});
|
|
4377
4446
|
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
operations.result?.forEach(unbindItem);
|
|
4381
|
-
newItems.forEach(bindItem);
|
|
4382
|
-
break;
|
|
4383
|
-
}
|
|
4447
|
+
return observable;
|
|
4448
|
+
};
|
|
4384
4449
|
|
|
4385
|
-
|
|
4386
|
-
unbindItem(operations.result);
|
|
4387
|
-
break;
|
|
4450
|
+
const StoreFactory = function() {
|
|
4388
4451
|
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
break;
|
|
4452
|
+
const $stores = new Map();
|
|
4453
|
+
const $followersCache = new Map();
|
|
4392
4454
|
|
|
4393
|
-
|
|
4394
|
-
|
|
4395
|
-
|
|
4455
|
+
/**
|
|
4456
|
+
* Internal helper — retrieves a store entry or throws if not found.
|
|
4457
|
+
*/
|
|
4458
|
+
const $getStoreOrThrow = (method, name) => {
|
|
4459
|
+
const item = $stores.get(name);
|
|
4460
|
+
if (!item) {
|
|
4461
|
+
DebugManager$1.error('Store', `Store.${method}('${name}') : store not found. Did you call Store.create('${name}') first?`);
|
|
4462
|
+
throw new NativeDocumentError(
|
|
4463
|
+
`Store.${method}('${name}') : store not found.`
|
|
4464
|
+
);
|
|
4396
4465
|
}
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
return () => {
|
|
4400
|
-
this.$currentValue.forEach(unbindItem);
|
|
4466
|
+
return item;
|
|
4401
4467
|
};
|
|
4402
|
-
};
|
|
4403
4468
|
|
|
4404
|
-
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
4410
|
-
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
*/
|
|
4419
|
-
Observable.array = function(target = [], configs = null) {
|
|
4420
|
-
return new ObservableArray(target, configs);
|
|
4421
|
-
};
|
|
4469
|
+
/**
|
|
4470
|
+
* Internal helper — blocks write operations on a read-only observer.
|
|
4471
|
+
*/
|
|
4472
|
+
const $applyReadOnly = (observer, name, context) => {
|
|
4473
|
+
const readOnlyError = (method) => () => {
|
|
4474
|
+
DebugManager$1.error('Store', `Store.${context}('${name}') is read-only. '${method}()' is not allowed.`);
|
|
4475
|
+
throw new NativeDocumentError(
|
|
4476
|
+
`Store.${context}('${name}') is read-only.`
|
|
4477
|
+
);
|
|
4478
|
+
};
|
|
4479
|
+
observer.set = readOnlyError('set');
|
|
4480
|
+
observer.toggle = readOnlyError('toggle');
|
|
4481
|
+
observer.reset = readOnlyError('reset');
|
|
4482
|
+
};
|
|
4422
4483
|
|
|
4423
|
-
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
* @returns {Function}
|
|
4427
|
-
*/
|
|
4428
|
-
Observable.batch = function(callback) {
|
|
4429
|
-
const $observer = Observable(0);
|
|
4430
|
-
const batch = function() {
|
|
4431
|
-
if(Validator.isAsyncFunction(callback)) {
|
|
4432
|
-
return (callback(...arguments)).then(() => {
|
|
4433
|
-
$observer.trigger();
|
|
4434
|
-
}).catch(error => { throw error; });
|
|
4484
|
+
const $createObservable = (value, options = {}) => {
|
|
4485
|
+
if(Array.isArray(value)) {
|
|
4486
|
+
return Observable.array(value, options);
|
|
4435
4487
|
}
|
|
4436
|
-
|
|
4437
|
-
|
|
4488
|
+
if(typeof value === 'object') {
|
|
4489
|
+
return Observable.object(value, options);
|
|
4490
|
+
}
|
|
4491
|
+
return Observable(value, options);
|
|
4438
4492
|
};
|
|
4439
|
-
batch.$observer = $observer;
|
|
4440
|
-
return batch;
|
|
4441
|
-
};
|
|
4442
4493
|
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4494
|
+
const $api = {
|
|
4495
|
+
/**
|
|
4496
|
+
* Create a new state and return the observer.
|
|
4497
|
+
* Throws if a store with the same name already exists.
|
|
4498
|
+
*
|
|
4499
|
+
* @param {string} name
|
|
4500
|
+
* @param {*} value
|
|
4501
|
+
* @returns {ObservableItem}
|
|
4502
|
+
*/
|
|
4503
|
+
create(name, value) {
|
|
4504
|
+
if ($stores.has(name)) {
|
|
4505
|
+
DebugManager$1.warn('Store', `Store.create('${name}') : a store with this name already exists. Use Store.get('${name}') to retrieve it.`);
|
|
4506
|
+
throw new NativeDocumentError(
|
|
4507
|
+
`Store.create('${name}') : a store with this name already exists.`
|
|
4508
|
+
);
|
|
4509
|
+
}
|
|
4510
|
+
const observer = $createObservable(value);
|
|
4511
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: false });
|
|
4512
|
+
return observer;
|
|
4513
|
+
},
|
|
4447
4514
|
|
|
4448
|
-
|
|
4515
|
+
/**
|
|
4516
|
+
* Create a new resettable state and return the observer.
|
|
4517
|
+
* The store can be reset to its initial value via Store.reset(name).
|
|
4518
|
+
* Throws if a store with the same name already exists.
|
|
4519
|
+
*
|
|
4520
|
+
* @param {string} name
|
|
4521
|
+
* @param {*} value
|
|
4522
|
+
* @returns {ObservableItem}
|
|
4523
|
+
*/
|
|
4524
|
+
createResettable(name, value) {
|
|
4525
|
+
if ($stores.has(name)) {
|
|
4526
|
+
DebugManager$1.warn('Store', `Store.createResettable('${name}') : a store with this name already exists.`);
|
|
4527
|
+
throw new NativeDocumentError(
|
|
4528
|
+
`Store.createResettable('${name}') : a store with this name already exists.`
|
|
4529
|
+
);
|
|
4530
|
+
}
|
|
4531
|
+
const observer = $createObservable(value, { reset: true });
|
|
4532
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: true, composed: false });
|
|
4533
|
+
return observer;
|
|
4534
|
+
},
|
|
4535
|
+
|
|
4536
|
+
/**
|
|
4537
|
+
* Create a computed store derived from other stores.
|
|
4538
|
+
* The value is automatically recalculated when any dependency changes.
|
|
4539
|
+
* This store is read-only — Store.use() and Store.set() will throw.
|
|
4540
|
+
* Throws if a store with the same name already exists.
|
|
4541
|
+
*
|
|
4542
|
+
* @param {string} name
|
|
4543
|
+
* @param {() => *} computation - Function that returns the computed value
|
|
4544
|
+
* @param {string[]} dependencies - Names of the stores to watch
|
|
4545
|
+
* @returns {ObservableItem}
|
|
4546
|
+
*
|
|
4547
|
+
* @example
|
|
4548
|
+
* Store.create('products', [{ id: 1, price: 10 }]);
|
|
4549
|
+
* Store.create('cart', [{ productId: 1, quantity: 2 }]);
|
|
4550
|
+
*
|
|
4551
|
+
* Store.createComposed('total', () => {
|
|
4552
|
+
* const products = Store.get('products').val();
|
|
4553
|
+
* const cart = Store.get('cart').val();
|
|
4554
|
+
* return cart.reduce((sum, item) => {
|
|
4555
|
+
* const product = products.find(p => p.id === item.productId);
|
|
4556
|
+
* return sum + (product.price * item.quantity);
|
|
4557
|
+
* }, 0);
|
|
4558
|
+
* }, ['products', 'cart']);
|
|
4559
|
+
*/
|
|
4560
|
+
createComposed(name, computation, dependencies) {
|
|
4561
|
+
if ($stores.has(name)) {
|
|
4562
|
+
DebugManager$1.warn('Store', `Store.createComposed('${name}') : a store with this name already exists.`);
|
|
4563
|
+
throw new NativeDocumentError(
|
|
4564
|
+
`Store.createComposed('${name}') : a store with this name already exists.`
|
|
4565
|
+
);
|
|
4566
|
+
}
|
|
4567
|
+
if (typeof computation !== 'function') {
|
|
4568
|
+
throw new NativeDocumentError(
|
|
4569
|
+
`Store.createComposed('${name}') : computation must be a function.`
|
|
4570
|
+
);
|
|
4571
|
+
}
|
|
4572
|
+
if (!Array.isArray(dependencies) || dependencies.length === 0) {
|
|
4573
|
+
throw new NativeDocumentError(
|
|
4574
|
+
`Store.createComposed('${name}') : dependencies must be a non-empty array of store names.`
|
|
4575
|
+
);
|
|
4576
|
+
}
|
|
4449
4577
|
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4578
|
+
// Resolve dependency observers
|
|
4579
|
+
const depObservers = dependencies.map(depName => {
|
|
4580
|
+
if(typeof depName !== 'string') {
|
|
4581
|
+
return depName;
|
|
4582
|
+
}
|
|
4583
|
+
const depItem = $stores.get(depName);
|
|
4584
|
+
if (!depItem) {
|
|
4585
|
+
DebugManager$1.error('Store', `Store.createComposed('${name}') : dependency '${depName}' not found. Create it first.`);
|
|
4586
|
+
throw new NativeDocumentError(
|
|
4587
|
+
`Store.createComposed('${name}') : dependency store '${depName}' not found.`
|
|
4588
|
+
);
|
|
4589
|
+
}
|
|
4590
|
+
return depItem.observer;
|
|
4455
4591
|
});
|
|
4456
|
-
}
|
|
4457
|
-
}
|
|
4458
|
-
|
|
4459
|
-
};
|
|
4460
4592
|
|
|
4461
|
-
|
|
4593
|
+
// Create computed observable from dependency observers
|
|
4594
|
+
const observer = Observable.computed(computation, depObservers);
|
|
4462
4595
|
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
},
|
|
4467
|
-
set(value) {
|
|
4468
|
-
this.set(value);
|
|
4469
|
-
}
|
|
4470
|
-
});
|
|
4596
|
+
$stores.set(name, { observer, subscribers: new Set(), resettable: false, composed: true });
|
|
4597
|
+
return observer;
|
|
4598
|
+
},
|
|
4471
4599
|
|
|
4472
|
-
|
|
4473
|
-
|
|
4600
|
+
/**
|
|
4601
|
+
* Returns true if a store with the given name exists.
|
|
4602
|
+
*
|
|
4603
|
+
* @param {string} name
|
|
4604
|
+
* @returns {boolean}
|
|
4605
|
+
*/
|
|
4606
|
+
has(name) {
|
|
4607
|
+
return $stores.has(name);
|
|
4608
|
+
},
|
|
4474
4609
|
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
|
|
4482
|
-
|
|
4483
|
-
|
|
4484
|
-
|
|
4485
|
-
|
|
4486
|
-
|
|
4487
|
-
|
|
4488
|
-
return Observable(item, configs);
|
|
4489
|
-
});
|
|
4490
|
-
this.$observables[key] = Observable.array(mappedItemValue, configs);
|
|
4491
|
-
continue;
|
|
4610
|
+
/**
|
|
4611
|
+
* Resets a resettable store to its initial value and notifies all subscribers.
|
|
4612
|
+
* Throws if the store was not created with createResettable().
|
|
4613
|
+
*
|
|
4614
|
+
* @param {string} name
|
|
4615
|
+
*/
|
|
4616
|
+
reset(name) {
|
|
4617
|
+
const item = $getStoreOrThrow('reset', name);
|
|
4618
|
+
if (item.composed) {
|
|
4619
|
+
DebugManager$1.error('Store', `Store.reset('${name}') : composed stores cannot be reset. Their value is derived from dependencies.`);
|
|
4620
|
+
throw new NativeDocumentError(
|
|
4621
|
+
`Store.reset('${name}') : composed stores cannot be reset.`
|
|
4622
|
+
);
|
|
4492
4623
|
}
|
|
4493
|
-
|
|
4494
|
-
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4500
|
-
|
|
4501
|
-
}
|
|
4502
|
-
};
|
|
4624
|
+
if (!item.resettable) {
|
|
4625
|
+
DebugManager$1.error('Store', `Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`);
|
|
4626
|
+
throw new NativeDocumentError(
|
|
4627
|
+
`Store.reset('${name}') : this store is not resettable. Use Store.createResettable('${name}', value) instead of Store.create().`
|
|
4628
|
+
);
|
|
4629
|
+
}
|
|
4630
|
+
item.observer.reset();
|
|
4631
|
+
},
|
|
4503
4632
|
|
|
4504
|
-
|
|
4505
|
-
|
|
4506
|
-
|
|
4507
|
-
|
|
4508
|
-
|
|
4509
|
-
|
|
4510
|
-
|
|
4511
|
-
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4516
|
-
|
|
4517
|
-
|
|
4518
|
-
|
|
4519
|
-
|
|
4633
|
+
/**
|
|
4634
|
+
* Returns a two-way synchronized follower of the store.
|
|
4635
|
+
* Writing to the follower propagates the value back to the store and all its subscribers.
|
|
4636
|
+
* Throws if called on a composed store — use Store.follow() instead.
|
|
4637
|
+
* Call follower.destroy() or follower.dispose() to unsubscribe.
|
|
4638
|
+
*
|
|
4639
|
+
* @param {string} name
|
|
4640
|
+
* @returns {ObservableItem}
|
|
4641
|
+
*/
|
|
4642
|
+
use(name) {
|
|
4643
|
+
const item = $getStoreOrThrow('use', name);
|
|
4644
|
+
|
|
4645
|
+
if (item.composed) {
|
|
4646
|
+
DebugManager$1.error('Store', `Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`);
|
|
4647
|
+
throw new NativeDocumentError(
|
|
4648
|
+
`Store.use('${name}') : composed stores are read-only. Use Store.follow('${name}') instead.`
|
|
4649
|
+
);
|
|
4520
4650
|
}
|
|
4521
|
-
result[key] = value;
|
|
4522
|
-
} else if(Validator.isProxy(dataItem)) {
|
|
4523
|
-
result[key] = dataItem.$value;
|
|
4524
|
-
} else {
|
|
4525
|
-
result[key] = dataItem;
|
|
4526
|
-
}
|
|
4527
|
-
}
|
|
4528
|
-
return result;
|
|
4529
|
-
};
|
|
4530
|
-
ObservableObject.prototype.$val = ObservableObject.prototype.val;
|
|
4531
4651
|
|
|
4532
|
-
|
|
4533
|
-
|
|
4534
|
-
if(Validator.isObservable(item)) {
|
|
4535
|
-
return item.val();
|
|
4536
|
-
}
|
|
4537
|
-
if(Validator.isProxy(item)) {
|
|
4538
|
-
return item.$value;
|
|
4539
|
-
}
|
|
4540
|
-
return item;
|
|
4541
|
-
};
|
|
4542
|
-
ObservableObject.prototype.$get = ObservableObject.prototype.get;
|
|
4652
|
+
const { observer: originalObserver, subscribers } = item;
|
|
4653
|
+
const observerFollower = $createObservable(originalObserver.val());
|
|
4543
4654
|
|
|
4544
|
-
|
|
4545
|
-
|
|
4546
|
-
const configs = this.configs;
|
|
4655
|
+
const onStoreChange = value => observerFollower.set(value);
|
|
4656
|
+
const onFollowerChange = value => originalObserver.set(value);
|
|
4547
4657
|
|
|
4548
|
-
|
|
4549
|
-
|
|
4550
|
-
const newValueOrigin = newData[key];
|
|
4551
|
-
const newValue = data[key];
|
|
4658
|
+
originalObserver.subscribe(onStoreChange);
|
|
4659
|
+
observerFollower.subscribe(onFollowerChange);
|
|
4552
4660
|
|
|
4553
|
-
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
const newValues = newValue.map(item => {
|
|
4561
|
-
if(Validator.isProxy(firstElementFromOriginalValue)) {
|
|
4562
|
-
return Observable.init(item, configs);
|
|
4563
|
-
}
|
|
4564
|
-
return Observable(item, configs);
|
|
4565
|
-
});
|
|
4566
|
-
targetItem.set(newValues);
|
|
4567
|
-
continue;
|
|
4568
|
-
}
|
|
4569
|
-
targetItem.set([...newValue]);
|
|
4570
|
-
continue;
|
|
4571
|
-
}
|
|
4572
|
-
if(Validator.isProxy(targetItem)) {
|
|
4573
|
-
targetItem.update(newValue);
|
|
4574
|
-
continue;
|
|
4575
|
-
}
|
|
4576
|
-
this[key] = newValue;
|
|
4577
|
-
}
|
|
4578
|
-
};
|
|
4579
|
-
ObservableObject.prototype.$set = ObservableObject.prototype.set;
|
|
4580
|
-
ObservableObject.prototype.$updateWith = ObservableObject.prototype.set;
|
|
4661
|
+
observerFollower.destroy = () => {
|
|
4662
|
+
originalObserver.unsubscribe(onStoreChange);
|
|
4663
|
+
observerFollower.unsubscribe(onFollowerChange);
|
|
4664
|
+
subscribers.delete(observerFollower);
|
|
4665
|
+
observerFollower.cleanup();
|
|
4666
|
+
};
|
|
4667
|
+
observerFollower.dispose = observerFollower.destroy;
|
|
4581
4668
|
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
ObservableObject.prototype.$observables = ObservableObject.prototype.observables;
|
|
4669
|
+
subscribers.add(observerFollower);
|
|
4670
|
+
return observerFollower;
|
|
4671
|
+
},
|
|
4586
4672
|
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
|
|
4596
|
-
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
};
|
|
4600
|
-
ObservableObject.prototype.originalSubscribe = ObservableObject.prototype.subscribe;
|
|
4601
|
-
ObservableObject.prototype.subscribe = function(callback) {
|
|
4602
|
-
const observables = this.observables();
|
|
4603
|
-
const updatedValue = nextTick(() => this.trigger());
|
|
4673
|
+
/**
|
|
4674
|
+
* Returns a read-only follower of the store.
|
|
4675
|
+
* The follower reflects store changes but cannot write back to the store.
|
|
4676
|
+
* Any attempt to call .set(), .toggle() or .reset() will throw.
|
|
4677
|
+
* Call follower.destroy() or follower.dispose() to unsubscribe.
|
|
4678
|
+
*
|
|
4679
|
+
* @param {string} name
|
|
4680
|
+
* @returns {ObservableItem}
|
|
4681
|
+
*/
|
|
4682
|
+
follow(name) {
|
|
4683
|
+
const { observer: originalObserver, subscribers } = $getStoreOrThrow('follow', name);
|
|
4684
|
+
const observerFollower = $createObservable(originalObserver.val());
|
|
4604
4685
|
|
|
4605
|
-
|
|
4686
|
+
const onStoreChange = value => observerFollower.set(value);
|
|
4687
|
+
originalObserver.subscribe(onStoreChange);
|
|
4606
4688
|
|
|
4607
|
-
|
|
4608
|
-
const observable = observables[i];
|
|
4609
|
-
if (observable.__$isObservableArray) {
|
|
4610
|
-
observable.deepSubscribe(updatedValue);
|
|
4611
|
-
continue
|
|
4612
|
-
}
|
|
4613
|
-
observable.subscribe(updatedValue);
|
|
4614
|
-
}
|
|
4615
|
-
};
|
|
4616
|
-
ObservableObject.prototype.configs = function() {
|
|
4617
|
-
return this.configs;
|
|
4618
|
-
};
|
|
4689
|
+
$applyReadOnly(observerFollower, name, 'follow');
|
|
4619
4690
|
|
|
4620
|
-
|
|
4691
|
+
observerFollower.destroy = () => {
|
|
4692
|
+
originalObserver.unsubscribe(onStoreChange);
|
|
4693
|
+
subscribers.delete(observerFollower);
|
|
4694
|
+
observerFollower.cleanup();
|
|
4695
|
+
};
|
|
4696
|
+
observerFollower.dispose = observerFollower.destroy;
|
|
4621
4697
|
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
|
|
4698
|
+
subscribers.add(observerFollower);
|
|
4699
|
+
return observerFollower;
|
|
4700
|
+
},
|
|
4625
4701
|
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4702
|
+
/**
|
|
4703
|
+
* Returns the raw store observer directly (no follower, no cleanup contract).
|
|
4704
|
+
* Use this for direct read access when you don't need to unsubscribe.
|
|
4705
|
+
* WARNING : mutations on this observer impact all subscribers immediately.
|
|
4706
|
+
*
|
|
4707
|
+
* @param {string} name
|
|
4708
|
+
* @returns {ObservableItem|null}
|
|
4709
|
+
*/
|
|
4710
|
+
get(name) {
|
|
4711
|
+
const item = $stores.get(name);
|
|
4712
|
+
if (!item) {
|
|
4713
|
+
DebugManager$1.warn('Store', `Store.get('${name}') : store not found.`);
|
|
4714
|
+
return null;
|
|
4715
|
+
}
|
|
4716
|
+
return item.observer;
|
|
4717
|
+
},
|
|
4634
4718
|
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
return data.val();
|
|
4643
|
-
}
|
|
4644
|
-
if(Validator.isProxy(data)) {
|
|
4645
|
-
return data.$value;
|
|
4646
|
-
}
|
|
4647
|
-
if(Validator.isArray(data)) {
|
|
4648
|
-
const result = [];
|
|
4649
|
-
for(let i = 0, length = data.length; i < length; i++) {
|
|
4650
|
-
const item = data[i];
|
|
4651
|
-
result.push(Observable.value(item));
|
|
4652
|
-
}
|
|
4653
|
-
return result;
|
|
4654
|
-
}
|
|
4655
|
-
return data;
|
|
4656
|
-
};
|
|
4719
|
+
/**
|
|
4720
|
+
* @param {string} name
|
|
4721
|
+
* @returns {{ observer: ObservableItem, subscribers: Set } | null}
|
|
4722
|
+
*/
|
|
4723
|
+
getWithSubscribers(name) {
|
|
4724
|
+
return $stores.get(name) ?? null;
|
|
4725
|
+
},
|
|
4657
4726
|
|
|
4658
|
-
|
|
4659
|
-
|
|
4727
|
+
/**
|
|
4728
|
+
* Destroys a store : cleans up the observer, destroys all followers, and removes the entry.
|
|
4729
|
+
*
|
|
4730
|
+
* @param {string} name
|
|
4731
|
+
*/
|
|
4732
|
+
delete(name) {
|
|
4733
|
+
const item = $stores.get(name);
|
|
4734
|
+
if (!item) {
|
|
4735
|
+
DebugManager$1.warn('Store', `Store.delete('${name}') : store not found, nothing to delete.`);
|
|
4736
|
+
return;
|
|
4737
|
+
}
|
|
4738
|
+
item.subscribers.forEach(follower => follower.destroy());
|
|
4739
|
+
item.subscribers.clear();
|
|
4740
|
+
item.observer.cleanup();
|
|
4741
|
+
$stores.delete(name);
|
|
4742
|
+
},
|
|
4743
|
+
/**
|
|
4744
|
+
* Creates an isolated store group with its own state namespace.
|
|
4745
|
+
* Each group is a fully independent StoreFactory instance —
|
|
4746
|
+
* no key conflicts, no shared state with the parent store.
|
|
4747
|
+
*
|
|
4748
|
+
* @param {string | ((group: ReturnType<typeof StoreFactory>) => void)} name - Group name for debugging, or setup callback if no name is provided
|
|
4749
|
+
* @param {((group: ReturnType<typeof StoreFactory>) => void)} [callback] - Setup function receiving the isolated store instance
|
|
4750
|
+
* @returns {ReturnType<typeof StoreFactory>}
|
|
4751
|
+
*
|
|
4752
|
+
* @example
|
|
4753
|
+
* // With name (recommended)
|
|
4754
|
+
* const EventStore = Store.group('events', (group) => {
|
|
4755
|
+
* group.create('catalog', []);
|
|
4756
|
+
* group.create('filters', { category: null, date: null });
|
|
4757
|
+
* group.createResettable('selected', null);
|
|
4758
|
+
* group.createComposed('filtered', () => {
|
|
4759
|
+
* const catalog = EventStore.get('catalog').val();
|
|
4760
|
+
* const filters = EventStore.get('filters').val();
|
|
4761
|
+
* return catalog.filter(event => {
|
|
4762
|
+
* if (filters.category && event.category !== filters.category) return false;
|
|
4763
|
+
* return true;
|
|
4764
|
+
* });
|
|
4765
|
+
* }, ['catalog', 'filters']);
|
|
4766
|
+
* });
|
|
4767
|
+
*
|
|
4768
|
+
* // Without name
|
|
4769
|
+
* const CartStore = Store.group((group) => {
|
|
4770
|
+
* group.create('items', []);
|
|
4771
|
+
* });
|
|
4772
|
+
*
|
|
4773
|
+
* // Usage
|
|
4774
|
+
* EventStore.use('catalog'); // two-way follower
|
|
4775
|
+
* EventStore.follow('filtered'); // read-only follower
|
|
4776
|
+
* EventStore.get('filters'); // raw observable
|
|
4777
|
+
*
|
|
4778
|
+
* // Cross-group composed
|
|
4779
|
+
* const OrderStore = Store.group('orders', (group) => {
|
|
4780
|
+
* group.createComposed('summary', () => {
|
|
4781
|
+
* const items = CartStore.get('items').val();
|
|
4782
|
+
* const events = EventStore.get('catalog').val();
|
|
4783
|
+
* return { items, events };
|
|
4784
|
+
* }, [CartStore.get('items'), EventStore.get('catalog')]);
|
|
4785
|
+
* });
|
|
4786
|
+
*/
|
|
4787
|
+
group(name, callback) {
|
|
4788
|
+
if (typeof name === 'function') {
|
|
4789
|
+
callback = name;
|
|
4790
|
+
name = 'anonymous';
|
|
4791
|
+
}
|
|
4792
|
+
const store = StoreFactory();
|
|
4793
|
+
callback && callback(store);
|
|
4794
|
+
return store;
|
|
4795
|
+
},
|
|
4796
|
+
createPersistent(name, value, localstorage_key) {
|
|
4797
|
+
localstorage_key = localstorage_key || name;
|
|
4798
|
+
const observer = this.create(name, $getFromStorage(localstorage_key, value));
|
|
4799
|
+
const saver = $saveToStorage(value);
|
|
4660
4800
|
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
|
|
4664
|
-
|
|
4665
|
-
|
|
4666
|
-
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
* const firstName = Observable('John');
|
|
4670
|
-
* const lastName = Observable('Doe');
|
|
4671
|
-
* const fullName = Observable.computed(
|
|
4672
|
-
* () => `${firstName.val()} ${lastName.val()}`,
|
|
4673
|
-
* [firstName, lastName]
|
|
4674
|
-
* );
|
|
4675
|
-
*
|
|
4676
|
-
* // With batch function
|
|
4677
|
-
* const batch = Observable.batch(() => { ... });
|
|
4678
|
-
* const computed = Observable.computed(() => { ... }, batch);
|
|
4679
|
-
*/
|
|
4680
|
-
Observable.computed = function(callback, dependencies = []) {
|
|
4681
|
-
const initialValue = callback();
|
|
4682
|
-
const observable = new ObservableItem(initialValue);
|
|
4683
|
-
const updatedValue = nextTick(() => observable.set(callback()));
|
|
4684
|
-
{
|
|
4685
|
-
PluginsManager.emit('CreateObservableComputed', observable, dependencies);
|
|
4686
|
-
}
|
|
4801
|
+
observer.subscribe((val) => saver(localstorage_key, val));
|
|
4802
|
+
return observer;
|
|
4803
|
+
},
|
|
4804
|
+
createPersistentResettable(name, value, localstorage_key) {
|
|
4805
|
+
localstorage_key = localstorage_key || name;
|
|
4806
|
+
const observer = this.createResettable(name, $getFromStorage(localstorage_key, value));
|
|
4807
|
+
const saver = $saveToStorage(value);
|
|
4808
|
+
observer.subscribe((val) => saver(localstorage_key, val));
|
|
4687
4809
|
|
|
4688
|
-
|
|
4689
|
-
|
|
4690
|
-
|
|
4810
|
+
const originalReset = observer.reset.bind(observer);
|
|
4811
|
+
observer.reset = () => {
|
|
4812
|
+
LocalStorage.remove(localstorage_key);
|
|
4813
|
+
originalReset();
|
|
4814
|
+
};
|
|
4815
|
+
|
|
4816
|
+
return observer;
|
|
4691
4817
|
}
|
|
4692
|
-
|
|
4693
|
-
return observable;
|
|
4694
|
-
}
|
|
4818
|
+
};
|
|
4695
4819
|
|
|
4696
|
-
|
|
4697
|
-
|
|
4698
|
-
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4820
|
+
|
|
4821
|
+
return new Proxy($api, {
|
|
4822
|
+
get(target, prop) {
|
|
4823
|
+
if (typeof prop === 'symbol' || prop.startsWith('$') || prop in target) {
|
|
4824
|
+
return target[prop];
|
|
4825
|
+
}
|
|
4826
|
+
if (target.has(prop)) {
|
|
4827
|
+
if ($followersCache.has(prop)) {
|
|
4828
|
+
return $followersCache.get(prop);
|
|
4829
|
+
}
|
|
4830
|
+
const follower = target.follow(prop);
|
|
4831
|
+
$followersCache.set(prop, follower);
|
|
4832
|
+
return follower;
|
|
4833
|
+
}
|
|
4834
|
+
return undefined;
|
|
4835
|
+
},
|
|
4836
|
+
set(target, prop, value) {
|
|
4837
|
+
DebugManager$1.error('Store', `Forbidden: You cannot overwrite the store key '${String(prop)}'. Use .use('${String(prop)}').set(value) instead.`);
|
|
4838
|
+
throw new NativeDocumentError(`Store structure is immutable. Use .set() on the observable.`);
|
|
4839
|
+
},
|
|
4840
|
+
deleteProperty(target, prop) {
|
|
4841
|
+
throw new NativeDocumentError(`Store keys cannot be deleted.`);
|
|
4702
4842
|
}
|
|
4703
|
-
dependency.subscribe(updatedValue);
|
|
4704
4843
|
});
|
|
4705
|
-
|
|
4706
|
-
return observable;
|
|
4707
4844
|
};
|
|
4708
4845
|
|
|
4846
|
+
const Store = StoreFactory();
|
|
4847
|
+
|
|
4848
|
+
Store.create('locale', navigator.language.split('-')[0] || 'en');
|
|
4849
|
+
|
|
4709
4850
|
/**
|
|
4710
4851
|
* Renders a list of items from an observable array or object, automatically updating when data changes.
|
|
4711
4852
|
* Efficiently manages DOM updates by tracking items with keys.
|
|
@@ -4781,7 +4922,7 @@ var NativeDocument = (function (exports) {
|
|
|
4781
4922
|
}
|
|
4782
4923
|
cache.set(keyId, { keyId, isNew: true, child: new WeakRef(child), indexObserver});
|
|
4783
4924
|
} catch (e) {
|
|
4784
|
-
DebugManager.error('ForEach', `Error creating element for key ${keyId}` , e);
|
|
4925
|
+
DebugManager$1.error('ForEach', `Error creating element for key ${keyId}` , e);
|
|
4785
4926
|
throw e;
|
|
4786
4927
|
}
|
|
4787
4928
|
return keyId;
|
|
@@ -4972,21 +5113,23 @@ var NativeDocument = (function (exports) {
|
|
|
4972
5113
|
};
|
|
4973
5114
|
|
|
4974
5115
|
|
|
4975
|
-
|
|
4976
|
-
|
|
4977
|
-
|
|
4978
|
-
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
5116
|
+
let cleanCache;
|
|
5117
|
+
|
|
5118
|
+
if(!isIndexRequired) {
|
|
5119
|
+
cleanCache = cache.clear.bind(cache);
|
|
5120
|
+
}
|
|
5121
|
+
else if(configs.shouldKeepItemsInCache) {
|
|
5122
|
+
cleanCache = () => {};
|
|
5123
|
+
} else {
|
|
5124
|
+
cleanCache = (items) => {
|
|
5125
|
+
for (const [itemAsKey, _] of cache.entries()) {
|
|
5126
|
+
if(items && items.includes(itemAsKey)) {
|
|
5127
|
+
continue;
|
|
5128
|
+
}
|
|
5129
|
+
removeCacheItem(itemAsKey, false);
|
|
4986
5130
|
}
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
};
|
|
5131
|
+
};
|
|
5132
|
+
}
|
|
4990
5133
|
|
|
4991
5134
|
const removeByItem = (item, fragment) => {
|
|
4992
5135
|
const cacheItem = cache.get(item);
|
|
@@ -5006,7 +5149,7 @@ var NativeDocument = (function (exports) {
|
|
|
5006
5149
|
};
|
|
5007
5150
|
|
|
5008
5151
|
const Actions = {
|
|
5009
|
-
toFragment(items){
|
|
5152
|
+
toFragment: (items) =>{
|
|
5010
5153
|
const fragment = document.createDocumentFragment();
|
|
5011
5154
|
for(let i = 0, length = items.length; i < length; i++) {
|
|
5012
5155
|
fragment.appendChild(buildItem(items[i], lastNumberOfItems));
|
|
@@ -5014,14 +5157,14 @@ var NativeDocument = (function (exports) {
|
|
|
5014
5157
|
}
|
|
5015
5158
|
return fragment;
|
|
5016
5159
|
},
|
|
5017
|
-
add(items) {
|
|
5160
|
+
add: (items) => {
|
|
5018
5161
|
element.appendElement(Actions.toFragment(items));
|
|
5019
5162
|
},
|
|
5020
|
-
replace(items) {
|
|
5163
|
+
replace: (items) => {
|
|
5021
5164
|
clear(items);
|
|
5022
5165
|
Actions.add(items);
|
|
5023
5166
|
},
|
|
5024
|
-
reOrder(items) {
|
|
5167
|
+
reOrder: (items) => {
|
|
5025
5168
|
let child = null;
|
|
5026
5169
|
const fragment = document.createDocumentFragment();
|
|
5027
5170
|
for(const item of items) {
|
|
@@ -5033,14 +5176,14 @@ var NativeDocument = (function (exports) {
|
|
|
5033
5176
|
child = null;
|
|
5034
5177
|
element.appendElement(fragment, blockEnd);
|
|
5035
5178
|
},
|
|
5036
|
-
removeOne(element, index) {
|
|
5179
|
+
removeOne: (element, index) => {
|
|
5037
5180
|
removeCacheItem(element, true);
|
|
5038
5181
|
},
|
|
5039
5182
|
clear,
|
|
5040
|
-
merge(items) {
|
|
5183
|
+
merge: (items) => {
|
|
5041
5184
|
Actions.add(items);
|
|
5042
5185
|
},
|
|
5043
|
-
push(items) {
|
|
5186
|
+
push: (items) => {
|
|
5044
5187
|
let delay = 0;
|
|
5045
5188
|
if(configs.pushDelay) {
|
|
5046
5189
|
delay = configs.pushDelay(items) ?? 0;
|
|
@@ -5048,7 +5191,7 @@ var NativeDocument = (function (exports) {
|
|
|
5048
5191
|
|
|
5049
5192
|
Actions.add(items, delay);
|
|
5050
5193
|
},
|
|
5051
|
-
populate([target, iteration, callback]) {
|
|
5194
|
+
populate: ([target, iteration, callback]) => {
|
|
5052
5195
|
const fragment = document.createDocumentFragment();
|
|
5053
5196
|
for (let i = 0; i < iteration; i++) {
|
|
5054
5197
|
const data = callback(i);
|
|
@@ -5059,10 +5202,10 @@ var NativeDocument = (function (exports) {
|
|
|
5059
5202
|
element.appendChild(fragment);
|
|
5060
5203
|
fragment.replaceChildren();
|
|
5061
5204
|
},
|
|
5062
|
-
unshift(values){
|
|
5205
|
+
unshift: (values) => {
|
|
5063
5206
|
element.insertBefore(Actions.toFragment(values), blockStart.nextSibling);
|
|
5064
5207
|
},
|
|
5065
|
-
splice(args, deleted) {
|
|
5208
|
+
splice: (args, deleted) => {
|
|
5066
5209
|
const [start, deleteCount, ...values] = args;
|
|
5067
5210
|
let elementBeforeFirst = null;
|
|
5068
5211
|
const garbageFragment = document.createDocumentFragment();
|
|
@@ -5089,22 +5232,22 @@ var NativeDocument = (function (exports) {
|
|
|
5089
5232
|
}
|
|
5090
5233
|
|
|
5091
5234
|
},
|
|
5092
|
-
reverse(_, reversed) {
|
|
5235
|
+
reverse: (_, reversed) => {
|
|
5093
5236
|
Actions.reOrder(reversed);
|
|
5094
5237
|
},
|
|
5095
|
-
sort(_, sorted) {
|
|
5238
|
+
sort: (_, sorted) => {
|
|
5096
5239
|
Actions.reOrder(sorted);
|
|
5097
5240
|
},
|
|
5098
|
-
remove(_, deleted) {
|
|
5241
|
+
remove: (_, deleted)=> {
|
|
5099
5242
|
Actions.removeOne(deleted);
|
|
5100
5243
|
},
|
|
5101
|
-
pop(_, deleted) {
|
|
5244
|
+
pop: (_, deleted) => {
|
|
5102
5245
|
Actions.removeOne(deleted);
|
|
5103
5246
|
},
|
|
5104
|
-
shift(_, deleted) {
|
|
5247
|
+
shift: (_, deleted) => {
|
|
5105
5248
|
Actions.removeOne(deleted);
|
|
5106
5249
|
},
|
|
5107
|
-
swap(args, elements) {
|
|
5250
|
+
swap: (args, elements) => {
|
|
5108
5251
|
const parent = blockEnd.parentNode;
|
|
5109
5252
|
|
|
5110
5253
|
let childA = getItemChild(elements[0]);
|
|
@@ -5171,7 +5314,7 @@ var NativeDocument = (function (exports) {
|
|
|
5171
5314
|
*/
|
|
5172
5315
|
const ShowIf = function(condition, child, { comment = null, shouldKeepInCache = true} = {}) {
|
|
5173
5316
|
if(!(Validator.isObservable(condition)) && !Validator.isObservableWhenResult(condition)) {
|
|
5174
|
-
return DebugManager.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
|
|
5317
|
+
return DebugManager$1.warn('ShowIf', "ShowIf : condition must be an Observable / "+comment, condition);
|
|
5175
5318
|
}
|
|
5176
5319
|
const element = Anchor('Show if : '+(comment || ''));
|
|
5177
5320
|
|
|
@@ -6590,7 +6733,7 @@ var NativeDocument = (function (exports) {
|
|
|
6590
6733
|
window.history.pushState({ name: route.name(), params, path}, route.name() || path , path);
|
|
6591
6734
|
this.handleRouteChange(route, params, query, path);
|
|
6592
6735
|
} catch (e) {
|
|
6593
|
-
DebugManager.error('HistoryRouter', 'Error in pushState', e);
|
|
6736
|
+
DebugManager$1.error('HistoryRouter', 'Error in pushState', e);
|
|
6594
6737
|
}
|
|
6595
6738
|
};
|
|
6596
6739
|
/**
|
|
@@ -6603,7 +6746,7 @@ var NativeDocument = (function (exports) {
|
|
|
6603
6746
|
window.history.replaceState({ name: route.name(), params, path}, route.name() || path , path);
|
|
6604
6747
|
this.handleRouteChange(route, params, {}, path);
|
|
6605
6748
|
} catch(e) {
|
|
6606
|
-
DebugManager.error('HistoryRouter', 'Error in replaceState', e);
|
|
6749
|
+
DebugManager$1.error('HistoryRouter', 'Error in replaceState', e);
|
|
6607
6750
|
}
|
|
6608
6751
|
};
|
|
6609
6752
|
this.forward = function() {
|
|
@@ -6630,7 +6773,7 @@ var NativeDocument = (function (exports) {
|
|
|
6630
6773
|
}
|
|
6631
6774
|
this.handleRouteChange(route, params, query, path);
|
|
6632
6775
|
} catch(e) {
|
|
6633
|
-
DebugManager.error('HistoryRouter', 'Error in popstate event', e);
|
|
6776
|
+
DebugManager$1.error('HistoryRouter', 'Error in popstate event', e);
|
|
6634
6777
|
}
|
|
6635
6778
|
});
|
|
6636
6779
|
const { route, params, query, path } = this.resolve(defaultPath || (window.location.pathname+window.location.search));
|
|
@@ -6855,7 +6998,7 @@ var NativeDocument = (function (exports) {
|
|
|
6855
6998
|
listener(request);
|
|
6856
6999
|
next && next(request);
|
|
6857
7000
|
} catch (e) {
|
|
6858
|
-
DebugManager.warn('Route Listener', 'Error in listener:', e);
|
|
7001
|
+
DebugManager$1.warn('Route Listener', 'Error in listener:', e);
|
|
6859
7002
|
}
|
|
6860
7003
|
}
|
|
6861
7004
|
};
|
|
@@ -7033,7 +7176,7 @@ var NativeDocument = (function (exports) {
|
|
|
7033
7176
|
*/
|
|
7034
7177
|
Router.create = function(options, callback) {
|
|
7035
7178
|
if(!Validator.isFunction(callback)) {
|
|
7036
|
-
DebugManager.error('Router', 'Callback must be a function');
|
|
7179
|
+
DebugManager$1.error('Router', 'Callback must be a function');
|
|
7037
7180
|
throw new RouterError('Callback must be a function');
|
|
7038
7181
|
}
|
|
7039
7182
|
const router = new Router(options);
|