634 lines
23 KiB
JavaScript
634 lines
23 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) {
|
||
|
|
||
|
// References
|
||
|
var Observable = Rx.Observable,
|
||
|
observableProto = Observable.prototype,
|
||
|
observableNever = Observable.never,
|
||
|
observableThrow = Observable.throwException,
|
||
|
AnonymousObservable = Rx.AnonymousObservable,
|
||
|
AnonymousObserver = Rx.AnonymousObserver,
|
||
|
notificationCreateOnNext = Rx.Notification.createOnNext,
|
||
|
notificationCreateOnError = Rx.Notification.createOnError,
|
||
|
notificationCreateOnCompleted = Rx.Notification.createOnCompleted,
|
||
|
Observer = Rx.Observer,
|
||
|
Subject = Rx.Subject,
|
||
|
internals = Rx.internals,
|
||
|
helpers = Rx.helpers,
|
||
|
ScheduledObserver = internals.ScheduledObserver,
|
||
|
SerialDisposable = Rx.SerialDisposable,
|
||
|
SingleAssignmentDisposable = Rx.SingleAssignmentDisposable,
|
||
|
CompositeDisposable = Rx.CompositeDisposable,
|
||
|
RefCountDisposable = Rx.RefCountDisposable,
|
||
|
disposableEmpty = Rx.Disposable.empty,
|
||
|
immediateScheduler = Rx.Scheduler.immediate,
|
||
|
defaultKeySerializer = helpers.defaultKeySerializer,
|
||
|
addRef = Rx.internals.addRef,
|
||
|
identity = helpers.identity,
|
||
|
isPromise = helpers.isPromise,
|
||
|
inherits = internals.inherits,
|
||
|
bindCallback = internals.bindCallback,
|
||
|
noop = helpers.noop,
|
||
|
isScheduler = helpers.isScheduler,
|
||
|
observableFromPromise = Observable.fromPromise,
|
||
|
slice = Array.prototype.slice;
|
||
|
|
||
|
function argsOrArray(args, idx) {
|
||
|
return args.length === 1 && Array.isArray(args[idx]) ?
|
||
|
args[idx] :
|
||
|
slice.call(args);
|
||
|
}
|
||
|
|
||
|
var argumentOutOfRange = 'Argument out of range';
|
||
|
|
||
|
function ScheduledDisposable(scheduler, disposable) {
|
||
|
this.scheduler = scheduler;
|
||
|
this.disposable = disposable;
|
||
|
this.isDisposed = false;
|
||
|
}
|
||
|
|
||
|
ScheduledDisposable.prototype.dispose = function () {
|
||
|
var parent = this;
|
||
|
this.scheduler.schedule(function () {
|
||
|
if (!parent.isDisposed) {
|
||
|
parent.isDisposed = true;
|
||
|
parent.disposable.dispose();
|
||
|
}
|
||
|
});
|
||
|
};
|
||
|
|
||
|
var CheckedObserver = (function (_super) {
|
||
|
inherits(CheckedObserver, _super);
|
||
|
|
||
|
function CheckedObserver(observer) {
|
||
|
_super.call(this);
|
||
|
this._observer = observer;
|
||
|
this._state = 0; // 0 - idle, 1 - busy, 2 - done
|
||
|
}
|
||
|
|
||
|
var CheckedObserverPrototype = CheckedObserver.prototype;
|
||
|
|
||
|
CheckedObserverPrototype.onNext = function (value) {
|
||
|
this.checkAccess();
|
||
|
try {
|
||
|
this._observer.onNext(value);
|
||
|
} catch (e) {
|
||
|
throw e;
|
||
|
} finally {
|
||
|
this._state = 0;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CheckedObserverPrototype.onError = function (err) {
|
||
|
this.checkAccess();
|
||
|
try {
|
||
|
this._observer.onError(err);
|
||
|
} catch (e) {
|
||
|
throw e;
|
||
|
} finally {
|
||
|
this._state = 2;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CheckedObserverPrototype.onCompleted = function () {
|
||
|
this.checkAccess();
|
||
|
try {
|
||
|
this._observer.onCompleted();
|
||
|
} catch (e) {
|
||
|
throw e;
|
||
|
} finally {
|
||
|
this._state = 2;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
CheckedObserverPrototype.checkAccess = function () {
|
||
|
if (this._state === 1) { throw new Error('Re-entrancy detected'); }
|
||
|
if (this._state === 2) { throw new Error('Observer completed'); }
|
||
|
if (this._state === 0) { this._state = 1; }
|
||
|
};
|
||
|
|
||
|
return CheckedObserver;
|
||
|
}(Observer));
|
||
|
|
||
|
var ObserveOnObserver = (function (__super__) {
|
||
|
inherits(ObserveOnObserver, __super__);
|
||
|
|
||
|
function ObserveOnObserver(scheduler, observer, cancel) {
|
||
|
__super__.call(this, scheduler, observer);
|
||
|
this._cancel = cancel;
|
||
|
}
|
||
|
|
||
|
ObserveOnObserver.prototype.next = function (value) {
|
||
|
__super__.prototype.next.call(this, value);
|
||
|
this.ensureActive();
|
||
|
};
|
||
|
|
||
|
ObserveOnObserver.prototype.error = function (e) {
|
||
|
__super__.prototype.error.call(this, e);
|
||
|
this.ensureActive();
|
||
|
};
|
||
|
|
||
|
ObserveOnObserver.prototype.completed = function () {
|
||
|
__super__.prototype.completed.call(this);
|
||
|
this.ensureActive();
|
||
|
};
|
||
|
|
||
|
ObserveOnObserver.prototype.dispose = function () {
|
||
|
__super__.prototype.dispose.call(this);
|
||
|
this._cancel && this._cancel.dispose();
|
||
|
this._cancel = null;
|
||
|
};
|
||
|
|
||
|
return ObserveOnObserver;
|
||
|
})(ScheduledObserver);
|
||
|
|
||
|
/**
|
||
|
* Checks access to the observer for grammar violations. This includes checking for multiple OnError or OnCompleted calls, as well as reentrancy in any of the observer methods.
|
||
|
* If a violation is detected, an Error is thrown from the offending observer method call.
|
||
|
*
|
||
|
* @returns An observer that checks callbacks invocations against the observer grammar and, if the checks pass, forwards those to the specified observer.
|
||
|
*/
|
||
|
Observer.prototype.checked = function () { return new CheckedObserver(this); };
|
||
|
|
||
|
/**
|
||
|
* Schedules the invocation of observer methods on the given scheduler.
|
||
|
* @param {Scheduler} scheduler Scheduler to schedule observer messages on.
|
||
|
* @returns {Observer} Observer whose messages are scheduled on the given scheduler.
|
||
|
*/
|
||
|
Observer.notifyOn = function (scheduler) {
|
||
|
return new ObserveOnObserver(scheduler, this);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates an observer from a notification callback.
|
||
|
* @param {Function} handler Action that handles a notification.
|
||
|
* @returns The observer object that invokes the specified handler using a notification corresponding to each message it receives.
|
||
|
*/
|
||
|
Observer.fromNotifier = function (handler, thisArg) {
|
||
|
var handlerFunc = bindCallback(handler, thisArg, 1);
|
||
|
return new AnonymousObserver(function (x) {
|
||
|
return handlerFunc(notificationCreateOnNext(x));
|
||
|
}, function (e) {
|
||
|
return handlerFunc(notificationCreateOnError(e));
|
||
|
}, function () {
|
||
|
return handlerFunc(notificationCreateOnCompleted());
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates a notification callback from an observer.
|
||
|
* @returns The action that forwards its input notification to the underlying observer.
|
||
|
*/
|
||
|
Observer.prototype.toNotifier = function () {
|
||
|
var observer = this;
|
||
|
return function (n) { return n.accept(observer); };
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Hides the identity of an observer.
|
||
|
* @returns An observer that hides the identity of the specified observer.
|
||
|
*/
|
||
|
Observer.prototype.asObserver = function () {
|
||
|
var source = this;
|
||
|
return new AnonymousObserver(
|
||
|
function (x) { source.onNext(x); },
|
||
|
function (e) { source.onError(e); },
|
||
|
function () { source.onCompleted(); }
|
||
|
);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Wraps the source sequence in order to run its observer callbacks on the specified scheduler.
|
||
|
*
|
||
|
* This only invokes observer callbacks on a scheduler. In case the subscription and/or unsubscription actions have side-effects
|
||
|
* that require to be run on a scheduler, use subscribeOn.
|
||
|
*
|
||
|
* @param {Scheduler} scheduler Scheduler to notify observers on.
|
||
|
* @returns {Observable} The source sequence whose observations happen on the specified scheduler.
|
||
|
*/
|
||
|
observableProto.observeOn = function (scheduler) {
|
||
|
var source = this;
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
return source.subscribe(new ObserveOnObserver(scheduler, observer));
|
||
|
}, source);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Wraps the source sequence in order to run its subscription and unsubscription logic on the specified scheduler. This operation is not commonly used;
|
||
|
* see the remarks section for more information on the distinction between subscribeOn and observeOn.
|
||
|
|
||
|
* This only performs the side-effects of subscription and unsubscription on the specified scheduler. In order to invoke observer
|
||
|
* callbacks on a scheduler, use observeOn.
|
||
|
|
||
|
* @param {Scheduler} scheduler Scheduler to perform subscription and unsubscription actions on.
|
||
|
* @returns {Observable} The source sequence whose subscriptions and unsubscriptions happen on the specified scheduler.
|
||
|
*/
|
||
|
observableProto.subscribeOn = function (scheduler) {
|
||
|
var source = this;
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var m = new SingleAssignmentDisposable(), d = new SerialDisposable();
|
||
|
d.setDisposable(m);
|
||
|
m.setDisposable(scheduler.schedule(function () {
|
||
|
d.setDisposable(new ScheduledDisposable(scheduler, source.subscribe(observer)));
|
||
|
}));
|
||
|
return d;
|
||
|
}, source);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Generates an observable sequence by running a state-driven loop producing the sequence's elements, using the specified scheduler to send out observer messages.
|
||
|
*
|
||
|
* @example
|
||
|
* var res = Rx.Observable.generate(0, function (x) { return x < 10; }, function (x) { return x + 1; }, function (x) { return x; });
|
||
|
* var res = Rx.Observable.generate(0, function (x) { return x < 10; }, function (x) { return x + 1; }, function (x) { return x; }, Rx.Scheduler.timeout);
|
||
|
* @param {Mixed} initialState Initial state.
|
||
|
* @param {Function} condition Condition to terminate generation (upon returning false).
|
||
|
* @param {Function} iterate Iteration step function.
|
||
|
* @param {Function} resultSelector Selector function for results produced in the sequence.
|
||
|
* @param {Scheduler} [scheduler] Scheduler on which to run the generator loop. If not provided, defaults to Scheduler.currentThread.
|
||
|
* @returns {Observable} The generated sequence.
|
||
|
*/
|
||
|
Observable.generate = function (initialState, condition, iterate, resultSelector, scheduler) {
|
||
|
isScheduler(scheduler) || (scheduler = currentThreadScheduler);
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var first = true, state = initialState;
|
||
|
return scheduler.scheduleRecursive(function (self) {
|
||
|
var hasResult, result;
|
||
|
try {
|
||
|
if (first) {
|
||
|
first = false;
|
||
|
} else {
|
||
|
state = iterate(state);
|
||
|
}
|
||
|
hasResult = condition(state);
|
||
|
if (hasResult) {
|
||
|
result = resultSelector(state);
|
||
|
}
|
||
|
} catch (exception) {
|
||
|
observer.onError(exception);
|
||
|
return;
|
||
|
}
|
||
|
if (hasResult) {
|
||
|
observer.onNext(result);
|
||
|
self();
|
||
|
} else {
|
||
|
observer.onCompleted();
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Constructs an observable sequence that depends on a resource object, whose lifetime is tied to the resulting observable sequence's lifetime.
|
||
|
* @param {Function} resourceFactory Factory function to obtain a resource object.
|
||
|
* @param {Function} observableFactory Factory function to obtain an observable sequence that depends on the obtained resource.
|
||
|
* @returns {Observable} An observable sequence whose lifetime controls the lifetime of the dependent resource object.
|
||
|
*/
|
||
|
Observable.using = function (resourceFactory, observableFactory) {
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var disposable = disposableEmpty, resource, source;
|
||
|
try {
|
||
|
resource = resourceFactory();
|
||
|
resource && (disposable = resource);
|
||
|
source = observableFactory(resource);
|
||
|
} catch (exception) {
|
||
|
return new CompositeDisposable(observableThrow(exception).subscribe(observer), disposable);
|
||
|
}
|
||
|
return new CompositeDisposable(source.subscribe(observer), disposable);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Propagates the observable sequence or Promise that reacts first.
|
||
|
* @param {Observable} rightSource Second observable sequence or Promise.
|
||
|
* @returns {Observable} {Observable} An observable sequence that surfaces either of the given sequences, whichever reacted first.
|
||
|
*/
|
||
|
observableProto.amb = function (rightSource) {
|
||
|
var leftSource = this;
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var choice,
|
||
|
leftChoice = 'L', rightChoice = 'R',
|
||
|
leftSubscription = new SingleAssignmentDisposable(),
|
||
|
rightSubscription = new SingleAssignmentDisposable();
|
||
|
|
||
|
isPromise(rightSource) && (rightSource = observableFromPromise(rightSource));
|
||
|
|
||
|
function choiceL() {
|
||
|
if (!choice) {
|
||
|
choice = leftChoice;
|
||
|
rightSubscription.dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function choiceR() {
|
||
|
if (!choice) {
|
||
|
choice = rightChoice;
|
||
|
leftSubscription.dispose();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
leftSubscription.setDisposable(leftSource.subscribe(function (left) {
|
||
|
choiceL();
|
||
|
if (choice === leftChoice) {
|
||
|
observer.onNext(left);
|
||
|
}
|
||
|
}, function (err) {
|
||
|
choiceL();
|
||
|
if (choice === leftChoice) {
|
||
|
observer.onError(err);
|
||
|
}
|
||
|
}, function () {
|
||
|
choiceL();
|
||
|
if (choice === leftChoice) {
|
||
|
observer.onCompleted();
|
||
|
}
|
||
|
}));
|
||
|
|
||
|
rightSubscription.setDisposable(rightSource.subscribe(function (right) {
|
||
|
choiceR();
|
||
|
if (choice === rightChoice) {
|
||
|
observer.onNext(right);
|
||
|
}
|
||
|
}, function (err) {
|
||
|
choiceR();
|
||
|
if (choice === rightChoice) {
|
||
|
observer.onError(err);
|
||
|
}
|
||
|
}, function () {
|
||
|
choiceR();
|
||
|
if (choice === rightChoice) {
|
||
|
observer.onCompleted();
|
||
|
}
|
||
|
}));
|
||
|
|
||
|
return new CompositeDisposable(leftSubscription, rightSubscription);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Propagates the observable sequence or Promise that reacts first.
|
||
|
*
|
||
|
* @example
|
||
|
* var = Rx.Observable.amb(xs, ys, zs);
|
||
|
* @returns {Observable} An observable sequence that surfaces any of the given sequences, whichever reacted first.
|
||
|
*/
|
||
|
Observable.amb = function () {
|
||
|
var acc = observableNever(),
|
||
|
items = argsOrArray(arguments, 0);
|
||
|
function func(previous, current) {
|
||
|
return previous.amb(current);
|
||
|
}
|
||
|
for (var i = 0, len = items.length; i < len; i++) {
|
||
|
acc = func(acc, items[i]);
|
||
|
}
|
||
|
return acc;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Continues an observable sequence that is terminated normally or by an exception with the next observable sequence.
|
||
|
* @param {Observable} second Second observable sequence used to produce results after the first sequence terminates.
|
||
|
* @returns {Observable} An observable sequence that concatenates the first and second sequence, even if the first sequence terminates exceptionally.
|
||
|
*/
|
||
|
observableProto.onErrorResumeNext = function (second) {
|
||
|
if (!second) { throw new Error('Second observable is required'); }
|
||
|
return onErrorResumeNext([this, second]);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Continues an observable sequence that is terminated normally or by an exception with the next observable sequence.
|
||
|
*
|
||
|
* @example
|
||
|
* 1 - res = Rx.Observable.onErrorResumeNext(xs, ys, zs);
|
||
|
* 1 - res = Rx.Observable.onErrorResumeNext([xs, ys, zs]);
|
||
|
* @returns {Observable} An observable sequence that concatenates the source sequences, even if a sequence terminates exceptionally.
|
||
|
*/
|
||
|
var onErrorResumeNext = Observable.onErrorResumeNext = function () {
|
||
|
var sources = argsOrArray(arguments, 0);
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var pos = 0, subscription = new SerialDisposable(),
|
||
|
cancelable = immediateScheduler.scheduleRecursive(function (self) {
|
||
|
var current, d;
|
||
|
if (pos < sources.length) {
|
||
|
current = sources[pos++];
|
||
|
isPromise(current) && (current = observableFromPromise(current));
|
||
|
d = new SingleAssignmentDisposable();
|
||
|
subscription.setDisposable(d);
|
||
|
d.setDisposable(current.subscribe(observer.onNext.bind(observer), self, self));
|
||
|
} else {
|
||
|
observer.onCompleted();
|
||
|
}
|
||
|
});
|
||
|
return new CompositeDisposable(subscription, cancelable);
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Projects each element of an observable sequence into zero or more buffers which are produced based on element count information.
|
||
|
*
|
||
|
* @example
|
||
|
* var res = xs.bufferWithCount(10);
|
||
|
* var res = xs.bufferWithCount(10, 1);
|
||
|
* @param {Number} count Length of each buffer.
|
||
|
* @param {Number} [skip] Number of elements to skip between creation of consecutive buffers. If not provided, defaults to the count.
|
||
|
* @returns {Observable} An observable sequence of buffers.
|
||
|
*/
|
||
|
observableProto.bufferWithCount = function (count, skip) {
|
||
|
if (typeof skip !== 'number') {
|
||
|
skip = count;
|
||
|
}
|
||
|
return this.windowWithCount(count, skip).selectMany(function (x) {
|
||
|
return x.toArray();
|
||
|
}).where(function (x) {
|
||
|
return x.length > 0;
|
||
|
});
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Projects each element of an observable sequence into zero or more windows which are produced based on element count information.
|
||
|
*
|
||
|
* var res = xs.windowWithCount(10);
|
||
|
* var res = xs.windowWithCount(10, 1);
|
||
|
* @param {Number} count Length of each window.
|
||
|
* @param {Number} [skip] Number of elements to skip between creation of consecutive windows. If not specified, defaults to the count.
|
||
|
* @returns {Observable} An observable sequence of windows.
|
||
|
*/
|
||
|
observableProto.windowWithCount = function (count, skip) {
|
||
|
var source = this;
|
||
|
+count || (count = 0);
|
||
|
Math.abs(count) === Infinity && (count = 0);
|
||
|
if (count <= 0) { throw new Error(argumentOutOfRange); }
|
||
|
skip == null && (skip = count);
|
||
|
+skip || (skip = 0);
|
||
|
Math.abs(skip) === Infinity && (skip = 0);
|
||
|
|
||
|
if (skip <= 0) { throw new Error(argumentOutOfRange); }
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var m = new SingleAssignmentDisposable(),
|
||
|
refCountDisposable = new RefCountDisposable(m),
|
||
|
n = 0,
|
||
|
q = [];
|
||
|
|
||
|
function createWindow () {
|
||
|
var s = new Subject();
|
||
|
q.push(s);
|
||
|
observer.onNext(addRef(s, refCountDisposable));
|
||
|
}
|
||
|
|
||
|
createWindow();
|
||
|
|
||
|
m.setDisposable(source.subscribe(
|
||
|
function (x) {
|
||
|
for (var i = 0, len = q.length; i < len; i++) { q[i].onNext(x); }
|
||
|
var c = n - count + 1;
|
||
|
c >= 0 && c % skip === 0 && q.shift().onCompleted();
|
||
|
++n % skip === 0 && createWindow();
|
||
|
},
|
||
|
function (e) {
|
||
|
while (q.length > 0) { q.shift().onError(e); }
|
||
|
observer.onError(e);
|
||
|
},
|
||
|
function () {
|
||
|
while (q.length > 0) { q.shift().onCompleted(); }
|
||
|
observer.onCompleted();
|
||
|
}
|
||
|
));
|
||
|
return refCountDisposable;
|
||
|
}, source);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns an array with the specified number of contiguous elements from the end of an observable sequence.
|
||
|
*
|
||
|
* @description
|
||
|
* This operator accumulates a buffer with a length enough to store count elements. Upon completion of the
|
||
|
* source sequence, this buffer is produced on the result sequence.
|
||
|
* @param {Number} count Number of elements to take from the end of the source sequence.
|
||
|
* @returns {Observable} An observable sequence containing a single array with the specified number of elements from the end of the source sequence.
|
||
|
*/
|
||
|
observableProto.takeLastBuffer = function (count) {
|
||
|
var source = this;
|
||
|
return new AnonymousObservable(function (o) {
|
||
|
var q = [];
|
||
|
return source.subscribe(function (x) {
|
||
|
q.push(x);
|
||
|
q.length > count && q.shift();
|
||
|
}, function (e) { o.onError(e); }, function () {
|
||
|
o.onNext(q);
|
||
|
o.onCompleted();
|
||
|
});
|
||
|
}, source);
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns the elements of the specified sequence or the specified value in a singleton sequence if the sequence is empty.
|
||
|
*
|
||
|
* var res = obs = xs.defaultIfEmpty();
|
||
|
* 2 - obs = xs.defaultIfEmpty(false);
|
||
|
*
|
||
|
* @memberOf Observable#
|
||
|
* @param defaultValue The value to return if the sequence is empty. If not provided, this defaults to null.
|
||
|
* @returns {Observable} An observable sequence that contains the specified default value if the source is empty; otherwise, the elements of the source itself.
|
||
|
*/
|
||
|
observableProto.defaultIfEmpty = function (defaultValue) {
|
||
|
var source = this;
|
||
|
defaultValue === undefined && (defaultValue = null);
|
||
|
return new AnonymousObservable(function (observer) {
|
||
|
var found = false;
|
||
|
return source.subscribe(function (x) {
|
||
|
found = true;
|
||
|
observer.onNext(x);
|
||
|
},
|
||
|
function (e) { observer.onError(e); },
|
||
|
function () {
|
||
|
!found && observer.onNext(defaultValue);
|
||
|
observer.onCompleted();
|
||
|
});
|
||
|
}, source);
|
||
|
};
|
||
|
|
||
|
// Swap out for Array.findIndex
|
||
|
function arrayIndexOfComparer(array, item, comparer) {
|
||
|
for (var i = 0, len = array.length; i < len; i++) {
|
||
|
if (comparer(array[i], item)) { return i; }
|
||
|
}
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
function HashSet(comparer) {
|
||
|
this.comparer = comparer;
|
||
|
this.set = [];
|
||
|
}
|
||
|
HashSet.prototype.push = function(value) {
|
||
|
var retValue = arrayIndexOfComparer(this.set, value, this.comparer) === -1;
|
||
|
retValue && this.set.push(value);
|
||
|
return retValue;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Returns an observable sequence that contains only distinct elements according to the keySelector and the comparer.
|
||
|
* Usage of this operator should be considered carefully due to the maintenance of an internal lookup structure which can grow large.
|
||
|
*
|
||
|
* @example
|
||
|
* var res = obs = xs.distinct();
|
||
|
* 2 - obs = xs.distinct(function (x) { return x.id; });
|
||
|
* 2 - obs = xs.distinct(function (x) { return x.id; }, function (a,b) { return a === b; });
|
||
|
* @param {Function} [keySelector] A function to compute the comparison key for each element.
|
||
|
* @param {Function} [comparer] Used to compare items in the collection.
|
||
|
* @returns {Observable} An observable sequence only containing the distinct elements, based on a computed key value, from the source sequence.
|
||
|
*/
|
||
|
observableProto.distinct = function (keySelector, comparer) {
|
||
|
var source = this;
|
||
|
comparer || (comparer = defaultComparer);
|
||
|
return new AnonymousObservable(function (o) {
|
||
|
var hashSet = new HashSet(comparer);
|
||
|
return source.subscribe(function (x) {
|
||
|
var key = x;
|
||
|
|
||
|
if (keySelector) {
|
||
|
try {
|
||
|
key = keySelector(x);
|
||
|
} catch (e) {
|
||
|
o.onError(e);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
hashSet.push(key) && o.onNext(x);
|
||
|
},
|
||
|
function (e) { o.onError(e); }, function () { o.onCompleted(); });
|
||
|
}, this);
|
||
|
};
|
||
|
|
||
|
return Rx;
|
||
|
}));
|