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