318 lines
10 KiB
JavaScript
318 lines
10 KiB
JavaScript
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. See License.txt in the project root for license information.
|
|
|
|
;(function (factory) {
|
|
var objectTypes = {
|
|
'boolean': false,
|
|
'function': true,
|
|
'object': true,
|
|
'number': false,
|
|
'string': false,
|
|
'undefined': false
|
|
};
|
|
|
|
var root = (objectTypes[typeof window] && window) || this,
|
|
freeExports = objectTypes[typeof exports] && exports && !exports.nodeType && exports,
|
|
freeModule = objectTypes[typeof module] && module && !module.nodeType && module,
|
|
moduleExports = freeModule && freeModule.exports === freeExports && freeExports,
|
|
freeGlobal = objectTypes[typeof global] && global;
|
|
|
|
if (freeGlobal && (freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal)) {
|
|
root = freeGlobal;
|
|
}
|
|
|
|
// Because of build optimizers
|
|
if (typeof define === 'function' && define.amd) {
|
|
define(['rx'], function (Rx, exports) {
|
|
return factory(root, exports, Rx);
|
|
});
|
|
} else if (typeof module === 'object' && module && module.exports === freeExports) {
|
|
module.exports = factory(root, module.exports, require('./rx'));
|
|
} else {
|
|
root.Rx = factory(root, {}, root.Rx);
|
|
}
|
|
}.call(this, function (root, exp, Rx, undefined) {
|
|
|
|
// Aliases
|
|
var Observable = Rx.Observable,
|
|
observableProto = Observable.prototype,
|
|
AnonymousObservable = Rx.AnonymousObservable,
|
|
observableThrow = Observable.throwException,
|
|
observerCreate = Rx.Observer.create,
|
|
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
|
|
CompositeDisposable = Rx.CompositeDisposable,
|
|
AbstractObserver = Rx.internals.AbstractObserver,
|
|
noop = Rx.helpers.noop,
|
|
defaultComparer = Rx.internals.isEqual,
|
|
inherits = Rx.internals.inherits,
|
|
Enumerable = Rx.internals.Enumerable,
|
|
Enumerator = Rx.internals.Enumerator,
|
|
$iterator$ = Rx.iterator,
|
|
doneEnumerator = Rx.doneEnumerator,
|
|
slice = Array.prototype.slice;
|
|
|
|
// Utilities
|
|
function argsOrArray(args, idx) {
|
|
return args.length === 1 && Array.isArray(args[idx]) ?
|
|
args[idx] :
|
|
slice.call(args);
|
|
}
|
|
|
|
/** @private */
|
|
var Map = root.Map || (function () {
|
|
|
|
function Map() {
|
|
this._keys = [];
|
|
this._values = [];
|
|
}
|
|
|
|
Map.prototype.get = function (key) {
|
|
var i = this._keys.indexOf(key);
|
|
return i !== -1 ? this._values[i] : undefined;
|
|
};
|
|
|
|
Map.prototype.set = function (key, value) {
|
|
var i = this._keys.indexOf(key);
|
|
i !== -1 && (this._values[i] = value);
|
|
this._values[this._keys.push(key) - 1] = value;
|
|
};
|
|
|
|
Map.prototype.forEach = function (callback, thisArg) {
|
|
for (var i = 0, len = this._keys.length; i < len; i++) {
|
|
callback.call(thisArg, this._values[i], this._keys[i]);
|
|
}
|
|
};
|
|
|
|
return Map;
|
|
}());
|
|
|
|
/**
|
|
* @constructor
|
|
* Represents a join pattern over observable sequences.
|
|
*/
|
|
function Pattern(patterns) {
|
|
this.patterns = patterns;
|
|
}
|
|
|
|
/**
|
|
* Creates a pattern that matches the current plan matches and when the specified observable sequences has an available value.
|
|
* @param other Observable sequence to match in addition to the current pattern.
|
|
* @return {Pattern} Pattern object that matches when all observable sequences in the pattern have an available value.
|
|
*/
|
|
Pattern.prototype.and = function (other) {
|
|
return new Pattern(this.patterns.concat(other));
|
|
};
|
|
|
|
/**
|
|
* Matches when all observable sequences in the pattern (specified using a chain of and operators) have an available value and projects the values.
|
|
* @param {Function} selector Selector that will be invoked with available values from the source sequences, in the same order of the sequences in the pattern.
|
|
* @return {Plan} Plan that produces the projected values, to be fed (with other plans) to the when operator.
|
|
*/
|
|
Pattern.prototype.thenDo = function (selector) {
|
|
return new Plan(this, selector);
|
|
};
|
|
|
|
function Plan(expression, selector) {
|
|
this.expression = expression;
|
|
this.selector = selector;
|
|
}
|
|
|
|
Plan.prototype.activate = function (externalSubscriptions, observer, deactivate) {
|
|
var self = this;
|
|
var joinObservers = [];
|
|
for (var i = 0, len = this.expression.patterns.length; i < len; i++) {
|
|
joinObservers.push(planCreateObserver(externalSubscriptions, this.expression.patterns[i], observer.onError.bind(observer)));
|
|
}
|
|
var activePlan = new ActivePlan(joinObservers, function () {
|
|
var result;
|
|
try {
|
|
result = self.selector.apply(self, arguments);
|
|
} catch (e) {
|
|
observer.onError(e);
|
|
return;
|
|
}
|
|
observer.onNext(result);
|
|
}, function () {
|
|
for (var j = 0, jlen = joinObservers.length; j < jlen; j++) {
|
|
joinObservers[j].removeActivePlan(activePlan);
|
|
}
|
|
deactivate(activePlan);
|
|
});
|
|
for (i = 0, len = joinObservers.length; i < len; i++) {
|
|
joinObservers[i].addActivePlan(activePlan);
|
|
}
|
|
return activePlan;
|
|
};
|
|
|
|
function planCreateObserver(externalSubscriptions, observable, onError) {
|
|
var entry = externalSubscriptions.get(observable);
|
|
if (!entry) {
|
|
var observer = new JoinObserver(observable, onError);
|
|
externalSubscriptions.set(observable, observer);
|
|
return observer;
|
|
}
|
|
return entry;
|
|
}
|
|
|
|
function ActivePlan(joinObserverArray, onNext, onCompleted) {
|
|
this.joinObserverArray = joinObserverArray;
|
|
this.onNext = onNext;
|
|
this.onCompleted = onCompleted;
|
|
this.joinObservers = new Map();
|
|
for (var i = 0, len = this.joinObserverArray.length; i < len; i++) {
|
|
var joinObserver = this.joinObserverArray[i];
|
|
this.joinObservers.set(joinObserver, joinObserver);
|
|
}
|
|
}
|
|
|
|
ActivePlan.prototype.dequeue = function () {
|
|
this.joinObservers.forEach(function (v) { v.queue.shift(); });
|
|
};
|
|
|
|
ActivePlan.prototype.match = function () {
|
|
var i, len, hasValues = true;
|
|
for (i = 0, len = this.joinObserverArray.length; i < len; i++) {
|
|
if (this.joinObserverArray[i].queue.length === 0) {
|
|
hasValues = false;
|
|
break;
|
|
}
|
|
}
|
|
if (hasValues) {
|
|
var firstValues = [],
|
|
isCompleted = false;
|
|
for (i = 0, len = this.joinObserverArray.length; i < len; i++) {
|
|
firstValues.push(this.joinObserverArray[i].queue[0]);
|
|
this.joinObserverArray[i].queue[0].kind === 'C' && (isCompleted = true);
|
|
}
|
|
if (isCompleted) {
|
|
this.onCompleted();
|
|
} else {
|
|
this.dequeue();
|
|
var values = [];
|
|
for (i = 0, len = firstValues.length; i < firstValues.length; i++) {
|
|
values.push(firstValues[i].value);
|
|
}
|
|
this.onNext.apply(this, values);
|
|
}
|
|
}
|
|
};
|
|
|
|
var JoinObserver = (function (__super__) {
|
|
|
|
inherits(JoinObserver, __super__);
|
|
|
|
function JoinObserver(source, onError) {
|
|
__super__.call(this);
|
|
this.source = source;
|
|
this.onError = onError;
|
|
this.queue = [];
|
|
this.activePlans = [];
|
|
this.subscription = new SingleAssignmentDisposable();
|
|
this.isDisposed = false;
|
|
}
|
|
|
|
var JoinObserverPrototype = JoinObserver.prototype;
|
|
|
|
JoinObserverPrototype.next = function (notification) {
|
|
if (!this.isDisposed) {
|
|
if (notification.kind === 'E') {
|
|
this.onError(notification.exception);
|
|
return;
|
|
}
|
|
this.queue.push(notification);
|
|
var activePlans = this.activePlans.slice(0);
|
|
for (var i = 0, len = activePlans.length; i < len; i++) {
|
|
activePlans[i].match();
|
|
}
|
|
}
|
|
};
|
|
|
|
JoinObserverPrototype.error = noop;
|
|
JoinObserverPrototype.completed = noop;
|
|
|
|
JoinObserverPrototype.addActivePlan = function (activePlan) {
|
|
this.activePlans.push(activePlan);
|
|
};
|
|
|
|
JoinObserverPrototype.subscribe = function () {
|
|
this.subscription.setDisposable(this.source.materialize().subscribe(this));
|
|
};
|
|
|
|
JoinObserverPrototype.removeActivePlan = function (activePlan) {
|
|
this.activePlans.splice(this.activePlans.indexOf(activePlan), 1);
|
|
this.activePlans.length === 0 && this.dispose();
|
|
};
|
|
|
|
JoinObserverPrototype.dispose = function () {
|
|
__super__.prototype.dispose.call(this);
|
|
if (!this.isDisposed) {
|
|
this.isDisposed = true;
|
|
this.subscription.dispose();
|
|
}
|
|
};
|
|
|
|
return JoinObserver;
|
|
} (AbstractObserver));
|
|
|
|
/**
|
|
* Creates a pattern that matches when both observable sequences have an available value.
|
|
*
|
|
* @param right Observable sequence to match with the current sequence.
|
|
* @return {Pattern} Pattern object that matches when both observable sequences have an available value.
|
|
*/
|
|
observableProto.and = function (right) {
|
|
return new Pattern([this, right]);
|
|
};
|
|
|
|
/**
|
|
* Matches when the observable sequence has an available value and projects the value.
|
|
*
|
|
* @param selector Selector that will be invoked for values in the source sequence.
|
|
* @returns {Plan} Plan that produces the projected values, to be fed (with other plans) to the when operator.
|
|
*/
|
|
observableProto.thenDo = function (selector) {
|
|
return new Pattern([this]).thenDo(selector);
|
|
};
|
|
|
|
/**
|
|
* Joins together the results from several patterns.
|
|
*
|
|
* @param plans A series of plans (specified as an Array of as a series of arguments) created by use of the Then operator on patterns.
|
|
* @returns {Observable} Observable sequence with the results form matching several patterns.
|
|
*/
|
|
Observable.when = function () {
|
|
var plans = argsOrArray(arguments, 0);
|
|
return new AnonymousObservable(function (observer) {
|
|
var activePlans = [],
|
|
externalSubscriptions = new Map();
|
|
var outObserver = observerCreate(
|
|
observer.onNext.bind(observer),
|
|
function (err) {
|
|
externalSubscriptions.forEach(function (v) { v.onError(err); });
|
|
observer.onError(err);
|
|
},
|
|
observer.onCompleted.bind(observer)
|
|
);
|
|
try {
|
|
for (var i = 0, len = plans.length; i < len; i++) {
|
|
activePlans.push(plans[i].activate(externalSubscriptions, outObserver, function (activePlan) {
|
|
var idx = activePlans.indexOf(activePlan);
|
|
activePlans.splice(idx, 1);
|
|
activePlans.length === 0 && observer.onCompleted();
|
|
}));
|
|
}
|
|
} catch (e) {
|
|
observableThrow(e).subscribe(observer);
|
|
}
|
|
var group = new CompositeDisposable();
|
|
externalSubscriptions.forEach(function (joinObserver) {
|
|
joinObserver.subscribe();
|
|
group.add(joinObserver);
|
|
});
|
|
|
|
return group;
|
|
});
|
|
};
|
|
|
|
return Rx;
|
|
}));
|