KnockoutJS to ES2015/ES6

TODO: use the new Proxy for ko.observable
This commit is contained in:
djmaze 2020-10-04 21:58:13 +02:00
parent a0f8ac0dad
commit 72ed2b08b2
56 changed files with 941 additions and 1873 deletions

View file

@ -1,19 +1,9 @@
ko.computedContext = ko.dependencyDetection = (function () {
ko.computedContext = ko.dependencyDetection = (() => {
var outerFrames = [],
currentFrame,
lastId = 0;
// Return a unique ID that can be assigned to an observable for dependency tracking.
// Theoretically, you could eventually overflow the number storage size, resulting
// in duplicate IDs. But in JavaScript, the largest exact integral value is 2^53
// or 9,007,199,254,740,992. If you created 1,000,000 IDs per second, it would
// take over 285 years to reach that number.
// Reference http://blog.vjeux.com/2010/javascript/javascript-max_int-number-limits.html
function getId() {
return ++lastId;
}
function begin(options) {
outerFrames.push(currentFrame);
currentFrame = options;
@ -28,15 +18,15 @@ ko.computedContext = ko.dependencyDetection = (function () {
end: end,
registerDependency: function (subscribable) {
registerDependency: subscribable => {
if (currentFrame) {
if (!ko.isSubscribable(subscribable))
throw new Error("Only subscribable things can act as dependencies");
currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = ++lastId));
}
},
ignore: function (callback, callbackTarget, callbackArgs) {
ignore: (callback, callbackTarget, callbackArgs) => {
try {
begin();
return callback.apply(callbackTarget, callbackArgs || []);
@ -45,22 +35,22 @@ ko.computedContext = ko.dependencyDetection = (function () {
}
},
getDependenciesCount: function () {
getDependenciesCount: () => {
if (currentFrame)
return currentFrame.computed.getDependenciesCount();
},
getDependencies: function () {
getDependencies: () => {
if (currentFrame)
return currentFrame.computed.getDependencies();
},
isInitial: function() {
isInitial: () => {
if (currentFrame)
return currentFrame.isInitial;
},
computed: function() {
computed: () => {
if (currentFrame)
return currentFrame.computed;
}

View file

@ -153,9 +153,9 @@ var computedFn = {
getDependencies: function () {
var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];
ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
dependentObservables[dependency._order] = dependency._target;
});
ko.utils.objectForEach(dependencyTracking, (id, dependency) =>
dependentObservables[dependency._order] = dependency._target
);
return dependentObservables;
},
@ -167,9 +167,9 @@ var computedFn = {
if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {
return true;
}
return !!ko.utils.arrayFirst(dependencies, function (dep) {
return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);
});
return !!ko.utils.arrayFirst(dependencies, dep =>
dep.hasAncestorDependency && dep.hasAncestorDependency(obs)
);
},
addDependencyTracking: function (id, target, trackingObj) {
if (this[computedState].pure && target === this) {
@ -215,7 +215,7 @@ var computedFn = {
changeSub = target.subscribe(this.respondToChange, this);
return {
_target: target,
dispose: function () {
dispose: () => {
dirtySub.dispose();
changeSub.dispose();
}
@ -229,9 +229,9 @@ var computedFn = {
throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
clearTimeout(this[computedState].evaluationTimeoutInstance);
this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
computedObservable.evaluateImmediate(true /*notifyChange*/);
}, throttleEvaluationTimeout);
this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(() =>
computedObservable.evaluateImmediate(true /*notifyChange*/)
, throttleEvaluationTimeout);
} else if (computedObservable._evalDelayed) {
computedObservable._evalDelayed(true /*isChange*/);
} else {
@ -340,7 +340,7 @@ var computedFn = {
return changed;
},
evaluateImmediate_CallReadThenEndDependencyDetection: function (state, dependencyDetectionContext) {
evaluateImmediate_CallReadThenEndDependencyDetection: (state, dependencyDetectionContext) => {
// This function is really part of the evaluateImmediate_CallReadWithDependencyDetection logic.
// You'd never call it from anywhere else. Factoring it out means that evaluateImmediate_CallReadWithDependencyDetection
// can be independent of try/finally blocks, which contributes to saving about 40% off the CPU
@ -399,10 +399,9 @@ var computedFn = {
dispose: function () {
var state = this[computedState];
if (!state.isSleeping && state.dependencyTracking) {
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
if (dependency.dispose)
dependency.dispose();
});
ko.utils.objectForEach(state.dependencyTracking, (id, dependency) =>
dependency.dispose && dependency.dispose()
);
}
if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
@ -438,11 +437,11 @@ var pureComputedOverrides = {
} else {
// First put the dependencies in order
var dependenciesOrder = [];
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
dependenciesOrder[dependency._order] = id;
});
ko.utils.objectForEach(state.dependencyTracking, (id, dependency) =>
dependenciesOrder[dependency._order] = id
);
// Next, subscribe to each one
ko.utils.arrayForEach(dependenciesOrder, function (id, order) {
ko.utils.arrayForEach(dependenciesOrder, (id, order) => {
var dependency = state.dependencyTracking[id],
subscription = computedObservable.subscribeToDependency(dependency._target);
subscription._order = order;
@ -465,7 +464,7 @@ var pureComputedOverrides = {
afterSubscriptionRemove: function (event) {
var state = this[computedState];
if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
ko.utils.objectForEach(state.dependencyTracking, (id, dependency) => {
if (dependency.dispose) {
state.dependencyTracking[id] = {
_target: dependency._target,
@ -510,13 +509,9 @@ if (ko.utils.canSetPrototype) {
var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
computedFn[protoProp] = ko.computed;
ko.isComputed = function (instance) {
return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
};
ko.isComputed = instance => (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
ko.isPureComputed = function (instance) {
return ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;
};
ko.isPureComputed = instance => ko.isComputed(instance) && instance[computedState] && instance[computedState].pure;
ko.exportSymbol('computed', ko.computed);
ko.exportSymbol('dependentObservable', ko.computed); // export ko.dependentObservable for backwards compatibility (1.x)
@ -529,13 +524,12 @@ ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);
ko.pureComputed = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget) {
ko.pureComputed = (evaluatorFunctionOrOptions, evaluatorFunctionTarget) => {
if (typeof evaluatorFunctionOrOptions === 'function') {
return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget, {'pure':true});
} else {
evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions); // make a copy of the parameter object
evaluatorFunctionOrOptions['pure'] = true;
return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
}
}
evaluatorFunctionOrOptions = ko.utils.extend({}, evaluatorFunctionOrOptions); // make a copy of the parameter object
evaluatorFunctionOrOptions['pure'] = true;
return ko.computed(evaluatorFunctionOrOptions, evaluatorFunctionTarget);
};
ko.exportSymbol('pureComputed', ko.pureComputed);

View file

@ -1,5 +1,5 @@
ko.extenders = {
'throttle': function(target, timeout) {
'throttle': (target, timeout) => {
// Throttling means two things:
// (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
@ -11,16 +11,14 @@ ko.extenders = {
var writeTimeoutInstance = null;
return ko.dependentObservable({
'read': target,
'write': function(value) {
'write': value => {
clearTimeout(writeTimeoutInstance);
writeTimeoutInstance = ko.utils.setTimeout(function() {
target(value);
}, timeout);
writeTimeoutInstance = ko.utils.setTimeout(() => target(value), timeout);
}
});
},
'rateLimit': function(target, options) {
'rateLimit': (target, options) => {
var timeout, method, limitFunction;
if (typeof options == 'number') {
@ -34,22 +32,20 @@ ko.extenders = {
target._deferUpdates = false;
limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ? debounce : throttle;
target.limit(function(callback) {
return limitFunction(callback, timeout, options);
});
target.limit(callback => limitFunction(callback, timeout, options));
},
'deferred': function(target, options) {
'deferred': (target, options) => {
if (options !== true) {
throw new Error('The \'deferred\' extender only accepts the value \'true\', because it is not supported to turn deferral off once enabled.')
}
if (!target._deferUpdates) {
target._deferUpdates = true;
target.limit(function (callback) {
target.limit(callback => {
var handle,
ignoreUpdates = false;
return function () {
return () => {
if (!ignoreUpdates) {
ko.tasks.cancel(handle);
handle = ko.tasks.schedule(callback);
@ -66,7 +62,7 @@ ko.extenders = {
}
},
'notify': function(target, notifyWhen) {
'notify': (target, notifyWhen) => {
target["equalityComparer"] = notifyWhen == "always" ?
null : // null equalityComparer means to always notify
valuesArePrimitiveAndEqual;
@ -81,9 +77,9 @@ function valuesArePrimitiveAndEqual(a, b) {
function throttle(callback, timeout) {
var timeoutInstance;
return function () {
return () => {
if (!timeoutInstance) {
timeoutInstance = ko.utils.setTimeout(function () {
timeoutInstance = ko.utils.setTimeout(() => {
timeoutInstance = undefined;
callback();
}, timeout);
@ -93,7 +89,7 @@ function throttle(callback, timeout) {
function debounce(callback, timeout) {
var timeoutInstance;
return function () {
return () => {
clearTimeout(timeoutInstance);
timeoutInstance = ko.utils.setTimeout(callback, timeout);
};
@ -102,7 +98,7 @@ function debounce(callback, timeout) {
function applyExtenders(requestedExtenders) {
var target = this;
if (requestedExtenders) {
ko.utils.objectForEach(requestedExtenders, function(key, value) {
ko.utils.objectForEach(requestedExtenders, (key, value) => {
var extenderHandler = ko.extenders[key];
if (typeof extenderHandler == 'function') {
target = extenderHandler(target, value) || target;

View file

@ -1,6 +1,6 @@
var observableLatestValue = Symbol('_latestValue');
ko.observable = function (initialValue) {
ko.observable = initialValue => {
function observable() {
if (arguments.length > 0) {
// Write
@ -59,7 +59,7 @@ if (ko.utils.canSetPrototype) {
var protoProperty = ko.observable.protoProperty = '__ko_proto__';
observableFn[protoProperty] = ko.observable;
ko.isObservable = function (instance) {
ko.isObservable = instance => {
var proto = typeof instance == 'function' && instance[protoProperty];
if (proto && proto !== observableFn[protoProperty] && proto !== ko.computed['fn'][protoProperty]) {
throw Error("Invalid object that looks like an observable; possibly from another Knockout instance");
@ -67,7 +67,7 @@ ko.isObservable = function (instance) {
return !!proto;
};
ko.isWriteableObservable = function (instance) {
ko.isWriteableObservable = instance => {
return (typeof instance == 'function' && (
(instance[protoProperty] === observableFn[protoProperty]) || // Observable
(instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction))); // Writable computed observable

View file

@ -1,5 +1,5 @@
var arrayChangeEventName = 'arrayChange';
ko.extenders['trackArrayChanges'] = function(target, options) {
ko.extenders['trackArrayChanges'] = (target, options) => {
// Use the provided options--each call to trackArrayChanges overwrites the previously set options
target.compareArrayOptions = {};
if (options && typeof options == "object") {
@ -21,7 +21,7 @@ ko.extenders['trackArrayChanges'] = function(target, options) {
underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
// Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
target.beforeSubscriptionAdd = function (event) {
target.beforeSubscriptionAdd = event => {
if (underlyingBeforeSubscriptionAddFunction) {
underlyingBeforeSubscriptionAddFunction.call(target, event);
}
@ -30,7 +30,7 @@ ko.extenders['trackArrayChanges'] = function(target, options) {
}
};
// Watch "dispose" calls, and for array change events, ensure change tracking is disabled when all are disposed
target.afterSubscriptionRemove = function (event) {
target.afterSubscriptionRemove = event => {
if (underlyingAfterSubscriptionRemoveFunction) {
underlyingAfterSubscriptionRemoveFunction.call(target, event);
}
@ -58,9 +58,7 @@ ko.extenders['trackArrayChanges'] = function(target, options) {
trackingChanges = true;
// Track how many times the array actually changed value
spectateSubscription = target.subscribe(function () {
++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
@ -102,7 +100,7 @@ ko.extenders['trackArrayChanges'] = function(target, options) {
return cachedDiff;
}
target.cacheDiffForKnownOperation = function(rawArray, operationName, args) {
target.cacheDiffForKnownOperation = (rawArray, operationName, args) => {
// Only run if we're currently tracking changes for this observable array
// and there aren't any pending deferred notifications.
if (!trackingChanges || pendingChanges) {

View file

@ -1,4 +1,4 @@
ko.observableArray = function (initialValues) {
ko.observableArray = initialValues => {
initialValues = initialValues || [];
if (typeof initialValues != 'object' || !('length' in initialValues))
@ -66,7 +66,7 @@ if (ko.utils.canSetPrototype) {
// Populate ko.observableArray.fn with read/write functions from native arrays
// Important: Do not add any additional functions here that may reasonably be used to *read* data from the array
// because we'll eval them without causing subscriptions, so ko.computed output could end up getting stale
ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], function (methodName) {
ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "unshift"], methodName => {
ko.observableArray['fn'][methodName] = function () {
// Use "peek" to avoid creating a subscription in any computed that we're executing in the context of
// (for consistency with mutating regular observables)
@ -81,14 +81,14 @@ ko.utils.arrayForEach(["pop", "push", "reverse", "shift", "sort", "splice", "uns
});
// Populate ko.observableArray.fn with read-only functions from native arrays
ko.utils.arrayForEach(["slice"], function (methodName) {
ko.utils.arrayForEach(["slice"], methodName => {
ko.observableArray['fn'][methodName] = function () {
var underlyingArray = this();
return underlyingArray[methodName].apply(underlyingArray, arguments);
};
});
ko.isObservableArray = function (instance) {
ko.isObservableArray = instance => {
return ko.isObservable(instance)
&& typeof instance["remove"] == "function"
&& typeof instance["push"] == "function";

View file

@ -1,7 +1,7 @@
ko.when = function(predicate, callback, context) {
ko.when = (predicate, callback, context) => {
function kowhen (resolve) {
var observable = ko.pureComputed(predicate, context).extend({notify:'always'});
var subscription = observable.subscribe(function(value) {
var subscription = observable.subscribe(value => {
if (value) {
subscription.dispose();
resolve(value);
@ -12,10 +12,7 @@ ko.when = function(predicate, callback, context) {
return subscription;
}
if (callback) {
return kowhen(callback.bind(context));
}
return new Promise(kowhen);
return callback ? kowhen(callback.bind(context)) : new Promise(kowhen);
};
ko.exportSymbol('when', ko.when);

View file

@ -45,7 +45,7 @@ function limitNotifySubscribers(value, event) {
}
var ko_subscribable_fn = {
init: function(instance) {
init: instance => {
instance._subscriptions = { "change": [] };
instance._versionNumber = 1;
},
@ -56,7 +56,7 @@ var ko_subscribable_fn = {
event = event || defaultEvent;
var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
var subscription = new ko.subscription(self, boundCallback, function () {
var subscription = new ko.subscription(self, boundCallback, () => {
ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
if (self.afterSubscriptionRemove)
self.afterSubscriptionRemove(event);
@ -115,7 +115,7 @@ var ko_subscribable_fn = {
self["notifySubscribers"] = limitNotifySubscribers;
}
var finish = limitFunction(function() {
var finish = limitFunction(() => {
self._notificationIsPending = false;
// If an observable provided a reference to itself, access it to get the latest value.
@ -132,7 +132,7 @@ var ko_subscribable_fn = {
}
});
self._limitChange = function(value, isDirty) {
self._limitChange = (value, isDirty) => {
if (!isDirty || !self._notificationIsPending) {
didUpdate = !isDirty;
}
@ -141,16 +141,16 @@ var ko_subscribable_fn = {
pendingValue = value;
finish();
};
self._limitBeforeChange = function(value) {
self._limitBeforeChange = value => {
if (!ignoreBeforeChange) {
previousValue = value;
self._origNotifySubscribers(value, beforeChange);
}
};
self._recordUpdate = function() {
self._recordUpdate = () => {
didUpdate = true;
};
self._notifyNextChangeIfValueIsDifferent = function() {
self._notifyNextChangeIfValueIsDifferent = () => {
if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
notifyNextChange = true;
}
@ -164,23 +164,20 @@ var ko_subscribable_fn = {
getSubscriptionsCount: function (event) {
if (event) {
return this._subscriptions[event] && this._subscriptions[event].length || 0;
} else {
var total = 0;
ko.utils.objectForEach(this._subscriptions, function(eventName, subscriptions) {
if (eventName !== 'dirty')
total += subscriptions.length;
});
return total;
}
var total = 0;
ko.utils.objectForEach(this._subscriptions, (eventName, subscriptions) => {
if (eventName !== 'dirty')
total += subscriptions.length;
});
return total;
},
isDifferent: function(oldValue, newValue) {
return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
},
toString: function() {
return '[object Object]'
},
toString: () => '[object Object]',
extend: applyExtenders
};
@ -200,9 +197,8 @@ if (ko.utils.canSetPrototype) {
ko.subscribable['fn'] = ko_subscribable_fn;
ko.isSubscribable = function (instance) {
return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
};
ko.isSubscribable = instance =>
instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
ko.exportSymbol('subscribable', ko.subscribable);
ko.exportSymbol('isSubscribable', ko.isSubscribable);