mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-09-03 14:37:02 +03:00
Clone 3.5.1 from github
This commit is contained in:
parent
5d281486d0
commit
091c4ec811
65 changed files with 6957 additions and 7 deletions
76
vendors/knockout/src/subscribables/dependencyDetection.js
vendored
Normal file
76
vendors/knockout/src/subscribables/dependencyDetection.js
vendored
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
|
||||
ko.computedContext = ko.dependencyDetection = (function () {
|
||||
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;
|
||||
}
|
||||
|
||||
function end() {
|
||||
currentFrame = outerFrames.pop();
|
||||
}
|
||||
|
||||
return {
|
||||
begin: begin,
|
||||
|
||||
end: end,
|
||||
|
||||
registerDependency: function (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()));
|
||||
}
|
||||
},
|
||||
|
||||
ignore: function (callback, callbackTarget, callbackArgs) {
|
||||
try {
|
||||
begin();
|
||||
return callback.apply(callbackTarget, callbackArgs || []);
|
||||
} finally {
|
||||
end();
|
||||
}
|
||||
},
|
||||
|
||||
getDependenciesCount: function () {
|
||||
if (currentFrame)
|
||||
return currentFrame.computed.getDependenciesCount();
|
||||
},
|
||||
|
||||
getDependencies: function () {
|
||||
if (currentFrame)
|
||||
return currentFrame.computed.getDependencies();
|
||||
},
|
||||
|
||||
isInitial: function() {
|
||||
if (currentFrame)
|
||||
return currentFrame.isInitial;
|
||||
},
|
||||
|
||||
computed: function() {
|
||||
if (currentFrame)
|
||||
return currentFrame.computed;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
ko.exportSymbol('computedContext', ko.computedContext);
|
||||
ko.exportSymbol('computedContext.getDependenciesCount', ko.computedContext.getDependenciesCount);
|
||||
ko.exportSymbol('computedContext.getDependencies', ko.computedContext.getDependencies);
|
||||
ko.exportSymbol('computedContext.isInitial', ko.computedContext.isInitial);
|
||||
ko.exportSymbol('computedContext.registerDependency', ko.computedContext.registerDependency);
|
||||
|
||||
ko.exportSymbol('ignoreDependencies', ko.ignoreDependencies = ko.dependencyDetection.ignore);
|
||||
541
vendors/knockout/src/subscribables/dependentObservable.js
vendored
Normal file
541
vendors/knockout/src/subscribables/dependentObservable.js
vendored
Normal file
|
|
@ -0,0 +1,541 @@
|
|||
var computedState = ko.utils.createSymbolOrString('_state');
|
||||
|
||||
ko.computed = ko.dependentObservable = function (evaluatorFunctionOrOptions, evaluatorFunctionTarget, options) {
|
||||
if (typeof evaluatorFunctionOrOptions === "object") {
|
||||
// Single-parameter syntax - everything is on this "options" param
|
||||
options = evaluatorFunctionOrOptions;
|
||||
} else {
|
||||
// Multi-parameter syntax - construct the options according to the params passed
|
||||
options = options || {};
|
||||
if (evaluatorFunctionOrOptions) {
|
||||
options["read"] = evaluatorFunctionOrOptions;
|
||||
}
|
||||
}
|
||||
if (typeof options["read"] != "function")
|
||||
throw Error("Pass a function that returns the value of the ko.computed");
|
||||
|
||||
var writeFunction = options["write"];
|
||||
var state = {
|
||||
latestValue: undefined,
|
||||
isStale: true,
|
||||
isDirty: true,
|
||||
isBeingEvaluated: false,
|
||||
suppressDisposalUntilDisposeWhenReturnsFalse: false,
|
||||
isDisposed: false,
|
||||
pure: false,
|
||||
isSleeping: false,
|
||||
readFunction: options["read"],
|
||||
evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
|
||||
disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
|
||||
disposeWhen: options["disposeWhen"] || options.disposeWhen,
|
||||
domNodeDisposalCallback: null,
|
||||
dependencyTracking: {},
|
||||
dependenciesCount: 0,
|
||||
evaluationTimeoutInstance: null
|
||||
};
|
||||
|
||||
function computedObservable() {
|
||||
if (arguments.length > 0) {
|
||||
if (typeof writeFunction === "function") {
|
||||
// Writing a value
|
||||
writeFunction.apply(state.evaluatorFunctionTarget, arguments);
|
||||
} else {
|
||||
throw new Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
|
||||
}
|
||||
return this; // Permits chained assignments
|
||||
} else {
|
||||
// Reading the value
|
||||
if (!state.isDisposed) {
|
||||
ko.dependencyDetection.registerDependency(computedObservable);
|
||||
}
|
||||
if (state.isDirty || (state.isSleeping && computedObservable.haveDependenciesChanged())) {
|
||||
computedObservable.evaluateImmediate();
|
||||
}
|
||||
return state.latestValue;
|
||||
}
|
||||
}
|
||||
|
||||
computedObservable[computedState] = state;
|
||||
computedObservable.hasWriteFunction = typeof writeFunction === "function";
|
||||
|
||||
// Inherit from 'subscribable'
|
||||
if (!ko.utils.canSetPrototype) {
|
||||
// 'subscribable' won't be on the prototype chain unless we put it there directly
|
||||
ko.utils.extend(computedObservable, ko.subscribable['fn']);
|
||||
}
|
||||
ko.subscribable['fn'].init(computedObservable);
|
||||
|
||||
// Inherit from 'computed'
|
||||
ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);
|
||||
|
||||
if (options['pure']) {
|
||||
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 (ko.options['deferUpdates']) {
|
||||
ko.extenders['deferred'](computedObservable, true);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
// #1731 - Aid debugging by exposing the computed's options
|
||||
computedObservable["_options"] = options;
|
||||
}
|
||||
|
||||
if (state.disposeWhenNodeIsRemoved) {
|
||||
// Since this computed is associated with a DOM node, and we don't want to dispose the computed
|
||||
// until the DOM node is *removed* from the document (as opposed to never having been in the document),
|
||||
// we'll prevent disposal until "disposeWhen" first returns false.
|
||||
state.suppressDisposalUntilDisposeWhenReturnsFalse = true;
|
||||
|
||||
// disposeWhenNodeIsRemoved: true can be used to opt into the "only dispose after first false result"
|
||||
// behaviour even if there's no specific node to watch. In that case, clear the option so we don't try
|
||||
// to watch for a non-node's disposal. This technique is intended for KO's internal use only and shouldn't
|
||||
// be documented or used by application code, as it's likely to change in a future version of KO.
|
||||
if (!state.disposeWhenNodeIsRemoved.nodeType) {
|
||||
state.disposeWhenNodeIsRemoved = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Evaluate, unless sleeping or deferEvaluation is true
|
||||
if (!state.isSleeping && !options['deferEvaluation']) {
|
||||
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).
|
||||
if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
|
||||
computedObservable.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
return computedObservable;
|
||||
};
|
||||
|
||||
// Utility function that disposes a given dependencyTracking entry
|
||||
function computedDisposeDependencyCallback(id, entryToDispose) {
|
||||
if (entryToDispose !== null && entryToDispose.dispose) {
|
||||
entryToDispose.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
// This function gets called each time a dependency is detected while evaluating a computed.
|
||||
// It's factored out as a shared function to avoid creating unnecessary function instances during evaluation.
|
||||
function computedBeginDependencyDetectionCallback(subscribable, id) {
|
||||
var computedObservable = this.computedObservable,
|
||||
state = computedObservable[computedState];
|
||||
if (!state.isDisposed) {
|
||||
if (this.disposalCount && this.disposalCandidates[id]) {
|
||||
// Don't want to dispose this subscription, as it's still being used
|
||||
computedObservable.addDependencyTracking(id, subscribable, this.disposalCandidates[id]);
|
||||
this.disposalCandidates[id] = null; // No need to actually delete the property - disposalCandidates is a transient object anyway
|
||||
--this.disposalCount;
|
||||
} else if (!state.dependencyTracking[id]) {
|
||||
// Brand new subscription - add it
|
||||
computedObservable.addDependencyTracking(id, subscribable, state.isSleeping ? { _target: subscribable } : computedObservable.subscribeToDependency(subscribable));
|
||||
}
|
||||
// If the observable we've accessed has a pending notification, ensure we get notified of the actual final value (bypass equality checks)
|
||||
if (subscribable._notificationIsPending) {
|
||||
subscribable._notifyNextChangeIfValueIsDifferent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var computedFn = {
|
||||
"equalityComparer": valuesArePrimitiveAndEqual,
|
||||
getDependenciesCount: function () {
|
||||
return this[computedState].dependenciesCount;
|
||||
},
|
||||
getDependencies: function () {
|
||||
var dependencyTracking = this[computedState].dependencyTracking, dependentObservables = [];
|
||||
|
||||
ko.utils.objectForEach(dependencyTracking, function (id, dependency) {
|
||||
dependentObservables[dependency._order] = dependency._target;
|
||||
});
|
||||
|
||||
return dependentObservables;
|
||||
},
|
||||
hasAncestorDependency: function (obs) {
|
||||
if (!this[computedState].dependenciesCount) {
|
||||
return false;
|
||||
}
|
||||
var dependencies = this.getDependencies();
|
||||
if (ko.utils.arrayIndexOf(dependencies, obs) !== -1) {
|
||||
return true;
|
||||
}
|
||||
return !!ko.utils.arrayFirst(dependencies, function (dep) {
|
||||
return dep.hasAncestorDependency && dep.hasAncestorDependency(obs);
|
||||
});
|
||||
},
|
||||
addDependencyTracking: function (id, target, trackingObj) {
|
||||
if (this[computedState].pure && target === this) {
|
||||
throw Error("A 'pure' computed must not be called recursively");
|
||||
}
|
||||
|
||||
this[computedState].dependencyTracking[id] = trackingObj;
|
||||
trackingObj._order = this[computedState].dependenciesCount++;
|
||||
trackingObj._version = target.getVersion();
|
||||
},
|
||||
haveDependenciesChanged: function () {
|
||||
var id, dependency, dependencyTracking = this[computedState].dependencyTracking;
|
||||
for (id in dependencyTracking) {
|
||||
if (Object.prototype.hasOwnProperty.call(dependencyTracking, id)) {
|
||||
dependency = dependencyTracking[id];
|
||||
if ((this._evalDelayed && dependency._target._notificationIsPending) || dependency._target.hasChanged(dependency._version)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
markDirty: function () {
|
||||
// Process "dirty" events if we can handle delayed notifications
|
||||
if (this._evalDelayed && !this[computedState].isBeingEvaluated) {
|
||||
this._evalDelayed(false /*isChange*/);
|
||||
}
|
||||
},
|
||||
isActive: function () {
|
||||
var state = this[computedState];
|
||||
return state.isDirty || state.dependenciesCount > 0;
|
||||
},
|
||||
respondToChange: function () {
|
||||
// Ignore "change" events if we've already scheduled a delayed notification
|
||||
if (!this._notificationIsPending) {
|
||||
this.evaluatePossiblyAsync();
|
||||
} else if (this[computedState].isDirty) {
|
||||
this[computedState].isStale = true;
|
||||
}
|
||||
},
|
||||
subscribeToDependency: function (target) {
|
||||
if (target._deferUpdates) {
|
||||
var dirtySub = target.subscribe(this.markDirty, this, 'dirty'),
|
||||
changeSub = target.subscribe(this.respondToChange, this);
|
||||
return {
|
||||
_target: target,
|
||||
dispose: function () {
|
||||
dirtySub.dispose();
|
||||
changeSub.dispose();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return target.subscribe(this.evaluatePossiblyAsync, this);
|
||||
}
|
||||
},
|
||||
evaluatePossiblyAsync: function () {
|
||||
var computedObservable = this,
|
||||
throttleEvaluationTimeout = computedObservable['throttleEvaluation'];
|
||||
if (throttleEvaluationTimeout && throttleEvaluationTimeout >= 0) {
|
||||
clearTimeout(this[computedState].evaluationTimeoutInstance);
|
||||
this[computedState].evaluationTimeoutInstance = ko.utils.setTimeout(function () {
|
||||
computedObservable.evaluateImmediate(true /*notifyChange*/);
|
||||
}, throttleEvaluationTimeout);
|
||||
} else if (computedObservable._evalDelayed) {
|
||||
computedObservable._evalDelayed(true /*isChange*/);
|
||||
} else {
|
||||
computedObservable.evaluateImmediate(true /*notifyChange*/);
|
||||
}
|
||||
},
|
||||
evaluateImmediate: function (notifyChange) {
|
||||
var computedObservable = this,
|
||||
state = computedObservable[computedState],
|
||||
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;
|
||||
}
|
||||
|
||||
// Do not evaluate (and possibly capture new dependencies) if disposed
|
||||
if (state.isDisposed) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.disposeWhenNodeIsRemoved && !ko.utils.domNodeIsAttachedToDocument(state.disposeWhenNodeIsRemoved) || disposeWhen && disposeWhen()) {
|
||||
// See comment above about suppressDisposalUntilDisposeWhenReturnsFalse
|
||||
if (!state.suppressDisposalUntilDisposeWhenReturnsFalse) {
|
||||
computedObservable.dispose();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// It just did return false, so we can stop suppressing now
|
||||
state.suppressDisposalUntilDisposeWhenReturnsFalse = false;
|
||||
}
|
||||
|
||||
state.isBeingEvaluated = true;
|
||||
try {
|
||||
changed = this.evaluateImmediate_CallReadWithDependencyDetection(notifyChange);
|
||||
} finally {
|
||||
state.isBeingEvaluated = false;
|
||||
}
|
||||
|
||||
return changed;
|
||||
},
|
||||
evaluateImmediate_CallReadWithDependencyDetection: function (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).
|
||||
|
||||
var computedObservable = this,
|
||||
state = computedObservable[computedState],
|
||||
changed = false;
|
||||
|
||||
// Initially, we assume that none of the subscriptions are still being used (i.e., all are candidates for disposal).
|
||||
// Then, during evaluation, we cross off any that are in fact still being used.
|
||||
var isInitial = state.pure ? undefined : !state.dependenciesCount, // If we're evaluating when there are no previous dependencies, it must be the first time
|
||||
dependencyDetectionContext = {
|
||||
computedObservable: computedObservable,
|
||||
disposalCandidates: state.dependencyTracking,
|
||||
disposalCount: state.dependenciesCount
|
||||
};
|
||||
|
||||
ko.dependencyDetection.begin({
|
||||
callbackTarget: dependencyDetectionContext,
|
||||
callback: computedBeginDependencyDetectionCallback,
|
||||
computed: computedObservable,
|
||||
isInitial: isInitial
|
||||
});
|
||||
|
||||
state.dependencyTracking = {};
|
||||
state.dependenciesCount = 0;
|
||||
|
||||
var newValue = this.evaluateImmediate_CallReadThenEndDependencyDetection(state, dependencyDetectionContext);
|
||||
|
||||
if (!state.dependenciesCount) {
|
||||
computedObservable.dispose();
|
||||
changed = true; // When evaluation causes a disposal, make sure all dependent computeds get notified so they'll see the new state
|
||||
} else {
|
||||
changed = computedObservable.isDifferent(state.latestValue, newValue);
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
if (!state.isSleeping) {
|
||||
computedObservable["notifySubscribers"](state.latestValue, "beforeChange");
|
||||
} else {
|
||||
computedObservable.updateVersion();
|
||||
}
|
||||
|
||||
state.latestValue = newValue;
|
||||
if (DEBUG) computedObservable._latestValue = newValue;
|
||||
|
||||
computedObservable["notifySubscribers"](state.latestValue, "spectate");
|
||||
|
||||
if (!state.isSleeping && notifyChange) {
|
||||
computedObservable["notifySubscribers"](state.latestValue);
|
||||
}
|
||||
if (computedObservable._recordUpdate) {
|
||||
computedObservable._recordUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
if (isInitial) {
|
||||
computedObservable["notifySubscribers"](state.latestValue, "awake");
|
||||
}
|
||||
|
||||
return changed;
|
||||
},
|
||||
evaluateImmediate_CallReadThenEndDependencyDetection: function (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
|
||||
// overhead of computed evaluation (on V8 at least).
|
||||
|
||||
try {
|
||||
var readFunction = state.readFunction;
|
||||
return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
|
||||
} finally {
|
||||
ko.dependencyDetection.end();
|
||||
|
||||
// For each subscription no longer being used, remove it from the active subscriptions list and dispose it
|
||||
if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
|
||||
ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
|
||||
}
|
||||
|
||||
state.isStale = state.isDirty = false;
|
||||
}
|
||||
},
|
||||
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.
|
||||
// Pass in true to evaluate if needed.
|
||||
var state = this[computedState];
|
||||
if ((state.isDirty && (evaluate || !state.dependenciesCount)) || (state.isSleeping && this.haveDependenciesChanged())) {
|
||||
this.evaluateImmediate();
|
||||
}
|
||||
return state.latestValue;
|
||||
},
|
||||
limit: function (limitFunction) {
|
||||
// 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();
|
||||
} else {
|
||||
this[computedState].isDirty = false;
|
||||
}
|
||||
}
|
||||
return this[computedState].latestValue;
|
||||
};
|
||||
this._evalDelayed = function (isChange) {
|
||||
this._limitBeforeChange(this[computedState].latestValue);
|
||||
|
||||
// Mark as dirty
|
||||
this[computedState].isDirty = true;
|
||||
if (isChange) {
|
||||
this[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 */);
|
||||
};
|
||||
},
|
||||
dispose: function () {
|
||||
var state = this[computedState];
|
||||
if (!state.isSleeping && state.dependencyTracking) {
|
||||
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
||||
if (dependency.dispose)
|
||||
dependency.dispose();
|
||||
});
|
||||
}
|
||||
if (state.disposeWhenNodeIsRemoved && state.domNodeDisposalCallback) {
|
||||
ko.utils.domNodeDisposal.removeDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback);
|
||||
}
|
||||
state.dependencyTracking = undefined;
|
||||
state.dependenciesCount = 0;
|
||||
state.isDisposed = true;
|
||||
state.isStale = false;
|
||||
state.isDirty = false;
|
||||
state.isSleeping = false;
|
||||
state.disposeWhenNodeIsRemoved = undefined;
|
||||
state.disposeWhen = undefined;
|
||||
state.readFunction = undefined;
|
||||
if (!this.hasWriteFunction) {
|
||||
state.evaluatorFunctionTarget = undefined;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var pureComputedOverrides = {
|
||||
beforeSubscriptionAdd: function (event) {
|
||||
// If asleep, wake up the computed by subscribing to any dependencies.
|
||||
var computedObservable = this,
|
||||
state = computedObservable[computedState];
|
||||
if (!state.isDisposed && state.isSleeping && event == 'change') {
|
||||
state.isSleeping = false;
|
||||
if (state.isStale || computedObservable.haveDependenciesChanged()) {
|
||||
state.dependencyTracking = null;
|
||||
state.dependenciesCount = 0;
|
||||
if (computedObservable.evaluateImmediate()) {
|
||||
computedObservable.updateVersion();
|
||||
}
|
||||
} else {
|
||||
// First put the dependencies in order
|
||||
var dependenciesOrder = [];
|
||||
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
||||
dependenciesOrder[dependency._order] = id;
|
||||
});
|
||||
// Next, subscribe to each one
|
||||
ko.utils.arrayForEach(dependenciesOrder, function (id, order) {
|
||||
var dependency = state.dependencyTracking[id],
|
||||
subscription = computedObservable.subscribeToDependency(dependency._target);
|
||||
subscription._order = order;
|
||||
subscription._version = dependency._version;
|
||||
state.dependencyTracking[id] = subscription;
|
||||
});
|
||||
// Waking dependencies may have triggered effects
|
||||
if (computedObservable.haveDependenciesChanged()) {
|
||||
if (computedObservable.evaluateImmediate()) {
|
||||
computedObservable.updateVersion();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!state.isDisposed) { // test since evaluating could trigger disposal
|
||||
computedObservable["notifySubscribers"](state.latestValue, "awake");
|
||||
}
|
||||
}
|
||||
},
|
||||
afterSubscriptionRemove: function (event) {
|
||||
var state = this[computedState];
|
||||
if (!state.isDisposed && event == 'change' && !this.hasSubscriptionsForEvent('change')) {
|
||||
ko.utils.objectForEach(state.dependencyTracking, function (id, dependency) {
|
||||
if (dependency.dispose) {
|
||||
state.dependencyTracking[id] = {
|
||||
_target: dependency._target,
|
||||
_order: dependency._order,
|
||||
_version: dependency._version
|
||||
};
|
||||
dependency.dispose();
|
||||
}
|
||||
});
|
||||
state.isSleeping = true;
|
||||
this["notifySubscribers"](undefined, "asleep");
|
||||
}
|
||||
},
|
||||
getVersion: function () {
|
||||
// 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.
|
||||
var state = this[computedState];
|
||||
if (state.isSleeping && (state.isStale || this.haveDependenciesChanged())) {
|
||||
this.evaluateImmediate();
|
||||
}
|
||||
return ko.subscribable['fn'].getVersion.call(this);
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
if (ko.utils.canSetPrototype) {
|
||||
ko.utils.setPrototypeOf(computedFn, ko.subscribable['fn']);
|
||||
}
|
||||
|
||||
// Set the proto values for ko.computed
|
||||
var protoProp = ko.observable.protoProperty; // == "__ko_proto__"
|
||||
computedFn[protoProp] = ko.computed;
|
||||
|
||||
ko.isComputed = function (instance) {
|
||||
return (typeof instance == 'function' && instance[protoProp] === computedFn[protoProp]);
|
||||
};
|
||||
|
||||
ko.isPureComputed = function (instance) {
|
||||
return 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)
|
||||
ko.exportSymbol('isComputed', ko.isComputed);
|
||||
ko.exportSymbol('isPureComputed', ko.isPureComputed);
|
||||
ko.exportSymbol('computed.fn', computedFn);
|
||||
ko.exportProperty(computedFn, 'peek', computedFn.peek);
|
||||
ko.exportProperty(computedFn, 'dispose', computedFn.dispose);
|
||||
ko.exportProperty(computedFn, 'isActive', computedFn.isActive);
|
||||
ko.exportProperty(computedFn, 'getDependenciesCount', computedFn.getDependenciesCount);
|
||||
ko.exportProperty(computedFn, 'getDependencies', computedFn.getDependencies);
|
||||
|
||||
ko.pureComputed = function (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);
|
||||
}
|
||||
}
|
||||
ko.exportSymbol('pureComputed', ko.pureComputed);
|
||||
115
vendors/knockout/src/subscribables/extenders.js
vendored
Normal file
115
vendors/knockout/src/subscribables/extenders.js
vendored
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
ko.extenders = {
|
||||
'throttle': function(target, timeout) {
|
||||
// Throttling means two things:
|
||||
|
||||
// (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
|
||||
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;
|
||||
return ko.dependentObservable({
|
||||
'read': target,
|
||||
'write': function(value) {
|
||||
clearTimeout(writeTimeoutInstance);
|
||||
writeTimeoutInstance = ko.utils.setTimeout(function() {
|
||||
target(value);
|
||||
}, timeout);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
'rateLimit': function(target, options) {
|
||||
var timeout, method, limitFunction;
|
||||
|
||||
if (typeof options == 'number') {
|
||||
timeout = options;
|
||||
} else {
|
||||
timeout = options['timeout'];
|
||||
method = options['method'];
|
||||
}
|
||||
|
||||
// rateLimit supersedes deferred updates
|
||||
target._deferUpdates = false;
|
||||
|
||||
limitFunction = typeof method == 'function' ? method : method == 'notifyWhenChangesStop' ? debounce : throttle;
|
||||
target.limit(function(callback) {
|
||||
return limitFunction(callback, timeout, options);
|
||||
});
|
||||
},
|
||||
|
||||
'deferred': function(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) {
|
||||
var handle,
|
||||
ignoreUpdates = false;
|
||||
return function () {
|
||||
if (!ignoreUpdates) {
|
||||
ko.tasks.cancel(handle);
|
||||
handle = ko.tasks.schedule(callback);
|
||||
|
||||
try {
|
||||
ignoreUpdates = true;
|
||||
target['notifySubscribers'](undefined, 'dirty');
|
||||
} finally {
|
||||
ignoreUpdates = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
'notify': function(target, notifyWhen) {
|
||||
target["equalityComparer"] = notifyWhen == "always" ?
|
||||
null : // null equalityComparer means to always notify
|
||||
valuesArePrimitiveAndEqual;
|
||||
}
|
||||
};
|
||||
|
||||
var primitiveTypes = { 'undefined':1, 'boolean':1, 'number':1, 'string':1 };
|
||||
function valuesArePrimitiveAndEqual(a, b) {
|
||||
var oldValueIsPrimitive = (a === null) || (typeof(a) in primitiveTypes);
|
||||
return oldValueIsPrimitive ? (a === b) : false;
|
||||
}
|
||||
|
||||
function throttle(callback, timeout) {
|
||||
var timeoutInstance;
|
||||
return function () {
|
||||
if (!timeoutInstance) {
|
||||
timeoutInstance = ko.utils.setTimeout(function () {
|
||||
timeoutInstance = undefined;
|
||||
callback();
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function debounce(callback, timeout) {
|
||||
var timeoutInstance;
|
||||
return function () {
|
||||
clearTimeout(timeoutInstance);
|
||||
timeoutInstance = ko.utils.setTimeout(callback, timeout);
|
||||
};
|
||||
}
|
||||
|
||||
function applyExtenders(requestedExtenders) {
|
||||
var target = this;
|
||||
if (requestedExtenders) {
|
||||
ko.utils.objectForEach(requestedExtenders, function(key, value) {
|
||||
var extenderHandler = ko.extenders[key];
|
||||
if (typeof extenderHandler == 'function') {
|
||||
target = extenderHandler(target, value) || target;
|
||||
}
|
||||
});
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
ko.exportSymbol('extenders', ko.extenders);
|
||||
96
vendors/knockout/src/subscribables/mappingHelpers.js
vendored
Normal file
96
vendors/knockout/src/subscribables/mappingHelpers.js
vendored
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
|
||||
(function() {
|
||||
var maxNestedObservableDepth = 10; // Escape the (unlikely) pathological case where an observable's current value is itself (or similar reference cycle)
|
||||
|
||||
ko.toJS = function(rootObject) {
|
||||
if (arguments.length == 0)
|
||||
throw new Error("When calling ko.toJS, pass the object you want to convert.");
|
||||
|
||||
// We just unwrap everything at every level in the object graph
|
||||
return mapJsObjectGraph(rootObject, function(valueToMap) {
|
||||
// Loop because an observable's value might in turn be another observable wrapper
|
||||
for (var i = 0; ko.isObservable(valueToMap) && (i < maxNestedObservableDepth); i++)
|
||||
valueToMap = valueToMap();
|
||||
return valueToMap;
|
||||
});
|
||||
};
|
||||
|
||||
ko.toJSON = function(rootObject, replacer, space) { // replacer and space are optional
|
||||
var plainJavaScriptObject = ko.toJS(rootObject);
|
||||
return ko.utils.stringifyJson(plainJavaScriptObject, replacer, space);
|
||||
};
|
||||
|
||||
function mapJsObjectGraph(rootObject, mapInputCallback, visitedObjects) {
|
||||
visitedObjects = visitedObjects || new objectLookup();
|
||||
|
||||
rootObject = mapInputCallback(rootObject);
|
||||
var canHaveProperties = (typeof rootObject == "object") && (rootObject !== null) && (rootObject !== undefined) && (!(rootObject instanceof RegExp)) && (!(rootObject instanceof Date)) && (!(rootObject instanceof String)) && (!(rootObject instanceof Number)) && (!(rootObject instanceof Boolean));
|
||||
if (!canHaveProperties)
|
||||
return rootObject;
|
||||
|
||||
var outputProperties = rootObject instanceof Array ? [] : {};
|
||||
visitedObjects.save(rootObject, outputProperties);
|
||||
|
||||
visitPropertiesOrArrayEntries(rootObject, function(indexer) {
|
||||
var propertyValue = mapInputCallback(rootObject[indexer]);
|
||||
|
||||
switch (typeof propertyValue) {
|
||||
case "boolean":
|
||||
case "number":
|
||||
case "string":
|
||||
case "function":
|
||||
outputProperties[indexer] = propertyValue;
|
||||
break;
|
||||
case "object":
|
||||
case "undefined":
|
||||
var previouslyMappedValue = visitedObjects.get(propertyValue);
|
||||
outputProperties[indexer] = (previouslyMappedValue !== undefined)
|
||||
? previouslyMappedValue
|
||||
: mapJsObjectGraph(propertyValue, mapInputCallback, visitedObjects);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return outputProperties;
|
||||
}
|
||||
|
||||
function visitPropertiesOrArrayEntries(rootObject, visitorCallback) {
|
||||
if (rootObject instanceof Array) {
|
||||
for (var i = 0; i < rootObject.length; i++)
|
||||
visitorCallback(i);
|
||||
|
||||
// For arrays, also respect toJSON property for custom mappings (fixes #278)
|
||||
if (typeof rootObject['toJSON'] == 'function')
|
||||
visitorCallback('toJSON');
|
||||
} else {
|
||||
for (var propertyName in rootObject) {
|
||||
visitorCallback(propertyName);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function objectLookup() {
|
||||
this.keys = [];
|
||||
this.values = [];
|
||||
};
|
||||
|
||||
objectLookup.prototype = {
|
||||
constructor: objectLookup,
|
||||
save: function(key, value) {
|
||||
var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
|
||||
if (existingIndex >= 0)
|
||||
this.values[existingIndex] = value;
|
||||
else {
|
||||
this.keys.push(key);
|
||||
this.values.push(value);
|
||||
}
|
||||
},
|
||||
get: function(key) {
|
||||
var existingIndex = ko.utils.arrayIndexOf(this.keys, key);
|
||||
return (existingIndex >= 0) ? this.values[existingIndex] : undefined;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
ko.exportSymbol('toJS', ko.toJS);
|
||||
ko.exportSymbol('toJSON', ko.toJSON);
|
||||
83
vendors/knockout/src/subscribables/observable.js
vendored
Normal file
83
vendors/knockout/src/subscribables/observable.js
vendored
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
var observableLatestValue = ko.utils.createSymbolOrString('_latestValue');
|
||||
|
||||
ko.observable = function (initialValue) {
|
||||
function observable() {
|
||||
if (arguments.length > 0) {
|
||||
// Write
|
||||
|
||||
// Ignore writes if the value hasn't changed
|
||||
if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
|
||||
observable.valueWillMutate();
|
||||
observable[observableLatestValue] = arguments[0];
|
||||
observable.valueHasMutated();
|
||||
}
|
||||
return this; // Permits chained assignments
|
||||
}
|
||||
else {
|
||||
// Read
|
||||
ko.dependencyDetection.registerDependency(observable); // The caller only needs to be notified of changes if they did a "read" operation
|
||||
return observable[observableLatestValue];
|
||||
}
|
||||
}
|
||||
|
||||
observable[observableLatestValue] = initialValue;
|
||||
|
||||
// Inherit from 'subscribable'
|
||||
if (!ko.utils.canSetPrototype) {
|
||||
// 'subscribable' won't be on the prototype chain unless we put it there directly
|
||||
ko.utils.extend(observable, ko.subscribable['fn']);
|
||||
}
|
||||
ko.subscribable['fn'].init(observable);
|
||||
|
||||
// Inherit from 'observable'
|
||||
ko.utils.setPrototypeOfOrExtend(observable, observableFn);
|
||||
|
||||
if (ko.options['deferUpdates']) {
|
||||
ko.extenders['deferred'](observable, true);
|
||||
}
|
||||
|
||||
return observable;
|
||||
}
|
||||
|
||||
// Define prototype for observables
|
||||
var observableFn = {
|
||||
'equalityComparer': valuesArePrimitiveAndEqual,
|
||||
peek: function() { return this[observableLatestValue]; },
|
||||
valueHasMutated: function () {
|
||||
this['notifySubscribers'](this[observableLatestValue], 'spectate');
|
||||
this['notifySubscribers'](this[observableLatestValue]);
|
||||
},
|
||||
valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
|
||||
};
|
||||
|
||||
// Note that for browsers that don't support proto assignment, the
|
||||
// inheritance chain is created manually in the ko.observable constructor
|
||||
if (ko.utils.canSetPrototype) {
|
||||
ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
|
||||
}
|
||||
|
||||
var protoProperty = ko.observable.protoProperty = '__ko_proto__';
|
||||
observableFn[protoProperty] = ko.observable;
|
||||
|
||||
ko.isObservable = function (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");
|
||||
}
|
||||
return !!proto;
|
||||
};
|
||||
|
||||
ko.isWriteableObservable = function (instance) {
|
||||
return (typeof instance == 'function' && (
|
||||
(instance[protoProperty] === observableFn[protoProperty]) || // Observable
|
||||
(instance[protoProperty] === ko.computed['fn'][protoProperty] && instance.hasWriteFunction))); // Writable computed observable
|
||||
};
|
||||
|
||||
ko.exportSymbol('observable', ko.observable);
|
||||
ko.exportSymbol('isObservable', ko.isObservable);
|
||||
ko.exportSymbol('isWriteableObservable', ko.isWriteableObservable);
|
||||
ko.exportSymbol('isWritableObservable', ko.isWriteableObservable);
|
||||
ko.exportSymbol('observable.fn', observableFn);
|
||||
ko.exportProperty(observableFn, 'peek', observableFn.peek);
|
||||
ko.exportProperty(observableFn, 'valueHasMutated', observableFn.valueHasMutated);
|
||||
ko.exportProperty(observableFn, 'valueWillMutate', observableFn.valueWillMutate);
|
||||
158
vendors/knockout/src/subscribables/observableArray.changeTracking.js
vendored
Normal file
158
vendors/knockout/src/subscribables/observableArray.changeTracking.js
vendored
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
var arrayChangeEventName = 'arrayChange';
|
||||
ko.extenders['trackArrayChanges'] = function(target, options) {
|
||||
// Use the provided options--each call to trackArrayChanges overwrites the previously set options
|
||||
target.compareArrayOptions = {};
|
||||
if (options && typeof options == "object") {
|
||||
ko.utils.extend(target.compareArrayOptions, options);
|
||||
}
|
||||
target.compareArrayOptions['sparse'] = true;
|
||||
|
||||
// Only modify the target observable once
|
||||
if (target.cacheDiffForKnownOperation) {
|
||||
return;
|
||||
}
|
||||
var trackingChanges = false,
|
||||
cachedDiff = null,
|
||||
changeSubscription,
|
||||
spectateSubscription,
|
||||
pendingChanges = 0,
|
||||
previousContents,
|
||||
underlyingBeforeSubscriptionAddFunction = target.beforeSubscriptionAdd,
|
||||
underlyingAfterSubscriptionRemoveFunction = target.afterSubscriptionRemove;
|
||||
|
||||
// Watch "subscribe" calls, and for array change events, ensure change tracking is enabled
|
||||
target.beforeSubscriptionAdd = function (event) {
|
||||
if (underlyingBeforeSubscriptionAddFunction) {
|
||||
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 = function (event) {
|
||||
if (underlyingAfterSubscriptionRemoveFunction) {
|
||||
underlyingAfterSubscriptionRemoveFunction.call(target, event);
|
||||
}
|
||||
if (event === arrayChangeEventName && !target.hasSubscriptionsForEvent(arrayChangeEventName)) {
|
||||
if (changeSubscription) {
|
||||
changeSubscription.dispose();
|
||||
}
|
||||
if (spectateSubscription) {
|
||||
spectateSubscription.dispose();
|
||||
}
|
||||
spectateSubscription = changeSubscription = null;
|
||||
trackingChanges = false;
|
||||
previousContents = undefined;
|
||||
}
|
||||
};
|
||||
|
||||
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(function () {
|
||||
++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) {
|
||||
// Make a copy of the current contents and ensure it's an array
|
||||
var currentContents = [].concat(target.peek() || []), changes;
|
||||
|
||||
// Compute the diff and issue notifications, but only if someone is listening
|
||||
if (target.hasSubscriptionsForEvent(arrayChangeEventName)) {
|
||||
changes = getChanges(previousContents, currentContents);
|
||||
}
|
||||
|
||||
// Eliminate references to the old, removed items, so they can be GCed
|
||||
previousContents = currentContents;
|
||||
cachedDiff = null;
|
||||
pendingChanges = 0;
|
||||
|
||||
if (changes && changes.length) {
|
||||
target['notifySubscribers'](changes, arrayChangeEventName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getChanges(previousContents, currentContents) {
|
||||
// We try to re-use cached diffs.
|
||||
// The scenarios where pendingChanges > 1 are when using rate limiting or deferred updates,
|
||||
// which without this check would not be compatible with arrayChange notifications. Normally,
|
||||
// notifications are issued immediately so we wouldn't be queueing up more than one.
|
||||
if (!cachedDiff || pendingChanges > 1) {
|
||||
cachedDiff = ko.utils.compareArrays(previousContents, currentContents, target.compareArrayOptions);
|
||||
}
|
||||
|
||||
return cachedDiff;
|
||||
}
|
||||
|
||||
target.cacheDiffForKnownOperation = function(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) {
|
||||
return;
|
||||
}
|
||||
var diff = [],
|
||||
arrayLength = rawArray.length,
|
||||
argsLength = args.length,
|
||||
offset = 0;
|
||||
|
||||
function pushDiff(status, value, index) {
|
||||
return diff[diff.length] = { 'status': status, 'value': value, 'index': index };
|
||||
}
|
||||
switch (operationName) {
|
||||
case 'push':
|
||||
offset = arrayLength;
|
||||
case 'unshift':
|
||||
for (var index = 0; index < argsLength; index++) {
|
||||
pushDiff('added', args[index], offset + index);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'pop':
|
||||
offset = arrayLength - 1;
|
||||
case 'shift':
|
||||
if (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,
|
||||
endIndex = Math.max(endDeleteIndex, endAddIndex),
|
||||
additions = [], deletions = [];
|
||||
for (var index = startIndex, argsIndex = 2; index < endIndex; ++index, ++argsIndex) {
|
||||
if (index < endDeleteIndex)
|
||||
deletions.push(pushDiff('deleted', rawArray[index], index));
|
||||
if (index < endAddIndex)
|
||||
additions.push(pushDiff('added', args[argsIndex], index));
|
||||
}
|
||||
ko.utils.findMovesInArrayComparison(deletions, additions);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
cachedDiff = diff;
|
||||
};
|
||||
};
|
||||
142
vendors/knockout/src/subscribables/observableArray.js
vendored
Normal file
142
vendors/knockout/src/subscribables/observableArray.js
vendored
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
ko.observableArray = function (initialValues) {
|
||||
initialValues = initialValues || [];
|
||||
|
||||
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.");
|
||||
|
||||
var result = ko.observable(initialValues);
|
||||
ko.utils.setPrototypeOfOrExtend(result, ko.observableArray['fn']);
|
||||
return result.extend({'trackArrayChanges':true});
|
||||
};
|
||||
|
||||
ko.observableArray['fn'] = {
|
||||
'remove': function (valueOrPredicate) {
|
||||
var underlyingArray = this.peek();
|
||||
var removedValues = [];
|
||||
var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
|
||||
for (var i = 0; i < underlyingArray.length; i++) {
|
||||
var value = underlyingArray[i];
|
||||
if (predicate(value)) {
|
||||
if (removedValues.length === 0) {
|
||||
this.valueWillMutate();
|
||||
}
|
||||
if (underlyingArray[i] !== value) {
|
||||
throw Error("Array modified during remove; cannot remove item");
|
||||
}
|
||||
removedValues.push(value);
|
||||
underlyingArray.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
if (removedValues.length) {
|
||||
this.valueHasMutated();
|
||||
}
|
||||
return removedValues;
|
||||
},
|
||||
|
||||
'removeAll': function (arrayOfValues) {
|
||||
// If you passed zero args, we remove everything
|
||||
if (arrayOfValues === undefined) {
|
||||
var underlyingArray = this.peek();
|
||||
var allValues = underlyingArray.slice(0);
|
||||
this.valueWillMutate();
|
||||
underlyingArray.splice(0, underlyingArray.length);
|
||||
this.valueHasMutated();
|
||||
return allValues;
|
||||
}
|
||||
// If you passed an arg, we interpret it as an array of entries to remove
|
||||
if (!arrayOfValues)
|
||||
return [];
|
||||
return this['remove'](function (value) {
|
||||
return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
|
||||
});
|
||||
},
|
||||
|
||||
'destroy': function (valueOrPredicate) {
|
||||
var underlyingArray = this.peek();
|
||||
var predicate = typeof valueOrPredicate == "function" && !ko.isObservable(valueOrPredicate) ? valueOrPredicate : function (value) { return value === valueOrPredicate; };
|
||||
this.valueWillMutate();
|
||||
for (var i = underlyingArray.length - 1; i >= 0; i--) {
|
||||
var value = underlyingArray[i];
|
||||
if (predicate(value))
|
||||
value["_destroy"] = true;
|
||||
}
|
||||
this.valueHasMutated();
|
||||
},
|
||||
|
||||
'destroyAll': function (arrayOfValues) {
|
||||
// If you passed zero args, we destroy everything
|
||||
if (arrayOfValues === undefined)
|
||||
return this['destroy'](function() { return true });
|
||||
|
||||
// If you passed an arg, we interpret it as an array of entries to destroy
|
||||
if (!arrayOfValues)
|
||||
return [];
|
||||
return this['destroy'](function (value) {
|
||||
return ko.utils.arrayIndexOf(arrayOfValues, value) >= 0;
|
||||
});
|
||||
},
|
||||
|
||||
'indexOf': function (item) {
|
||||
var underlyingArray = this();
|
||||
return ko.utils.arrayIndexOf(underlyingArray, item);
|
||||
},
|
||||
|
||||
'replace': function(oldItem, newItem) {
|
||||
var index = this['indexOf'](oldItem);
|
||||
if (index >= 0) {
|
||||
this.valueWillMutate();
|
||||
this.peek()[index] = newItem;
|
||||
this.valueHasMutated();
|
||||
}
|
||||
},
|
||||
|
||||
'sorted': function (compareFunction) {
|
||||
var arrayCopy = this().slice(0);
|
||||
return compareFunction ? arrayCopy.sort(compareFunction) : arrayCopy.sort();
|
||||
},
|
||||
|
||||
'reversed': function () {
|
||||
return this().slice(0).reverse();
|
||||
}
|
||||
};
|
||||
|
||||
// Note that for browsers that don't support proto assignment, the
|
||||
// inheritance chain is created manually in the ko.observableArray constructor
|
||||
if (ko.utils.canSetPrototype) {
|
||||
ko.utils.setPrototypeOf(ko.observableArray['fn'], ko.observable['fn']);
|
||||
}
|
||||
|
||||
// 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.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)
|
||||
var underlyingArray = this.peek();
|
||||
this.valueWillMutate();
|
||||
this.cacheDiffForKnownOperation(underlyingArray, methodName, arguments);
|
||||
var methodCallResult = underlyingArray[methodName].apply(underlyingArray, arguments);
|
||||
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;
|
||||
};
|
||||
});
|
||||
|
||||
// Populate ko.observableArray.fn with read-only functions from native arrays
|
||||
ko.utils.arrayForEach(["slice"], function (methodName) {
|
||||
ko.observableArray['fn'][methodName] = function () {
|
||||
var underlyingArray = this();
|
||||
return underlyingArray[methodName].apply(underlyingArray, arguments);
|
||||
};
|
||||
});
|
||||
|
||||
ko.isObservableArray = function (instance) {
|
||||
return ko.isObservable(instance)
|
||||
&& typeof instance["remove"] == "function"
|
||||
&& typeof instance["push"] == "function";
|
||||
};
|
||||
|
||||
ko.exportSymbol('observableArray', ko.observableArray);
|
||||
ko.exportSymbol('isObservableArray', ko.isObservableArray);
|
||||
22
vendors/knockout/src/subscribables/observableUtils.js
vendored
Normal file
22
vendors/knockout/src/subscribables/observableUtils.js
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
ko.when = function(predicate, callback, context) {
|
||||
function kowhen (resolve) {
|
||||
var observable = ko.pureComputed(predicate, context).extend({notify:'always'});
|
||||
var subscription = observable.subscribe(function(value) {
|
||||
if (value) {
|
||||
subscription.dispose();
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
// In case the initial value is true, process it right away
|
||||
observable['notifySubscribers'](observable.peek());
|
||||
|
||||
return subscription;
|
||||
}
|
||||
if (typeof Promise === "function" && !callback) {
|
||||
return new Promise(kowhen);
|
||||
} else {
|
||||
return kowhen(callback.bind(context));
|
||||
}
|
||||
};
|
||||
|
||||
ko.exportSymbol('when', ko.when);
|
||||
208
vendors/knockout/src/subscribables/subscribable.js
vendored
Normal file
208
vendors/knockout/src/subscribables/subscribable.js
vendored
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
|
||||
ko.subscription = function (target, callback, disposeCallback) {
|
||||
this._target = target;
|
||||
this._callback = callback;
|
||||
this._disposeCallback = disposeCallback;
|
||||
this._isDisposed = false;
|
||||
this._node = null;
|
||||
this._domNodeDisposalCallback = null;
|
||||
ko.exportProperty(this, 'dispose', this.dispose);
|
||||
ko.exportProperty(this, 'disposeWhenNodeIsRemoved', this.disposeWhenNodeIsRemoved);
|
||||
};
|
||||
ko.subscription.prototype.dispose = function () {
|
||||
var self = this;
|
||||
if (!self._isDisposed) {
|
||||
if (self._domNodeDisposalCallback) {
|
||||
ko.utils.domNodeDisposal.removeDisposeCallback(self._node, self._domNodeDisposalCallback);
|
||||
}
|
||||
self._isDisposed = true;
|
||||
self._disposeCallback();
|
||||
|
||||
self._target = self._callback = self._disposeCallback = self._node = self._domNodeDisposalCallback = null;
|
||||
}
|
||||
};
|
||||
ko.subscription.prototype.disposeWhenNodeIsRemoved = function (node) {
|
||||
this._node = node;
|
||||
ko.utils.domNodeDisposal.addDisposeCallback(node, this._domNodeDisposalCallback = this.dispose.bind(this));
|
||||
};
|
||||
|
||||
ko.subscribable = function () {
|
||||
ko.utils.setPrototypeOfOrExtend(this, ko_subscribable_fn);
|
||||
ko_subscribable_fn.init(this);
|
||||
}
|
||||
|
||||
var defaultEvent = "change";
|
||||
|
||||
// Moved out of "limit" to avoid the extra closure
|
||||
function limitNotifySubscribers(value, event) {
|
||||
if (!event || event === defaultEvent) {
|
||||
this._limitChange(value);
|
||||
} else if (event === 'beforeChange') {
|
||||
this._limitBeforeChange(value);
|
||||
} else {
|
||||
this._origNotifySubscribers(value, event);
|
||||
}
|
||||
}
|
||||
|
||||
var ko_subscribable_fn = {
|
||||
init: function(instance) {
|
||||
instance._subscriptions = { "change": [] };
|
||||
instance._versionNumber = 1;
|
||||
},
|
||||
|
||||
subscribe: function (callback, callbackTarget, event) {
|
||||
var self = this;
|
||||
|
||||
event = event || defaultEvent;
|
||||
var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
|
||||
|
||||
var subscription = new ko.subscription(self, boundCallback, function () {
|
||||
ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
|
||||
if (self.afterSubscriptionRemove)
|
||||
self.afterSubscriptionRemove(event);
|
||||
});
|
||||
|
||||
if (self.beforeSubscriptionAdd)
|
||||
self.beforeSubscriptionAdd(event);
|
||||
|
||||
if (!self._subscriptions[event])
|
||||
self._subscriptions[event] = [];
|
||||
self._subscriptions[event].push(subscription);
|
||||
|
||||
return subscription;
|
||||
},
|
||||
|
||||
"notifySubscribers": function (valueToNotify, event) {
|
||||
event = event || defaultEvent;
|
||||
if (event === defaultEvent) {
|
||||
this.updateVersion();
|
||||
}
|
||||
if (this.hasSubscriptionsForEvent(event)) {
|
||||
var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
|
||||
try {
|
||||
ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
|
||||
for (var i = 0, subscription; subscription = subs[i]; ++i) {
|
||||
// In case a subscription was disposed during the arrayForEach cycle, check
|
||||
// for isDisposed on each subscription before invoking its callback
|
||||
if (!subscription._isDisposed)
|
||||
subscription._callback(valueToNotify);
|
||||
}
|
||||
} finally {
|
||||
ko.dependencyDetection.end(); // End suppressing dependency detection
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getVersion: function () {
|
||||
return this._versionNumber;
|
||||
},
|
||||
|
||||
hasChanged: function (versionToCheck) {
|
||||
return this.getVersion() !== versionToCheck;
|
||||
},
|
||||
|
||||
updateVersion: function () {
|
||||
++this._versionNumber;
|
||||
},
|
||||
|
||||
limit: function(limitFunction) {
|
||||
var self = this, selfIsObservable = ko.isObservable(self),
|
||||
ignoreBeforeChange, notifyNextChange, previousValue, pendingValue, didUpdate,
|
||||
beforeChange = 'beforeChange';
|
||||
|
||||
if (!self._origNotifySubscribers) {
|
||||
self._origNotifySubscribers = self["notifySubscribers"];
|
||||
self["notifySubscribers"] = limitNotifySubscribers;
|
||||
}
|
||||
|
||||
var finish = limitFunction(function() {
|
||||
self._notificationIsPending = false;
|
||||
|
||||
// If an observable provided a reference to itself, access it to get the latest value.
|
||||
// This allows computed observables to delay calculating their value until needed.
|
||||
if (selfIsObservable && pendingValue === self) {
|
||||
pendingValue = self._evalIfChanged ? self._evalIfChanged() : self();
|
||||
}
|
||||
var shouldNotify = notifyNextChange || (didUpdate && self.isDifferent(previousValue, pendingValue));
|
||||
|
||||
didUpdate = notifyNextChange = ignoreBeforeChange = false;
|
||||
|
||||
if (shouldNotify) {
|
||||
self._origNotifySubscribers(previousValue = pendingValue);
|
||||
}
|
||||
});
|
||||
|
||||
self._limitChange = function(value, isDirty) {
|
||||
if (!isDirty || !self._notificationIsPending) {
|
||||
didUpdate = !isDirty;
|
||||
}
|
||||
self._changeSubscriptions = self._subscriptions[defaultEvent].slice(0);
|
||||
self._notificationIsPending = ignoreBeforeChange = true;
|
||||
pendingValue = value;
|
||||
finish();
|
||||
};
|
||||
self._limitBeforeChange = function(value) {
|
||||
if (!ignoreBeforeChange) {
|
||||
previousValue = value;
|
||||
self._origNotifySubscribers(value, beforeChange);
|
||||
}
|
||||
};
|
||||
self._recordUpdate = function() {
|
||||
didUpdate = true;
|
||||
};
|
||||
self._notifyNextChangeIfValueIsDifferent = function() {
|
||||
if (self.isDifferent(previousValue, self.peek(true /*evaluate*/))) {
|
||||
notifyNextChange = true;
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
hasSubscriptionsForEvent: function(event) {
|
||||
return this._subscriptions[event] && this._subscriptions[event].length;
|
||||
},
|
||||
|
||||
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;
|
||||
}
|
||||
},
|
||||
|
||||
isDifferent: function(oldValue, newValue) {
|
||||
return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
|
||||
},
|
||||
|
||||
toString: function() {
|
||||
return '[object Object]'
|
||||
},
|
||||
|
||||
extend: applyExtenders
|
||||
};
|
||||
|
||||
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);
|
||||
ko.exportProperty(ko_subscribable_fn, 'getSubscriptionsCount', ko_subscribable_fn.getSubscriptionsCount);
|
||||
|
||||
// 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.
|
||||
if (ko.utils.canSetPrototype) {
|
||||
ko.utils.setPrototypeOf(ko_subscribable_fn, Function.prototype);
|
||||
}
|
||||
|
||||
ko.subscribable['fn'] = ko_subscribable_fn;
|
||||
|
||||
|
||||
ko.isSubscribable = function (instance) {
|
||||
return instance != null && typeof instance.subscribe == "function" && typeof instance["notifySubscribers"] == "function";
|
||||
};
|
||||
|
||||
ko.exportSymbol('subscribable', ko.subscribable);
|
||||
ko.exportSymbol('isSubscribable', ko.isSubscribable);
|
||||
Loading…
Add table
Add a link
Reference in a new issue