mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-03 14:37:02 +03:00
More Optional chaining in Knockout.js
This commit is contained in:
parent
d18f93d87f
commit
a566f6ed5a
10 changed files with 391 additions and 476 deletions
|
|
@ -63,8 +63,6 @@ ko.computed = (evaluatorFunctionOrOptions, options) => {
|
|||
state.pure = true;
|
||||
state.isSleeping = true; // Starts off sleeping; will awake on the first subscription
|
||||
ko.utils.extend(computedObservable, pureComputedOverrides);
|
||||
} else if (options['deferEvaluation']) {
|
||||
ko.utils.extend(computedObservable, deferEvaluationOverrides);
|
||||
}
|
||||
|
||||
if (state.disposeWhenNodeIsRemoved) {
|
||||
|
|
@ -82,10 +80,8 @@ ko.computed = (evaluatorFunctionOrOptions, options) => {
|
|||
}
|
||||
}
|
||||
|
||||
// Evaluate, unless sleeping or deferEvaluation is true
|
||||
if (!state.isSleeping && !options['deferEvaluation']) {
|
||||
computedObservable.evaluateImmediate();
|
||||
}
|
||||
// Evaluate, unless sleeping
|
||||
state.isSleeping || computedObservable.evaluateImmediate();
|
||||
|
||||
// Attach a DOM node disposal callback so that the computed will be proactively disposed as soon as the node is
|
||||
// removed using ko.removeNode. But skip if isActive is false (there will never be any dependencies to dispose).
|
||||
|
|
@ -228,16 +224,13 @@ var computedFn = {
|
|||
disposeWhen = state.disposeWhen,
|
||||
changed = false;
|
||||
|
||||
if (state.isBeingEvaluated) {
|
||||
// If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
|
||||
// This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
|
||||
// certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
|
||||
// their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
|
||||
return;
|
||||
}
|
||||
|
||||
// If the evaluation of a ko.computed causes side effects, it's possible that it will trigger its own re-evaluation.
|
||||
// This is not desirable (it's hard for a developer to realise a chain of dependencies might cause this, and they almost
|
||||
// certainly didn't intend infinite re-evaluations). So, for predictability, we simply prevent ko.computeds from causing
|
||||
// their own re-evaluation. Further discussion at https://github.com/SteveSanderson/knockout/pull/387
|
||||
if (state.isBeingEvaluated
|
||||
// Do not evaluate (and possibly capture new dependencies) if disposed
|
||||
if (state.isDisposed) {
|
||||
|| state.isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -252,8 +245,8 @@ var computedFn = {
|
|||
state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
|
||||
}
|
||||
|
||||
state.isBeingEvaluated = true;
|
||||
try {
|
||||
state.isBeingEvaluated = true;
|
||||
changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
|
||||
} finally {
|
||||
state.isBeingEvaluated = false;
|
||||
|
|
@ -324,7 +317,7 @@ var computedFn = {
|
|||
return changed;
|
||||
},
|
||||
peek: function (evaluate) {
|
||||
// By default, peek won't re-evaluate, except while the computed is sleeping or to get the initial value when "deferEvaluation" is set.
|
||||
// By default, peek won't re-evaluate, except while the computed is sleeping.
|
||||
// Pass in true to evaluate if needed.
|
||||
var state = this[computedState];
|
||||
if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {
|
||||
|
|
@ -333,30 +326,31 @@ var computedFn = {
|
|||
return state.latestValue;
|
||||
},
|
||||
limit: function (limitFunction) {
|
||||
var self = this;
|
||||
// Override the limit function with one that delays evaluation as well
|
||||
ko.subscribable['fn'].limit.call(this, limitFunction);
|
||||
this._evalIfChanged = function () {
|
||||
if (!this[computedState].isSleeping) {
|
||||
if (this[computedState].isStale) {
|
||||
this.evaluateImmediate();
|
||||
ko.subscribable['fn'].limit.call(self, limitFunction);
|
||||
self._evalIfChanged = () => {
|
||||
if (!self[computedState].isSleeping) {
|
||||
if (self[computedState].isStale) {
|
||||
self.evaluateImmediate();
|
||||
} else {
|
||||
this[computedState].isDirty = false;
|
||||
self[computedState].isDirty = false;
|
||||
}
|
||||
}
|
||||
return this[computedState].latestValue;
|
||||
return self[computedState].latestValue;
|
||||
};
|
||||
this._evalDelayed = function (isChange) {
|
||||
this._limitBeforeChange(this[computedState].latestValue);
|
||||
self._evalDelayed = isChange => {
|
||||
self._limitBeforeChange(self[computedState].latestValue);
|
||||
|
||||
// Mark as dirty
|
||||
this[computedState].isDirty = true;
|
||||
self[computedState].isDirty = true;
|
||||
if (isChange) {
|
||||
this[computedState].isStale = true;
|
||||
self[computedState].isStale = true;
|
||||
}
|
||||
|
||||
// Pass the observable to the "limit" code, which will evaluate it when
|
||||
// it's time to do the notification.
|
||||
this._limitChange(this, !isChange /* isDirty */);
|
||||
self._limitChange(self, !isChange /* isDirty */);
|
||||
};
|
||||
},
|
||||
dispose: function () {
|
||||
|
|
@ -450,15 +444,6 @@ var pureComputedOverrides = {
|
|||
}
|
||||
};
|
||||
|
||||
var deferEvaluationOverrides = {
|
||||
beforeSubscriptionAdd: function (event) {
|
||||
// This will force a computed with deferEvaluation to evaluate when the first subscription is registered.
|
||||
if (event == 'change' || event == 'beforeChange') {
|
||||
this.peek();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Note that for browsers that don't support proto assignment, the
|
||||
// inheritance chain is created manually in the ko.computed constructor
|
||||
Object.setPrototypeOf(computedFn, ko.subscribable['fn']);
|
||||
|
|
|
|||
|
|
@ -22,25 +22,17 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
|
||||
// Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
|
||||
target.beforeSubscriptionAdd = event => {
|
||||
if (underlyingBeforeSubscriptionAddFunction) {
|
||||
underlyingBeforeSubscriptionAddFunction.call(target, event);
|
||||
}
|
||||
underlyingBeforeSubscriptionAddFunction?.call(target, event);
|
||||
if (event === arrayChangeEventName) {
|
||||
trackChanges();
|
||||
}
|
||||
};
|
||||
// Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
|
||||
target.afterSubscriptionRemove = event => {
|
||||
if (underlyingAfterSubscriptionRemoveFunction) {
|
||||
underlyingAfterSubscriptionRemoveFunction.call(target, event);
|
||||
}
|
||||
underlyingAfterSubscriptionRemoveFunction?.call(target, event);
|
||||
if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
|
||||
if (changeSubscription) {
|
||||
changeSubscription.dispose();
|
||||
}
|
||||
if (spectateSubscription) {
|
||||
spectateSubscription.dispose();
|
||||
}
|
||||
changeSubscription?.dispose();
|
||||
spectateSubscription?.dispose();
|
||||
spectateSubscription = changeSubscription = null;
|
||||
trackingChanges = false;
|
||||
previousContents = undefined;
|
||||
|
|
@ -48,23 +40,6 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
};
|
||||
|
||||
function trackChanges() {
|
||||
if (trackingChanges) {
|
||||
// Whenever there's a new subscription and there are pending notifications, make sure all previous
|
||||
// subscriptions are notified of the change so that all subscriptions are in sync.
|
||||
notifyChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
trackingChanges = true;
|
||||
|
||||
// Track how many times the array actually changed value
|
||||
spectateSubscription = target.subscribe(() => ++pendingChanges, null, "spectate");
|
||||
|
||||
// Each time the array changes value, capture a clone so that on the next
|
||||
// change it's possible to produce a diff
|
||||
previousContents = [].concat(target.peek() || []);
|
||||
cachedDiff = null;
|
||||
changeSubscription = target.subscribe(notifyChanges);
|
||||
|
||||
function notifyChanges() {
|
||||
if (pendingChanges) {
|
||||
|
|
@ -86,6 +61,24 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trackingChanges) {
|
||||
// Whenever there's a new subscription and there are pending notifications, make sure all previous
|
||||
// subscriptions are notified of the change so that all subscriptions are in sync.
|
||||
notifyChanges();
|
||||
return;
|
||||
}
|
||||
|
||||
trackingChanges = true;
|
||||
|
||||
// Track how many times the array actually changed value
|
||||
spectateSubscription = target.subscribe(() => ++pendingChanges, null, "spectate");
|
||||
|
||||
// Each time the array changes value, capture a clone so that on the next
|
||||
// change it's possible to produce a diff
|
||||
previousContents = [].concat(target.peek() || []);
|
||||
cachedDiff = null;
|
||||
changeSubscription = target.subscribe(notifyChanges);
|
||||
}
|
||||
|
||||
function getChanges(previousContents, currentContents) {
|
||||
|
|
@ -126,20 +119,19 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
|
|||
case 'pop':
|
||||
offset = arrayLength - 1;
|
||||
case 'shift':
|
||||
if (arrayLength) {
|
||||
pushDiff('deleted', rawArray[offset], offset);
|
||||
}
|
||||
arrayLength && pushDiff('deleted', rawArray[offset], offset);
|
||||
break;
|
||||
|
||||
case 'splice':
|
||||
// Negative start index means 'from end of array'. After that we clamp to [0...arrayLength].
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
|
||||
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,
|
||||
var index = Math.min(Math.max(0, args[0] < 0 ? arrayLength + args[0] : args[0]), arrayLength),
|
||||
endDeleteIndex = argsLength === 1 ? arrayLength : Math.min(index + (args[1] || 0), arrayLength),
|
||||
endAddIndex = index + argsLength - 2,
|
||||
endIndex = Math.max(endDeleteIndex, endAddIndex),
|
||||
additions = [], deletions = [];
|
||||
for (let index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
|
||||
additions = [], deletions = [],
|
||||
argsIndex = 2;
|
||||
for (; index < endIndex; ++index, ++argsIndex) {
|
||||
if (index < endDeleteIndex)
|
||||
deletions.push(pushDiff('deleted', rawArray[index], index));
|
||||
if (index < endAddIndex)
|
||||
|
|
|
|||
|
|
@ -14,9 +14,8 @@ class koSubscription
|
|||
dispose() {
|
||||
var self = this;
|
||||
if (!self._isDisposed) {
|
||||
if (self._domNodeDisposalCallback) {
|
||||
ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
|
||||
}
|
||||
self._domNodeDisposalCallback
|
||||
&& ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
|
||||
self._isDisposed = true;
|
||||
self._disposeCallback();
|
||||
|
||||
|
|
@ -53,15 +52,12 @@ var ko_subscribable_fn = {
|
|||
|
||||
var subscription = new koSubscription(self, boundCallback, () => {
|
||||
self._subscriptions.get(event).delete(subscription);
|
||||
if (self.afterSubscriptionRemove)
|
||||
self.afterSubscriptionRemove(event);
|
||||
self.afterSubscriptionRemove?.(event);
|
||||
});
|
||||
|
||||
if (self.beforeSubscriptionAdd)
|
||||
self.beforeSubscriptionAdd(event);
|
||||
self.beforeSubscriptionAdd?.(event);
|
||||
|
||||
if (!self._subscriptions.has(event))
|
||||
self._subscriptions.set(event, new Set);
|
||||
self._subscriptions.has(event) || self._subscriptions.set(event, new Set);
|
||||
self._subscriptions.get(event).add(subscription);
|
||||
|
||||
return subscription;
|
||||
|
|
@ -107,13 +103,13 @@ var ko_subscribable_fn = {
|
|||
if (!self._origNotifySubscribers) {
|
||||
self._origNotifySubscribers = self.notifySubscribers;
|
||||
// Moved out of "limit" to avoid the extra closure
|
||||
self.notifySubscribers = function(value, event) {
|
||||
self.notifySubscribers = (value, event) => {
|
||||
if (!event || event === defaultEvent) {
|
||||
this._limitChange(value);
|
||||
} else if (event === 'beforeChange') {
|
||||
this._limitBeforeChange(value);
|
||||
self._limitChange(value);
|
||||
} else if (event === beforeChange) {
|
||||
self._limitBeforeChange(value);
|
||||
} else {
|
||||
this._origNotifySubscribers(value, event);
|
||||
self._origNotifySubscribers(value, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue