Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 37 additions & 26 deletions assets/knockout/src/subscribables/extenders.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
ko.extenders = {
'throttle': function(target, timeout) {
// Throttling means two things:
// Throttles the evaluation of dependent observables and the writes to writable targets.

// (1) For dependent observables, we throttle *evaluations* so that, no matter how fast its dependencies
// notify updates, the target doesn't re-evaluate (and hence doesn't notify) faster than a certain rate
// (1) Throttle the evaluations of dependent observables
target['throttleEvaluation'] = timeout;

// (2) For writable targets (observables, or writable dependent observables), we throttle *writes*
// so the target cannot change value synchronously or faster than a certain rate
var writeTimeoutInstance = null;
// (2) Throttle writes to writable observables
let writeTimeoutInstance = null;
return ko.dependentObservable({
'read': target,
'write': function(value) {
Expand All @@ -21,34 +19,41 @@ ko.extenders = {
},

'rateLimit': function(target, options) {
var timeout, method, limitFunction;
// Rate limits changes to the target observable, controlling the frequency of updates.

if (typeof options == 'number') {
let timeout, method, limitFunction;

if (typeof options === 'number') {
timeout = options;
} else {
timeout = options['timeout'];
method = options['method'];
}

// rateLimit supersedes deferred updates
// Prevent deferred updates when rateLimit is active
target._deferUpdates = false;

limitFunction = method == 'notifyWhenChangesStop' ? debounce : throttle;
// Choose the appropriate limiting function (debounce or throttle)
limitFunction = method === 'notifyWhenChangesStop' ? debounce : throttle;

// Apply the limit function to the target
target.limit(function(callback) {
return limitFunction(callback, timeout);
});
},

'deferred': function(target, options) {
// Enables deferred updates for the target observable. Throws error if 'true' isn't passed.

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.')
throw new Error("The 'deferred' extender only accepts the value 'true', as it cannot be turned off once enabled.");
}

if (!target._deferUpdates) {
target._deferUpdates = true;
target.limit(function (callback) {
var handle;
return function () {
target.limit(function(callback) {
let handle;
return function() {
ko.tasks.cancel(handle);
handle = ko.tasks.schedule(callback);
target['notifySubscribers'](undefined, 'dirty');
Expand All @@ -58,44 +63,50 @@ ko.extenders = {
},

'notify': function(target, notifyWhen) {
target["equalityComparer"] = notifyWhen == "always" ?
null : // null equalityComparer means to always notify
// Adjusts the target's equalityComparer function based on when it should notify subscribers.

target["equalityComparer"] = notifyWhen === "always" ?
null : // null equalityComparer means always notify
valuesArePrimitiveAndEqual;
}
};

var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };
// Helper function to compare primitive values
function valuesArePrimitiveAndEqual(a, b) {
var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
const primitiveTypes = { 'undefined': 1, 'boolean': 1, 'number': 1, 'string': 1 };
const oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
return oldValueIsPrimitive ? (a === b) : false;
}

// Throttle function implementation to limit the frequency of function calls
function throttle(callback, timeout) {
var timeoutInstance;
return function () {
let timeoutInstance;
return function() {
if (!timeoutInstance) {
timeoutInstance = ko.utils.setTimeout(function () {
timeoutInstance = ko.utils.setTimeout(function() {
timeoutInstance = undefined;
callback();
}, timeout);
}
};
}

// Debounce function implementation to delay the invocation of the callback
function debounce(callback, timeout) {
var timeoutInstance;
return function () {
let timeoutInstance;
return function() {
clearTimeout(timeoutInstance);
timeoutInstance = ko.utils.setTimeout(callback, timeout);
};
}

// Applies the requested extenders to the target observable
function applyExtenders(requestedExtenders) {
var target = this;
let target = this;
if (requestedExtenders) {
ko.utils.objectForEach(requestedExtenders, function(key, value) {
var extenderHandler = ko.extenders[key];
if (typeof extenderHandler == 'function') {
const extenderHandler = ko.extenders[key];
if (typeof extenderHandler === 'function') {
target = extenderHandler(target, value) || target;
}
});
Expand Down
Loading