Changed Knockout a bit

This commit is contained in:
the-djmaze 2024-02-03 12:06:56 +01:00
parent a352ebff25
commit f105ed3f9d
15 changed files with 205 additions and 194 deletions

View file

@ -1,6 +1,6 @@
(() => {
var outerFrames = [],
let outerFrames = [],
currentFrame,
lastId = 0,
@ -25,7 +25,7 @@ ko.dependencyDetection = {
}
},
ignore: (callback, callbackTarget, callbackArgs) => {
ignore(callback, callbackTarget, callbackArgs) {
try {
begin();
return callback.apply(callbackTarget, callbackArgs || []);

View file

@ -54,7 +54,7 @@ ko.computed = (evaluatorFunctionOrOptions, options) => {
computedObservable.hasWriteFunction = typeof writeFunction === "function";
// Inherit from 'subscribable'
ko.subscribable['fn'].init(computedObservable);
ko.subscribable['fn']['init'](computedObservable);
// Inherit from 'computed'
Object.setPrototypeOf(computedObservable, computedFn);
@ -143,10 +143,10 @@ function evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyD
var computedFn = {
equalityComparer: valuesArePrimitiveAndEqual,
getDependenciesCount: function () {
getDependenciesCount() {
return this[computedState].dependenciesCount;
},
getDependencies: function () {
getDependencies() {
var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];
ko.utils.objectForEach(dependencyTracking, (id, dependency) =>
@ -155,7 +155,7 @@ var computedFn = {
return dependentObservables;
},
hasAncestorDependency: function (obs) {
hasAncestorDependency(obs) {
if (!this[computedState].dependenciesCount) {
return false;
}
@ -163,7 +163,7 @@ var computedFn = {
return dependencies.includes(obs)
|| !!dependencies.find(dep => dep.hasAncestorDependency && dep.hasAncestorDependency(obs));
},
addDependencyTracking: function (id, target, trackingObj) {
addDependencyTracking(id, target, trackingObj) {
if (this[computedState].pure && target === this) {
throw Error("A 'pure' computed must not be called recursively");
}
@ -172,7 +172,7 @@ var computedFn = {
trackingObj._order = this[computedState].dependenciesCount++;
trackingObj._version = target.getVersion();
},
haveDependenciesChanged: function () {
haveDependenciesChanged() {
var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
for (id in dependencyTracking) {
if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {
@ -183,17 +183,17 @@ var computedFn = {
}
}
},
markDirty: function () {
markDirty() {
// Process "dirty" events if we can handle delayed notifications
if (!this[computedState].isBeingEvaluated) {
this._evalDelayed?.(false /*isChange*/);
}
},
isActive: function () {
isActive() {
var state = this[computedState];
return state.isDirty || state.dependenciesCount > 0;
},
respondToChange: function () {
respondToChange() {
// Ignore "change" events if we've already scheduled a delayed notification
if (!this._notificationIsPending) {
this.evaluatePossiblyAsync();
@ -201,10 +201,10 @@ var computedFn = {
this[computedState].isStale = true;
}
},
subscribeToDependency: function (target) {
return target.subscribe(this.evaluatePossiblyAsync, this);
subscribeToDependency(target) {
return target['subscribe'](this.evaluatePossiblyAsync, this);
},
evaluatePossiblyAsync: function () {
evaluatePossiblyAsync() {
var computedObservable = this,
throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
if (throttleEvaluationTimeout >= 0) {
@ -218,7 +218,7 @@ var computedFn = {
computedObservable.evaluateImmediate(true /*notifyChange*/);
}
},
evaluateImmediate: function (notifyChange) {
evaluateImmediate(notifyChange) {
var computedObservable = this,
state = computedObservable[computedState],
disposeWhen = state.disposeWhen,
@ -254,7 +254,7 @@ var computedFn = {
return changed;
},
evaluateImmediate_CallReadWithDependencyDetection: function (notifyChange) {
evaluateImmediate_CallReadWithDependencyDetection(notifyChange) {
// This function is really just part of the evaluateImmediate logic. You would never call it from anywhere else.
// Factoring it out into a separate function means it can be independent of the try/catch block in evaluateImmediate,
// which contributes to saving about 40% off the CPU overhead of computed evaluation (on V8 at least).
@ -316,7 +316,7 @@ var computedFn = {
return changed;
},
peek: function (evaluate) {
peek(evaluate) {
// By default, peek won't re-evaluate, except while the computed is sleeping.
// Pass in true to evaluate if needed.
var state = this[computedState];
@ -325,7 +325,7 @@ var computedFn = {
}
return state.latestValue;
},
limit: function (limitFunction) {
limit(limitFunction) {
var self = this;
// Override the limit function with one that delays evaluation as well
ko.subscribable['fn'].limit.call(self, limitFunction);
@ -353,7 +353,7 @@ var computedFn = {
self._limitChange(self, !isChange /* isDirty */);
};
},
dispose: function () {
dispose() {
var state = this[computedState];
if (!state.isSleeping && state.dependencyTracking) {
ko.utils.objectForEach(state.dependencyTracking, (id, dependency) =>
@ -376,7 +376,7 @@ var computedFn = {
};
var pureComputedOverrides = {
beforeSubscriptionAdd: function (event) {
beforeSubscriptionAdd(event) {
// If asleep, wake up the computed by subscribing to any dependencies.
var computedObservable = this,
state = computedObservable[computedState];
@ -415,7 +415,7 @@ var pureComputedOverrides = {
}
}
},
afterSubscriptionRemove: function (event) {
afterSubscriptionRemove(event) {
var state = this[computedState];
if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
ko.utils.objectForEach(state.dependencyTracking, (id, dependency) => {
@ -432,7 +432,7 @@ var pureComputedOverrides = {
this.notifySubscribers(undefined, "asleep");
}
},
getVersion: function () {
getVersion() {
// Because a pure computed is not automatically updated while it is sleeping, we can't
// simply return the version number. Instead, we check if any of the dependencies have
// changed and conditionally re-evaluate the computed observable.

View file

@ -1,5 +1,6 @@
const observableLatestValue = Symbol('_latestValue'),
length = 'length';
//const IS_OBSERVABLE = Symbol('IS_OBSERVABLE');
ko.observable = initialValue => {
function observable() {
@ -10,7 +11,7 @@ ko.observable = initialValue => {
if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
observable.valueWillMutate();
observable[observableLatestValue] = arguments[0];
observable.valueHasMutated();
observable['valueHasMutated']();
}
return this; // Permits chained assignments
}
@ -26,7 +27,7 @@ ko.observable = initialValue => {
});
// Inherit from 'subscribable'
ko.subscribable['fn'].init(observable);
ko.subscribable['fn']['init'](observable);
// Inherit from 'observable'
return Object.setPrototypeOf(observable, observableFn);
@ -34,17 +35,18 @@ ko.observable = initialValue => {
// Define prototype for observables
var observableFn = {
'toJSON': function() {
// [IS_OBSERVABLE]: 1,
'toJSON'() {
let value = this[observableLatestValue];
return value?.toJSON?.() || value;
},
equalityComparer: valuesArePrimitiveAndEqual,
peek: function() { return this[observableLatestValue]; },
valueHasMutated: function () {
peek() { return this[observableLatestValue]; },
'valueHasMutated'() {
this.notifySubscribers(this[observableLatestValue], 'spectate');
this.notifySubscribers(this[observableLatestValue]);
},
valueWillMutate: function () { this.notifySubscribers(this[observableLatestValue], 'beforeChange'); }
valueWillMutate() { this.notifySubscribers(this[observableLatestValue], 'beforeChange'); }
};
// Note that for browsers that don't support proto assignment, the
@ -54,6 +56,7 @@ Object.setPrototypeOf(observableFn, ko.subscribable['fn']);
var protoProperty = ko.observable.protoProperty = '__ko_proto__';
observableFn[protoProperty] = ko.observable;
//ko.isObservable = obj => !!(obj && obj[IS_OBSERVABLE]);
ko.isObservable = instance => {
var proto = typeof instance == 'function' && instance[protoProperty];
if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {
@ -71,4 +74,3 @@ ko.isWriteableObservable = instance => {
ko.exportSymbol('observable', ko.observable);
ko.exportSymbol('isObservable', ko.isObservable);
ko.exportSymbol('observable.fn', observableFn);
ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);

View file

@ -72,13 +72,13 @@ ko.extenders['trackArrayChanges'] = (target, options) => {
trackingChanges = true;
// Track how many times the array actually changed value
spectateSubscription = target.subscribe(() => ++pendingChanges, null, "spectate");
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);
changeSubscription = target['subscribe'](notifyChanges);
}
function getChanges(previousContents, currentContents) {

View file

@ -7,10 +7,13 @@ ko.observableArray = initialValues => {
return Object.setPrototypeOf(ko.observable(initialValues), ko.observableArray['fn']).extend({'trackArrayChanges':true});
};
//const IS_OBSERVABLE_ARRAY = Symbol('IS_OBSERVABLE_ARRAY');
// Note that for browsers that don't support proto assignment, the
// inheritance chain is created manually in the ko.observableArray constructor
ko.observableArray['fn'] = Object.setPrototypeOf({
'remove': function (valueOrPredicate) {
// [IS_OBSERVABLE_ARRAY]: 1,
'remove'(valueOrPredicate) {
var underlyingArray = this.peek();
var removed = false;
var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate)
@ -27,7 +30,7 @@ ko.observableArray['fn'] = Object.setPrototypeOf({
underlyingArray.splice(i, 1);
}
}
removed && this.valueHasMutated();
removed && this['valueHasMutated']();
}
}, ko.observable['fn']);
@ -47,7 +50,7 @@ Object.getOwnPropertyNames(Array.prototype).forEach(methodName => {
this.valueWillMutate();
this.cacheDiffForKnownOperation(underlyingArray, methodName, args);
var methodCallResult = underlyingArray[methodName](...args);
this.valueHasMutated();
this['valueHasMutated']();
// The native sort and reverse methods return a reference to the array, but it makes more sense to return the observable array instead.
return methodCallResult === underlyingArray ? this : methodCallResult;
};
@ -60,11 +63,11 @@ Object.getOwnPropertyNames(Array.prototype).forEach(methodName => {
}
});
ko.isObservableArray = instance => {
return ko.isObservable(instance)
//ko.isObservableArray = obj => !!(obj && obj[IS_OBSERVABLE_ARRAY]);
ko.isObservableArray = instance =>
ko.isObservable(instance)
&& typeof instance["remove"] == "function"
&& typeof instance["push"] == "function";
};
ko.exportSymbol('observableArray', ko.observableArray);
ko.exportSymbol('isObservableArray', ko.isObservableArray);

View file

@ -32,19 +32,23 @@ class koSubscription
ko.subscribable = function () {
Object.setPrototypeOf(this, ko_subscribable_fn);
ko_subscribable_fn.init(this);
ko_subscribable_fn['init'](this);
}
var defaultEvent = "change";
//const IS_SUBSCRIBABLE = Symbol('IS_SUBSCRIBABLE');
var ko_subscribable_fn = {
init: instance => {
// [IS_SUBSCRIBABLE]: 1,
'init': instance => {
instance._subscriptions = new Map();
instance._subscriptions.set("change", new Set);
instance._versionNumber = 1;
},
subscribe: function (callback, callbackTarget, event) {
'subscribe'(callback, callbackTarget, event) {
var self = this;
event = event || defaultEvent;
@ -63,7 +67,7 @@ var ko_subscribable_fn = {
return subscription;
},
notifySubscribers: function (valueToNotify, event) {
notifySubscribers(valueToNotify, event) {
event = event || defaultEvent;
if (event === defaultEvent) {
this.updateVersion();
@ -83,19 +87,19 @@ var ko_subscribable_fn = {
}
},
getVersion: function () {
getVersion() {
return this._versionNumber;
},
hasChanged: function (versionToCheck) {
hasChanged(versionToCheck) {
return this.getVersion() !== versionToCheck;
},
updateVersion: function () {
updateVersion() {
++this._versionNumber;
},
limit: function(limitFunction) {
limit(limitFunction) {
var self = this, selfIsObservable = ko.isObservable(self),
ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
beforeChange = 'beforeChange';
@ -156,17 +160,17 @@ var ko_subscribable_fn = {
};
},
hasSubscriptionsForEvent: function(event) {
hasSubscriptionsForEvent(event) {
return (this._subscriptions.get(event) || []).size;
},
isDifferent: function(oldValue, newValue) {
isDifferent(oldValue, newValue) {
return !this.equalityComparer || !this.equalityComparer(oldValue, newValue);
},
toString: () => '[object Object]',
extend: function(requestedExtenders) {
'extend'(requestedExtenders) {
var target = this;
if (requestedExtenders) {
ko.utils.objectForEach(requestedExtenders, (key, value) => {
@ -180,14 +184,11 @@ var ko_subscribable_fn = {
}
};
ko.exportProperty(ko_subscribable_fn, 'init', ko_subscribable_fn.init);
ko.exportProperty(ko_subscribable_fn, 'subscribe', ko_subscribable_fn.subscribe);
ko.exportProperty(ko_subscribable_fn, 'extend', ko_subscribable_fn.extend);
// For browsers that support proto assignment, we overwrite the prototype of each
// observable instance. Since observables are functions, we need Function.prototype
// to still be in the prototype chain.
ko.subscribable['fn'] = Object.setPrototypeOf(ko_subscribable_fn, Function.prototype);
//ko.isSubscribable = obj => !!(obj && obj[IS_SUBSCRIBABLE]);
ko.isSubscribable = instance =>
typeof instance?.subscribe == "function" && typeof instance.notifySubscribers == "function";
typeof instance?.['subscribe'] == "function" && typeof instance.notifySubscribers == "function";