@tko/observable 4.0.0-alpha7.3 → 4.0.0-beta1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,915 +1,125 @@
1
- /*!
2
- * TKO subscribables and observables 🥊 @tko/observable@4.0.0-alpha7.3
3
- * (c) The Knockout.js Team - https://tko.io
4
- * License: MIT (http://www.opensource.org/licenses/mit-license.php)
5
- */
6
-
7
- import { tasks, removeDisposeCallback, addDisposeCallback, options, objectForEach, throttle, debounce, arrayRemoveItem, overwriteLengthPropertyIfSupported, extend, compareArrays, findMovesInArrayComparison, arrayIndexOf, arrayForEach } from '@tko/utils';
8
-
9
- /*! *****************************************************************************
10
- Copyright (c) Microsoft Corporation. All rights reserved.
11
- Licensed under the Apache License, Version 2.0 (the "License"); you may not use
12
- this file except in compliance with the License. You may obtain a copy of the
13
- License at http://www.apache.org/licenses/LICENSE-2.0
14
-
15
- THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
- KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
17
- WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
18
- MERCHANTABLITY OR NON-INFRINGEMENT.
19
-
20
- See the Apache Version 2.0 License for specific language governing permissions
21
- and limitations under the License.
22
- ***************************************************************************** */
23
-
24
- function __generator(thisArg, body) {
25
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
26
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
27
- function verb(n) { return function (v) { return step([n, v]); }; }
28
- function step(op) {
29
- if (f) throw new TypeError("Generator is already executing.");
30
- while (_) try {
31
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
32
- if (y = 0, t) op = [op[0] & 2, t.value];
33
- switch (op[0]) {
34
- case 0: case 1: t = op; break;
35
- case 4: _.label++; return { value: op[1], done: false };
36
- case 5: _.label++; y = op[1]; op = [0]; continue;
37
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
38
- default:
39
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
40
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
41
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
42
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
43
- if (t[2]) _.ops.pop();
44
- _.trys.pop(); continue;
45
- }
46
- op = body.call(thisArg, _);
47
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
48
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
49
- }
50
- }
51
-
52
- function __values(o) {
53
- var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0;
54
- if (m) return m.call(o);
55
- return {
56
- next: function () {
57
- if (o && i >= o.length) o = void 0;
58
- return { value: o && o[i++], done: !o };
59
- }
60
- };
61
- }
62
-
63
- function __read(o, n) {
64
- var m = typeof Symbol === "function" && o[Symbol.iterator];
65
- if (!m) return o;
66
- var i = m.call(o), r, ar = [], e;
67
- try {
68
- while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
69
- }
70
- catch (error) { e = { error: error }; }
71
- finally {
72
- try {
73
- if (r && !r.done && (m = i["return"])) m.call(i);
74
- }
75
- finally { if (e) throw e.error; }
76
- }
77
- return ar;
78
- }
79
-
80
- function __spread() {
81
- for (var ar = [], i = 0; i < arguments.length; i++)
82
- ar = ar.concat(__read(arguments[i]));
83
- return ar;
1
+ // @tko/observable 🥊 4.0.0-beta1.0 ESM
2
+ import {
3
+ options,
4
+ overwriteLengthPropertyIfSupported
5
+ } from "@tko/utils";
6
+ import * as dependencyDetection from "./dependencyDetection";
7
+ import { deferUpdates } from "./defer";
8
+ import { subscribable, defaultEvent, LATEST_VALUE } from "./subscribable";
9
+ import { valuesArePrimitiveAndEqual } from "./extenders";
10
+ export function observable(initialValue) {
11
+ function Observable() {
12
+ if (arguments.length > 0) {
13
+ if (Observable.isDifferent(Observable[LATEST_VALUE], arguments[0])) {
14
+ Observable.valueWillMutate();
15
+ Observable[LATEST_VALUE] = arguments[0];
16
+ Observable.valueHasMutated();
17
+ }
18
+ return this;
19
+ } else {
20
+ dependencyDetection.registerDependency(Observable);
21
+ return Observable[LATEST_VALUE];
22
+ }
23
+ }
24
+ overwriteLengthPropertyIfSupported(Observable, { value: void 0 });
25
+ Observable[LATEST_VALUE] = initialValue;
26
+ subscribable.fn.init(Observable);
27
+ Object.setPrototypeOf(Observable, observable.fn);
28
+ if (options.deferUpdates) {
29
+ deferUpdates(Observable);
30
+ }
31
+ return Observable;
84
32
  }
85
-
86
- /**
87
- * Create a subscribable symbol that's used to identify subscribables.
88
- */
89
- var SUBSCRIBABLE_SYM = Symbol('Knockout Subscribable');
90
- function isSubscribable(instance) {
91
- return (instance && instance[SUBSCRIBABLE_SYM]) || false;
33
+ observable.fn = {
34
+ equalityComparer: valuesArePrimitiveAndEqual,
35
+ peek() {
36
+ return this[LATEST_VALUE];
37
+ },
38
+ valueHasMutated() {
39
+ this.notifySubscribers(this[LATEST_VALUE], "spectate");
40
+ this.notifySubscribers(this[LATEST_VALUE]);
41
+ },
42
+ valueWillMutate() {
43
+ this.notifySubscribers(this[LATEST_VALUE], "beforeChange");
44
+ },
45
+ modify(fn, peek2 = true) {
46
+ return this(fn(peek2 ? this.peek() : this()));
47
+ },
48
+ isWriteable: true
49
+ };
50
+ function limitNotifySubscribers(value, event) {
51
+ if (!event || event === defaultEvent) {
52
+ this._limitChange(value);
53
+ } else if (event === "beforeChange") {
54
+ this._limitBeforeChange(value);
55
+ } else {
56
+ this._origNotifySubscribers(value, event);
57
+ }
92
58
  }
93
-
94
- //
95
- var outerFrames = [];
96
- var currentFrame;
97
- var lastId = 0;
98
- // Return a unique ID that can be assigned to an observable for dependency tracking.
99
- // Theoretically, you could eventually overflow the number storage size, resulting
100
- // in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
101
- // or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
102
- // take over 285 years to reach that number.
103
- // Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
104
- function getId() {
105
- return ++lastId;
106
- }
107
- function begin(options$$1) {
108
- outerFrames.push(currentFrame);
109
- currentFrame = options$$1;
110
- }
111
- function end() {
112
- currentFrame = outerFrames.pop();
113
- }
114
- function registerDependency(subscribable) {
115
- if (currentFrame) {
116
- if (!isSubscribable(subscribable)) {
117
- throw new Error('Only subscribable things can act as dependencies');
118
- }
119
- currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
120
- }
121
- }
122
- function ignore(callback, callbackTarget, callbackArgs) {
123
- try {
124
- begin();
125
- return callback.apply(callbackTarget, callbackArgs || []);
126
- }
127
- finally {
128
- end();
129
- }
130
- }
131
- function getDependenciesCount() {
132
- if (currentFrame) {
133
- return currentFrame.computed.getDependenciesCount();
134
- }
135
- }
136
- function getDependencies() {
137
- if (currentFrame) {
138
- return currentFrame.computed.getDependencies();
139
- }
140
- }
141
- function isInitial() {
142
- if (currentFrame) {
143
- return currentFrame.isInitial;
144
- }
59
+ subscribable.fn.limit = function limit(limitFunction) {
60
+ var self = this;
61
+ var selfIsObservable = isObservable(self);
62
+ var beforeChange = "beforeChange";
63
+ var ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate;
64
+ if (!self._origNotifySubscribers) {
65
+ self._origNotifySubscribers = self.notifySubscribers;
66
+ self.notifySubscribers = limitNotifySubscribers;
67
+ }
68
+ var finish = limitFunction(function() {
69
+ self._notificationIsPending = false;
70
+ if (selfIsObservable && pendingValue === self) {
71
+ pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
72
+ }
73
+ const shouldNotify = notifyNextChange || didUpdate && self.isDifferent(previousValue, pendingValue);
74
+ self._notifyNextChange = didUpdate = ignoreBeforeChange = false;
75
+ if (shouldNotify) {
76
+ self._origNotifySubscribers(previousValue = pendingValue);
77
+ }
78
+ });
79
+ Object.assign(self, {
80
+ _limitChange(value, isDirty) {
81
+ if (!isDirty || !self._notificationIsPending) {
82
+ didUpdate = !isDirty;
83
+ }
84
+ self._changeSubscriptions = [...self._subscriptions[defaultEvent]];
85
+ self._notificationIsPending = ignoreBeforeChange = true;
86
+ pendingValue = value;
87
+ finish();
88
+ },
89
+ _limitBeforeChange(value) {
90
+ if (!ignoreBeforeChange) {
91
+ previousValue = value;
92
+ self._origNotifySubscribers(value, beforeChange);
93
+ }
94
+ },
95
+ _notifyNextChangeIfValueIsDifferent() {
96
+ if (self.isDifferent(previousValue, self.peek(true))) {
97
+ notifyNextChange = true;
98
+ }
99
+ },
100
+ _recordUpdate() {
101
+ didUpdate = true;
102
+ }
103
+ });
104
+ };
105
+ Object.setPrototypeOf(observable.fn, subscribable.fn);
106
+ var protoProperty = observable.protoProperty = options.protoProperty;
107
+ observable.fn[protoProperty] = observable;
108
+ observable.observablePrototypes = /* @__PURE__ */ new Set([observable]);
109
+ export function isObservable(instance) {
110
+ const proto = typeof instance === "function" && instance[protoProperty];
111
+ if (proto && !observable.observablePrototypes.has(proto)) {
112
+ throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
113
+ }
114
+ return !!proto;
145
115
  }
146
-
147
- var dependencyDetection = /*#__PURE__*/Object.freeze({
148
- begin: begin,
149
- end: end,
150
- registerDependency: registerDependency,
151
- ignore: ignore,
152
- getDependenciesCount: getDependenciesCount,
153
- getDependencies: getDependencies,
154
- isInitial: isInitial,
155
- ignoreDependencies: ignore
156
- });
157
-
158
- //
159
- function deferUpdates(target) {
160
- if (target._deferUpdates) {
161
- return;
162
- }
163
- target._deferUpdates = true;
164
- target.limit(function (callback) {
165
- var handle;
166
- var ignoreUpdates = false;
167
- return function () {
168
- if (!ignoreUpdates) {
169
- tasks.cancel(handle);
170
- handle = tasks.schedule(callback);
171
- try {
172
- ignoreUpdates = true;
173
- target.notifySubscribers(undefined, 'dirty');
174
- }
175
- finally {
176
- ignoreUpdates = false;
177
- }
178
- }
179
- };
180
- });
116
+ export function unwrap(value) {
117
+ return isObservable(value) ? value() : value;
181
118
  }
182
-
183
- var Subscription = /** @class */ (function () {
184
- function Subscription(target, observer, disposeCallback) {
185
- this._target = target;
186
- this._callback = observer.next;
187
- this._disposeCallback = disposeCallback;
188
- this._isDisposed = false;
189
- this._domNodeDisposalCallback = null;
190
- }
191
- Subscription.prototype.dispose = function () {
192
- if (this._domNodeDisposalCallback) {
193
- removeDisposeCallback(this._node, this._domNodeDisposalCallback);
194
- }
195
- this._isDisposed = true;
196
- this._disposeCallback();
197
- };
198
- Subscription.prototype.disposeWhenNodeIsRemoved = function (node) {
199
- this._node = node;
200
- addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));
201
- };
202
- // TC39 Observable API
203
- Subscription.prototype.unsubscribe = function () { this.dispose(); };
204
- Object.defineProperty(Subscription.prototype, "closed", {
205
- get: function () { return this._isDisposed; },
206
- enumerable: true,
207
- configurable: true
208
- });
209
- return Subscription;
210
- }());
211
-
212
- //
213
- var primitiveTypes = {
214
- 'undefined': 1, 'boolean': 1, 'number': 1, 'string': 1
215
- };
216
- function valuesArePrimitiveAndEqual(a, b) {
217
- var oldValueIsPrimitive = (a === null) || (typeof (a) in primitiveTypes);
218
- return oldValueIsPrimitive ? (a === b) : false;
219
- }
220
- function applyExtenders(requestedExtenders) {
221
- var target = this;
222
- if (requestedExtenders) {
223
- objectForEach(requestedExtenders, function (key, value) {
224
- var extenderHandler = extenders[key];
225
- if (typeof extenderHandler === 'function') {
226
- target = extenderHandler(target, value) || target;
227
- }
228
- else {
229
- options.onError(new Error('Extender not found: ' + key));
230
- }
231
- });
232
- }
233
- return target;
234
- }
235
- /*
236
- --- DEFAULT EXTENDERS ---
237
- */
238
- // Change when notifications are published.
239
- function notify(target, notifyWhen) {
240
- target.equalityComparer = notifyWhen == 'always'
241
- ? null // null equalityComparer means to always notify
242
- : valuesArePrimitiveAndEqual;
243
- }
244
- function deferred(target, option) {
245
- if (option !== true) {
246
- throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.');
247
- }
248
- deferUpdates(target);
249
- }
250
- function rateLimit(target, options$$1) {
251
- var timeout, method, limitFunction;
252
- if (typeof options$$1 === 'number') {
253
- timeout = options$$1;
254
- }
255
- else {
256
- timeout = options$$1.timeout;
257
- method = options$$1.method;
258
- }
259
- // rateLimit supersedes deferred updates
260
- target._deferUpdates = false;
261
- limitFunction = method === 'notifyWhenChangesStop' ? debounce : throttle;
262
- target.limit(function (callback) {
263
- return limitFunction(callback, timeout);
264
- });
265
- }
266
- var extenders = {
267
- notify: notify,
268
- deferred: deferred,
269
- rateLimit: rateLimit
270
- };
271
-
272
- var _a;
273
- // Descendants may have a LATEST_VALUE, which if present
274
- // causes TC39 subscriptions to emit the latest value when
275
- // subscribed.
276
- var LATEST_VALUE = Symbol('Knockout latest value');
277
- function subscribable() {
278
- Object.setPrototypeOf(this, ko_subscribable_fn);
279
- ko_subscribable_fn.init(this);
280
- }
281
- var defaultEvent = 'change';
282
- var ko_subscribable_fn = (_a = {},
283
- _a[SUBSCRIBABLE_SYM] = true,
284
- _a[Symbol.observable] = function () { return this; },
285
- _a.init = function (instance) {
286
- instance._subscriptions = { change: [] };
287
- instance._versionNumber = 1;
288
- },
289
- _a.subscribe = function (callback, callbackTarget, event) {
290
- var _this = this;
291
- // TC39 proposed standard Observable { next: () => ... }
292
- var isTC39Callback = typeof callback === 'object' && callback.next;
293
- event = event || defaultEvent;
294
- var observer = isTC39Callback ? callback : {
295
- next: callbackTarget ? callback.bind(callbackTarget) : callback
296
- };
297
- var subscriptionInstance = new Subscription(this, observer, function () {
298
- arrayRemoveItem(_this._subscriptions[event], subscriptionInstance);
299
- if (_this.afterSubscriptionRemove) {
300
- _this.afterSubscriptionRemove(event);
301
- }
302
- });
303
- if (this.beforeSubscriptionAdd) {
304
- this.beforeSubscriptionAdd(event);
305
- }
306
- if (!this._subscriptions[event]) {
307
- this._subscriptions[event] = [];
308
- }
309
- this._subscriptions[event].push(subscriptionInstance);
310
- // Have TC39 `subscribe` immediately emit.
311
- // https://github.com/tc39/proposal-observable/issues/190
312
- if (isTC39Callback && LATEST_VALUE in this) {
313
- observer.next(this[LATEST_VALUE]);
314
- }
315
- return subscriptionInstance;
316
- },
317
- _a.notifySubscribers = function (valueToNotify, event) {
318
- event = event || defaultEvent;
319
- if (event === defaultEvent) {
320
- this.updateVersion();
321
- }
322
- if (this.hasSubscriptionsForEvent(event)) {
323
- var subs = event === defaultEvent && this._changeSubscriptions
324
- || __spread(this._subscriptions[event]);
325
- try {
326
- begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
327
- for (var i = 0, subscriptionInstance = void 0; subscriptionInstance = subs[i]; ++i) {
328
- // In case a subscription was disposed during the arrayForEach cycle, check
329
- // for isDisposed on each subscription before invoking its callback
330
- if (!subscriptionInstance._isDisposed) {
331
- subscriptionInstance._callback(valueToNotify);
332
- }
333
- }
334
- }
335
- finally {
336
- end(); // End suppressing dependency detection
337
- }
338
- }
339
- },
340
- _a.getVersion = function () {
341
- return this._versionNumber;
342
- },
343
- _a.hasChanged = function (versionToCheck) {
344
- return this.getVersion() !== versionToCheck;
345
- },
346
- _a.updateVersion = function () {
347
- ++this._versionNumber;
348
- },
349
- _a.hasSubscriptionsForEvent = function (event) {
350
- return this._subscriptions[event] && this._subscriptions[event].length;
351
- },
352
- _a.getSubscriptionsCount = function (event) {
353
- if (event) {
354
- return this._subscriptions[event] && this._subscriptions[event].length || 0;
355
- }
356
- else {
357
- var total = 0;
358
- objectForEach(this._subscriptions, function (eventName, subscriptions) {
359
- if (eventName !== 'dirty') {
360
- total += subscriptions.length;
361
- }
362
- });
363
- return total;
364
- }
365
- },
366
- _a.isDifferent = function (oldValue, newValue) {
367
- return !this.equalityComparer ||
368
- !this.equalityComparer(oldValue, newValue);
369
- },
370
- _a.once = function (cb) {
371
- var subs = this.subscribe(function (nv) {
372
- subs.dispose();
373
- cb(nv);
374
- });
375
- },
376
- _a.when = function (test, returnValue) {
377
- var _this = this;
378
- var current = this.peek();
379
- var givenRv = arguments.length > 1;
380
- var testFn = typeof test === 'function' ? test : function (v) { return v === test; };
381
- if (testFn(current)) {
382
- return options.Promise.resolve(givenRv ? returnValue : current);
383
- }
384
- return new options.Promise(function (resolve, reject) {
385
- var subs = _this.subscribe(function (newValue) {
386
- if (testFn(newValue)) {
387
- subs.dispose();
388
- resolve(givenRv ? returnValue : newValue);
389
- }
390
- });
391
- });
392
- },
393
- _a.yet = function (test) {
394
- var args = [];
395
- for (var _i = 1; _i < arguments.length; _i++) {
396
- args[_i - 1] = arguments[_i];
397
- }
398
- var testFn = typeof test === 'function' ? test : function (v) { return v === test; };
399
- var negated = function (v) { return !testFn(v); };
400
- return this.when.apply(this, __spread([negated], args));
401
- },
402
- _a.next = function () {
403
- var _this = this;
404
- return new Promise(function (resolve) { return _this.once(resolve); });
405
- },
406
- _a.toString = function () { return '[object Object]'; },
407
- _a.extend = applyExtenders,
408
- _a);
409
- // For browsers that support proto assignment, we overwrite the prototype of each
410
- // observable instance. Since observables are functions, we need Function.prototype
411
- // to still be in the prototype chain.
412
- Object.setPrototypeOf(ko_subscribable_fn, Function.prototype);
413
- subscribable.fn = ko_subscribable_fn;
414
-
415
- function observable(initialValue) {
416
- function Observable() {
417
- if (arguments.length > 0) {
418
- // Write
419
- // Ignore writes if the value hasn't changed
420
- if (Observable.isDifferent(Observable[LATEST_VALUE], arguments[0])) {
421
- Observable.valueWillMutate();
422
- Observable[LATEST_VALUE] = arguments[0];
423
- Observable.valueHasMutated();
424
- }
425
- return this; // Permits chained assignments
426
- }
427
- else {
428
- // Read
429
- registerDependency(Observable); // The caller only needs to be notified of changes if they did a "read" operation
430
- return Observable[LATEST_VALUE];
431
- }
432
- }
433
- overwriteLengthPropertyIfSupported(Observable, { value: undefined });
434
- Observable[LATEST_VALUE] = initialValue;
435
- subscribable.fn.init(Observable);
436
- // Inherit from 'observable'
437
- Object.setPrototypeOf(Observable, observable.fn);
438
- if (options.deferUpdates) {
439
- deferUpdates(Observable);
440
- }
441
- return Observable;
442
- }
443
- // Define prototype for observables
444
- observable.fn = {
445
- equalityComparer: valuesArePrimitiveAndEqual,
446
- peek: function () { return this[LATEST_VALUE]; },
447
- valueHasMutated: function () {
448
- this.notifySubscribers(this[LATEST_VALUE], 'spectate');
449
- this.notifySubscribers(this[LATEST_VALUE]);
450
- },
451
- valueWillMutate: function () {
452
- this.notifySubscribers(this[LATEST_VALUE], 'beforeChange');
453
- },
454
- modify: function (fn, peek) {
455
- if (peek === void 0) { peek = true; }
456
- return this(fn(peek ? this.peek() : this()));
457
- },
458
- // Some observables may not always be writeable, notably computeds.
459
- isWriteable: true
460
- };
461
- // Moved out of "limit" to avoid the extra closure
462
- function limitNotifySubscribers(value, event) {
463
- if (!event || event === defaultEvent) {
464
- this._limitChange(value);
465
- }
466
- else if (event === 'beforeChange') {
467
- this._limitBeforeChange(value);
468
- }
469
- else {
470
- this._origNotifySubscribers(value, event);
471
- }
472
- }
473
- // Add `limit` function to the subscribable prototype
474
- subscribable.fn.limit = function limit(limitFunction) {
475
- var self = this;
476
- var selfIsObservable = isObservable(self);
477
- var beforeChange = 'beforeChange';
478
- var ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate;
479
- if (!self._origNotifySubscribers) {
480
- self._origNotifySubscribers = self.notifySubscribers;
481
- self.notifySubscribers = limitNotifySubscribers;
482
- }
483
- var finish = limitFunction(function () {
484
- self._notificationIsPending = false;
485
- // If an observable provided a reference to itself, access it to get the latest value.
486
- // This allows computed observables to delay calculating their value until needed.
487
- if (selfIsObservable && pendingValue === self) {
488
- pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
489
- }
490
- var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));
491
- self._notifyNextChange = didUpdate = ignoreBeforeChange = false;
492
- if (shouldNotify) {
493
- self._origNotifySubscribers(previousValue = pendingValue);
494
- }
495
- });
496
- Object.assign(self, {
497
- _limitChange: function (value, isDirty) {
498
- if (!isDirty || !self._notificationIsPending) {
499
- didUpdate = !isDirty;
500
- }
501
- self._changeSubscriptions = __spread(self._subscriptions[defaultEvent]);
502
- self._notificationIsPending = ignoreBeforeChange = true;
503
- pendingValue = value;
504
- finish();
505
- },
506
- _limitBeforeChange: function (value) {
507
- if (!ignoreBeforeChange) {
508
- previousValue = value;
509
- self._origNotifySubscribers(value, beforeChange);
510
- }
511
- },
512
- _notifyNextChangeIfValueIsDifferent: function () {
513
- if (self.isDifferent(previousValue, self.peek(true /* evaluate */))) {
514
- notifyNextChange = true;
515
- }
516
- },
517
- _recordUpdate: function () {
518
- didUpdate = true;
519
- }
520
- });
521
- };
522
- Object.setPrototypeOf(observable.fn, subscribable.fn);
523
- var protoProperty = observable.protoProperty = options.protoProperty;
524
- observable.fn[protoProperty] = observable;
525
- // Subclasses can add themselves to observableProperties so that
526
- // isObservable will be `true`.
527
- observable.observablePrototypes = new Set([observable]);
528
- function isObservable(instance) {
529
- var proto = typeof instance === 'function' && instance[protoProperty];
530
- if (proto && !observable.observablePrototypes.has(proto)) {
531
- throw Error('Invalid object that looks like an observable; possibly from another Knockout instance');
532
- }
533
- return !!proto;
534
- }
535
- function unwrap(value) {
536
- return isObservable(value) ? value() : value;
537
- }
538
- function peek(value) {
539
- return isObservable(value) ? value.peek() : value;
540
- }
541
- function isWriteableObservable(instance) {
542
- return isObservable(instance) && instance.isWriteable;
119
+ export function peek(value) {
120
+ return isObservable(value) ? value.peek() : value;
543
121
  }
544
-
545
- //
546
- var arrayChangeEventName = 'arrayChange';
547
- function trackArrayChanges(target, options$$1) {
548
- // Use the provided options--each call to trackArrayChanges overwrites the previously set options
549
- target.compareArrayOptions = {};
550
- if (options$$1 && typeof options$$1 === 'object') {
551
- extend(target.compareArrayOptions, options$$1);
552
- }
553
- target.compareArrayOptions.sparse = true;
554
- // Only modify the target observable once
555
- if (target.cacheDiffForKnownOperation) {
556
- return;
557
- }
558
- var trackingChanges = false;
559
- var cachedDiff = null;
560
- var arrayChangeSubscription;
561
- var pendingNotifications = 0;
562
- var underlyingNotifySubscribersFunction;
563
- var underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd;
564
- var underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
565
- // Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
566
- target.beforeSubscriptionAdd = function (event) {
567
- if (underlyingBeforeSubscriptionAddFunction) {
568
- underlyingBeforeSubscriptionAddFunction.call(target, event);
569
- }
570
- if (event === arrayChangeEventName) {
571
- trackChanges();
572
- }
573
- };
574
- // Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
575
- target.afterSubscriptionRemove = function (event) {
576
- if (underlyingAfterSubscriptionRemoveFunction) {
577
- underlyingAfterSubscriptionRemoveFunction.call(target, event);
578
- }
579
- if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
580
- if (underlyingNotifySubscribersFunction) {
581
- target.notifySubscribers = underlyingNotifySubscribersFunction;
582
- underlyingNotifySubscribersFunction = undefined;
583
- }
584
- if (arrayChangeSubscription) {
585
- arrayChangeSubscription.dispose();
586
- }
587
- arrayChangeSubscription = null;
588
- trackingChanges = false;
589
- }
590
- };
591
- function trackChanges() {
592
- // Calling 'trackChanges' multiple times is the same as calling it once
593
- if (trackingChanges) {
594
- return;
595
- }
596
- trackingChanges = true;
597
- // Intercept "notifySubscribers" to track how many times it was called.
598
- underlyingNotifySubscribersFunction = target['notifySubscribers'];
599
- target.notifySubscribers = function (valueToNotify, event) {
600
- if (!event || event === defaultEvent) {
601
- ++pendingNotifications;
602
- }
603
- return underlyingNotifySubscribersFunction.apply(this, arguments);
604
- };
605
- // Each time the array changes value, capture a clone so that on the next
606
- // change it's possible to produce a diff
607
- var previousContents = [].concat(target.peek() === undefined ? [] : target.peek());
608
- cachedDiff = null;
609
- arrayChangeSubscription = target.subscribe(function (currentContents) {
610
- var changes;
611
- // Make a copy of the current contents and ensure it's an array
612
- currentContents = [].concat(currentContents || []);
613
- // Compute the diff and issue notifications, but only if someone is listening
614
- if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
615
- changes = getChanges(previousContents, currentContents);
616
- }
617
- // Eliminate references to the old, removed items, so they can be GCed
618
- previousContents = currentContents;
619
- cachedDiff = null;
620
- pendingNotifications = 0;
621
- if (changes && changes.length) {
622
- target.notifySubscribers(changes, arrayChangeEventName);
623
- }
624
- });
625
- }
626
- function getChanges(previousContents, currentContents) {
627
- // We try to re-use cached diffs.
628
- // The scenarios where pendingNotifications > 1 are when using rate-limiting or the Deferred Updates
629
- // plugin, which without this check would not be compatible with arrayChange notifications. Normally,
630
- // notifications are issued immediately so we wouldn't be queueing up more than one.
631
- if (!cachedDiff || pendingNotifications > 1) {
632
- cachedDiff = trackArrayChanges.compareArrays(previousContents, currentContents, target.compareArrayOptions);
633
- }
634
- return cachedDiff;
635
- }
636
- target.cacheDiffForKnownOperation = function (rawArray, operationName, args) {
637
- // Only run if we're currently tracking changes for this observable array
638
- // and there aren't any pending deferred notifications.
639
- if (!trackingChanges || pendingNotifications) {
640
- return;
641
- }
642
- var diff = [], arrayLength = rawArray.length, argsLength = args.length, offset = 0;
643
- function pushDiff(status, value, index) {
644
- return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
645
- }
646
- switch (operationName) {
647
- case 'push':
648
- offset = arrayLength;
649
- case 'unshift':
650
- for (var index = 0; index < argsLength; index++) {
651
- pushDiff('added', args[index], offset + index);
652
- }
653
- break;
654
- case 'pop':
655
- offset = arrayLength - 1;
656
- case 'shift':
657
- if (arrayLength) {
658
- pushDiff('deleted', rawArray[offset], offset);
659
- }
660
- break;
661
- case 'splice':
662
- // Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
663
- // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
664
- var startIndex = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength), endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(startIndex + (args[1] || 0), arrayLength), endAddIndex = startIndex + argsLength - 2, endIndex = Math.max(endDeleteIndex, endAddIndex), additions = [], deletions = [];
665
- for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
666
- if (index < endDeleteIndex) {
667
- deletions.push(pushDiff('deleted', rawArray[index], index));
668
- }
669
- if (index < endAddIndex) {
670
- additions.push(pushDiff('added', args[argsIndex], index));
671
- }
672
- }
673
- findMovesInArrayComparison(deletions, additions);
674
- break;
675
- default:
676
- return;
677
- }
678
- cachedDiff = diff;
679
- };
680
- }
681
- // Expose compareArrays for testing.
682
- trackArrayChanges.compareArrays = compareArrays;
683
- // Add the trackArrayChanges extender so we can use
684
- // obs.extend({ trackArrayChanges: true })
685
- extenders.trackArrayChanges = trackArrayChanges;
686
-
687
- var _a$1;
688
- function observableArray(initialValues) {
689
- initialValues = initialValues || [];
690
- if (typeof initialValues !== 'object' || !('length' in initialValues)) {
691
- throw new Error('The argument passed when initializing an observable array must be an array, or null, or undefined.');
692
- }
693
- var result = observable(initialValues);
694
- Object.setPrototypeOf(result, observableArray.fn);
695
- trackArrayChanges(result);
696
- // ^== result.extend({ trackArrayChanges: true })
697
- overwriteLengthPropertyIfSupported(result, { get: function () { return result().length; } });
698
- return result;
699
- }
700
- function isObservableArray(instance) {
701
- return isObservable(instance) && typeof instance.remove === 'function' && typeof instance.push === 'function';
702
- }
703
- observableArray.fn = (_a$1 = {
704
- remove: function (valueOrPredicate) {
705
- var underlyingArray = this.peek();
706
- var removedValues = [];
707
- var predicate = typeof valueOrPredicate === 'function' && !isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
708
- for (var i = 0; i < underlyingArray.length; i++) {
709
- var value = underlyingArray[i];
710
- if (predicate(value)) {
711
- if (removedValues.length === 0) {
712
- this.valueWillMutate();
713
- }
714
- if (underlyingArray[i] !== value) {
715
- throw Error("Array modified during remove; cannot remove item");
716
- }
717
- removedValues.push(value);
718
- underlyingArray.splice(i, 1);
719
- i--;
720
- }
721
- }
722
- if (removedValues.length) {
723
- this.valueHasMutated();
724
- }
725
- return removedValues;
726
- },
727
- removeAll: function (arrayOfValues) {
728
- // If you passed zero args, we remove everything
729
- if (arrayOfValues === undefined) {
730
- var underlyingArray = this.peek();
731
- var allValues = underlyingArray.slice(0);
732
- this.valueWillMutate();
733
- underlyingArray.splice(0, underlyingArray.length);
734
- this.valueHasMutated();
735
- return allValues;
736
- }
737
- // If you passed an arg, we interpret it as an array of entries to remove
738
- if (!arrayOfValues) {
739
- return [];
740
- }
741
- return this['remove'](function (value) {
742
- return arrayIndexOf(arrayOfValues, value) >= 0;
743
- });
744
- },
745
- destroy: function (valueOrPredicate) {
746
- var underlyingArray = this.peek();
747
- var predicate = typeof valueOrPredicate === 'function' && !isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
748
- this.valueWillMutate();
749
- for (var i = underlyingArray.length - 1; i >= 0; i--) {
750
- var value = underlyingArray[i];
751
- if (predicate(value)) {
752
- value['_destroy'] = true;
753
- }
754
- }
755
- this.valueHasMutated();
756
- },
757
- destroyAll: function (arrayOfValues) {
758
- // If you passed zero args, we destroy everything
759
- if (arrayOfValues === undefined) {
760
- return this.destroy(function () { return true; });
761
- }
762
- // If you passed an arg, we interpret it as an array of entries to destroy
763
- if (!arrayOfValues) {
764
- return [];
765
- }
766
- return this.destroy(function (value) {
767
- return arrayIndexOf(arrayOfValues, value) >= 0;
768
- });
769
- },
770
- indexOf: function (item) {
771
- return arrayIndexOf(this(), item);
772
- },
773
- replace: function (oldItem, newItem) {
774
- var index = this.indexOf(oldItem);
775
- if (index >= 0) {
776
- this.valueWillMutate();
777
- this.peek()[index] = newItem;
778
- this.valueHasMutated();
779
- }
780
- },
781
- sorted: function (compareFn) {
782
- return __spread(this()).sort(compareFn);
783
- },
784
- reversed: function () {
785
- return __spread(this()).reverse();
786
- }
787
- },
788
- _a$1[Symbol.iterator] = function () {
789
- return __generator(this, function (_a) {
790
- switch (_a.label) {
791
- case 0: return [5 /*yield**/, __values(this())];
792
- case 1:
793
- _a.sent();
794
- return [2 /*return*/];
795
- }
796
- });
797
- },
798
- _a$1);
799
- Object.setPrototypeOf(observableArray.fn, observable.fn);
800
- // Populate ko.observableArray.fn with read/write functions from native arrays
801
- // Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
802
- // because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
803
- arrayForEach(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function (methodName) {
804
- observableArray.fn[methodName] = function () {
805
- // Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
806
- // (for consistency with mutating regular observables)
807
- var underlyingArray = this.peek();
808
- this.valueWillMutate();
809
- this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
810
- var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
811
- this.valueHasMutated();
812
- // The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
813
- return methodCallResult === underlyingArray ? this : methodCallResult;
814
- };
815
- });
816
- // Populate ko.observableArray.fn with read-only functions from native arrays
817
- arrayForEach(['slice'], function (methodName) {
818
- observableArray.fn[methodName] = function () {
819
- var underlyingArray = this();
820
- return underlyingArray[methodName].apply(underlyingArray, arguments);
821
- };
822
- });
823
- // Expose for testing.
824
- observableArray.trackArrayChanges = trackArrayChanges;
825
-
826
- //
827
- var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)
828
- function toJS(rootObject) {
829
- if (arguments.length == 0) {
830
- throw new Error('When calling ko.toJS, pass the object you want to convert.');
831
- }
832
- // We just unwrap everything at every level in the object graph
833
- return mapJsObjectGraph(rootObject, function (valueToMap) {
834
- // Loop because an observable's value might in turn be another observable wrapper
835
- for (var i = 0; isObservable(valueToMap) && (i < maxNestedObservableDepth); i++) {
836
- valueToMap = valueToMap();
837
- }
838
- return valueToMap;
839
- });
840
- }
841
- function toJSON(rootObject, replacer, space) {
842
- var plainJavaScriptObject = toJS(rootObject);
843
- return JSON.stringify(plainJavaScriptObject, replacer, space);
844
- }
845
- function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
846
- visitedObjects = visitedObjects || new objectLookup();
847
- rootObject = mapInputCallback(rootObject);
848
- var canHaveProperties = (typeof rootObject === 'object') && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
849
- if (!canHaveProperties) {
850
- return rootObject;
851
- }
852
- var outputProperties = rootObject instanceof Array ? [] : {};
853
- visitedObjects.save(rootObject, outputProperties);
854
- visitPropertiesOrArrayEntries(rootObject, function (indexer) {
855
- var propertyValue = mapInputCallback(rootObject[indexer]);
856
- switch (typeof propertyValue) {
857
- case 'boolean':
858
- case 'number':
859
- case 'string':
860
- case 'function':
861
- outputProperties[indexer] = propertyValue;
862
- break;
863
- case 'object':
864
- case 'undefined':
865
- var previouslyMappedValue = visitedObjects.get(propertyValue);
866
- outputProperties[indexer] = (previouslyMappedValue !== undefined)
867
- ? previouslyMappedValue
868
- : mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
869
- break;
870
- }
871
- });
872
- return outputProperties;
873
- }
874
- function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
875
- if (rootObject instanceof Array) {
876
- for (var i = 0; i < rootObject.length; i++) {
877
- visitorCallback(i);
878
- }
879
- // For arrays, also respect toJSON property for custom mappings (fixes #278)
880
- if (typeof rootObject['toJSON'] === 'function') {
881
- visitorCallback('toJSON');
882
- }
883
- }
884
- else {
885
- for (var propertyName in rootObject) {
886
- visitorCallback(propertyName);
887
- }
888
- }
889
- }
890
- function objectLookup() {
891
- this.keys = [];
892
- this.values = [];
893
- }
894
- objectLookup.prototype = {
895
- constructor: objectLookup,
896
- save: function (key, value) {
897
- var existingIndex = arrayIndexOf(this.keys, key);
898
- if (existingIndex >= 0) {
899
- this.values[existingIndex] = value;
900
- }
901
- else {
902
- this.keys.push(key);
903
- this.values.push(value);
904
- }
905
- },
906
- get: function (key) {
907
- var existingIndex = arrayIndexOf(this.keys, key);
908
- return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
909
- }
910
- };
911
-
912
- //
913
-
914
- export { dependencyDetection, observable, isObservable, unwrap, peek, isWriteableObservable, isWriteableObservable as isWritableObservable, isSubscribable, subscribable, LATEST_VALUE, observableArray, isObservableArray, trackArrayChanges, arrayChangeEventName, toJS, toJSON, deferUpdates, valuesArePrimitiveAndEqual, applyExtenders, extenders };
915
- //# sourceMappingURL=observable.js.map
122
+ export function isWriteableObservable(instance) {
123
+ return isObservable(instance) && instance.isWriteable;
124
+ }
125
+ export { isWriteableObservable as isWritableObservable };