// 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, AnonymousObservable = Rx.AnonymousObservable, AbstractObserver = Rx.internals.AbstractObserver, CompositeDisposable = Rx.CompositeDisposable, Subject = Rx.Subject, Observer = Rx.Observer, disposableEmpty = Rx.Disposable.empty, disposableCreate = Rx.Disposable.create, inherits = Rx.internals.inherits, addProperties = Rx.internals.addProperties, timeoutScheduler = Rx.Scheduler.timeout, currentThreadScheduler = Rx.Scheduler.currentThread, identity = Rx.helpers.identity; var objectDisposed = 'Object has been disposed'; function checkDisposed() { if (this.isDisposed) { throw new Error(objectDisposed); } } /** * Used to pause and resume streams. */ Rx.Pauser = (function (__super__) { inherits(Pauser, __super__); function Pauser() { __super__.call(this); } /** * Pauses the underlying sequence. */ Pauser.prototype.pause = function () { this.onNext(false); }; /** * Resumes the underlying sequence. */ Pauser.prototype.resume = function () { this.onNext(true); }; return Pauser; }(Subject)); var PausableObservable = (function (__super__) { inherits(PausableObservable, __super__); function subscribe(observer) { var conn = this.source.publish(), subscription = conn.subscribe(observer), connection = disposableEmpty; var pausable = this.pauser.distinctUntilChanged().subscribe(function (b) { if (b) { connection = conn.connect(); } else { connection.dispose(); connection = disposableEmpty; } }); return new CompositeDisposable(subscription, connection, pausable); } function PausableObservable(source, pauser) { this.source = source; this.controller = new Subject(); if (pauser && pauser.subscribe) { this.pauser = this.controller.merge(pauser); } else { this.pauser = this.controller; } __super__.call(this, subscribe, source); } PausableObservable.prototype.pause = function () { this.controller.onNext(false); }; PausableObservable.prototype.resume = function () { this.controller.onNext(true); }; return PausableObservable; }(Observable)); /** * Pauses the underlying observable sequence based upon the observable sequence which yields true/false. * @example * var pauser = new Rx.Subject(); * var source = Rx.Observable.interval(100).pausable(pauser); * @param {Observable} pauser The observable sequence used to pause the underlying sequence. * @returns {Observable} The observable sequence which is paused based upon the pauser. */ observableProto.pausable = function (pauser) { return new PausableObservable(this, pauser); }; function combineLatestSource(source, subject, resultSelector) { return new AnonymousObservable(function (o) { var hasValue = [false, false], hasValueAll = false, isDone = false, values = new Array(2), err; function next(x, i) { values[i] = x var res; hasValue[i] = true; if (hasValueAll || (hasValueAll = hasValue.every(identity))) { if (err) { o.onError(err); return; } try { res = resultSelector.apply(null, values); } catch (ex) { o.onError(ex); return; } o.onNext(res); } if (isDone && values[1]) { o.onCompleted(); } } return new CompositeDisposable( source.subscribe( function (x) { next(x, 0); }, function (e) { if (values[1]) { o.onError(e); } else { err = e; } }, function () { isDone = true; values[1] && o.onCompleted(); }), subject.subscribe( function (x) { next(x, 1); }, function (e) { o.onError(e); }, function () { isDone = true; next(true, 1); }) ); }, source); } var PausableBufferedObservable = (function (__super__) { inherits(PausableBufferedObservable, __super__); function subscribe(o) { var q = [], previousShouldFire; var subscription = combineLatestSource( this.source, this.pauser.distinctUntilChanged().startWith(false), function (data, shouldFire) { return { data: data, shouldFire: shouldFire }; }) .subscribe( function (results) { if (previousShouldFire !== undefined && results.shouldFire != previousShouldFire) { previousShouldFire = results.shouldFire; // change in shouldFire if (results.shouldFire) { while (q.length > 0) { o.onNext(q.shift()); } } } else { previousShouldFire = results.shouldFire; // new data if (results.shouldFire) { o.onNext(results.data); } else { q.push(results.data); } } }, function (err) { // Empty buffer before sending error while (q.length > 0) { o.onNext(q.shift()); } o.onError(err); }, function () { // Empty buffer before sending completion while (q.length > 0) { o.onNext(q.shift()); } o.onCompleted(); } ); return subscription; } function PausableBufferedObservable(source, pauser) { this.source = source; this.controller = new Subject(); if (pauser && pauser.subscribe) { this.pauser = this.controller.merge(pauser); } else { this.pauser = this.controller; } __super__.call(this, subscribe, source); } PausableBufferedObservable.prototype.pause = function () { this.controller.onNext(false); }; PausableBufferedObservable.prototype.resume = function () { this.controller.onNext(true); }; return PausableBufferedObservable; }(Observable)); /** * Pauses the underlying observable sequence based upon the observable sequence which yields true/false, * and yields the values that were buffered while paused. * @example * var pauser = new Rx.Subject(); * var source = Rx.Observable.interval(100).pausableBuffered(pauser); * @param {Observable} pauser The observable sequence used to pause the underlying sequence. * @returns {Observable} The observable sequence which is paused based upon the pauser. */ observableProto.pausableBuffered = function (subject) { return new PausableBufferedObservable(this, subject); }; var ControlledObservable = (function (__super__) { inherits(ControlledObservable, __super__); function subscribe (observer) { return this.source.subscribe(observer); } function ControlledObservable (source, enableQueue) { __super__.call(this, subscribe, source); this.subject = new ControlledSubject(enableQueue); this.source = source.multicast(this.subject).refCount(); } ControlledObservable.prototype.request = function (numberOfItems) { if (numberOfItems == null) { numberOfItems = -1; } return this.subject.request(numberOfItems); }; return ControlledObservable; }(Observable)); var ControlledSubject = (function (__super__) { function subscribe (observer) { return this.subject.subscribe(observer); } inherits(ControlledSubject, __super__); function ControlledSubject(enableQueue) { enableQueue == null && (enableQueue = true); __super__.call(this, subscribe); this.subject = new Subject(); this.enableQueue = enableQueue; this.queue = enableQueue ? [] : null; this.requestedCount = 0; this.requestedDisposable = disposableEmpty; this.error = null; this.hasFailed = false; this.hasCompleted = false; this.controlledDisposable = disposableEmpty; } addProperties(ControlledSubject.prototype, Observer, { onCompleted: function () { this.hasCompleted = true; (!this.enableQueue || this.queue.length === 0) && this.subject.onCompleted(); }, onError: function (error) { this.hasFailed = true; this.error = error; (!this.enableQueue || this.queue.length === 0) && this.subject.onError(error); }, onNext: function (value) { var hasRequested = false; if (this.requestedCount === 0) { this.enableQueue && this.queue.push(value); } else { (this.requestedCount !== -1 && this.requestedCount-- === 0) && this.disposeCurrentRequest(); hasRequested = true; } hasRequested && this.subject.onNext(value); }, _processRequest: function (numberOfItems) { if (this.enableQueue) { while (this.queue.length >= numberOfItems && numberOfItems > 0) { this.subject.onNext(this.queue.shift()); numberOfItems--; } return this.queue.length !== 0 ? { numberOfItems: numberOfItems, returnValue: true } : { numberOfItems: numberOfItems, returnValue: false }; } if (this.hasFailed) { this.subject.onError(this.error); this.controlledDisposable.dispose(); this.controlledDisposable = disposableEmpty; } else if (this.hasCompleted) { this.subject.onCompleted(); this.controlledDisposable.dispose(); this.controlledDisposable = disposableEmpty; } return { numberOfItems: numberOfItems, returnValue: false }; }, request: function (number) { this.disposeCurrentRequest(); var self = this, r = this._processRequest(number); var number = r.numberOfItems; if (!r.returnValue) { this.requestedCount = number; this.requestedDisposable = disposableCreate(function () { self.requestedCount = 0; }); return this.requestedDisposable } else { return disposableEmpty; } }, disposeCurrentRequest: function () { this.requestedDisposable.dispose(); this.requestedDisposable = disposableEmpty; } }); return ControlledSubject; }(Observable)); /** * Attaches a controller to the observable sequence with the ability to queue. * @example * var source = Rx.Observable.interval(100).controlled(); * source.request(3); // Reads 3 values * @param {Observable} pauser The observable sequence used to pause the underlying sequence. * @returns {Observable} The observable sequence which is paused based upon the pauser. */ observableProto.controlled = function (enableQueue) { if (enableQueue == null) { enableQueue = true; } return new ControlledObservable(this, enableQueue); }; var StopAndWaitObservable = (function (__super__) { function subscribe (observer) { this.subscription = this.source.subscribe(new StopAndWaitObserver(observer, this, this.subscription)); var self = this; timeoutScheduler.schedule(function () { self.source.request(1); }); return this.subscription; } inherits(StopAndWaitObservable, __super__); function StopAndWaitObservable (source) { __super__.call(this, subscribe, source); this.source = source; } var StopAndWaitObserver = (function (__sub__) { inherits(StopAndWaitObserver, __sub__); function StopAndWaitObserver (observer, observable, cancel) { __sub__.call(this); this.observer = observer; this.observable = observable; this.cancel = cancel; } var stopAndWaitObserverProto = StopAndWaitObserver.prototype; stopAndWaitObserverProto.completed = function () { this.observer.onCompleted(); this.dispose(); }; stopAndWaitObserverProto.error = function (error) { this.observer.onError(error); this.dispose(); } stopAndWaitObserverProto.next = function (value) { this.observer.onNext(value); var self = this; timeoutScheduler.schedule(function () { self.observable.source.request(1); }); }; stopAndWaitObserverProto.dispose = function () { this.observer = null; if (this.cancel) { this.cancel.dispose(); this.cancel = null; } __sub__.prototype.dispose.call(this); }; return StopAndWaitObserver; }(AbstractObserver)); return StopAndWaitObservable; }(Observable)); /** * Attaches a stop and wait observable to the current observable. * @returns {Observable} A stop and wait observable. */ ControlledObservable.prototype.stopAndWait = function () { return new StopAndWaitObservable(this); }; var WindowedObservable = (function (__super__) { function subscribe (observer) { this.subscription = this.source.subscribe(new WindowedObserver(observer, this, this.subscription)); var self = this; timeoutScheduler.schedule(function () { self.source.request(self.windowSize); }); return this.subscription; } inherits(WindowedObservable, __super__); function WindowedObservable(source, windowSize) { __super__.call(this, subscribe, source); this.source = source; this.windowSize = windowSize; } var WindowedObserver = (function (__sub__) { inherits(WindowedObserver, __sub__); function WindowedObserver(observer, observable, cancel) { this.observer = observer; this.observable = observable; this.cancel = cancel; this.received = 0; } var windowedObserverPrototype = WindowedObserver.prototype; windowedObserverPrototype.completed = function () { this.observer.onCompleted(); this.dispose(); }; windowedObserverPrototype.error = function (error) { this.observer.onError(error); this.dispose(); }; windowedObserverPrototype.next = function (value) { this.observer.onNext(value); this.received = ++this.received % this.observable.windowSize; if (this.received === 0) { var self = this; timeoutScheduler.schedule(function () { self.observable.source.request(self.observable.windowSize); }); } }; windowedObserverPrototype.dispose = function () { this.observer = null; if (this.cancel) { this.cancel.dispose(); this.cancel = null; } __sub__.prototype.dispose.call(this); }; return WindowedObserver; }(AbstractObserver)); return WindowedObservable; }(Observable)); /** * Creates a sliding windowed observable based upon the window size. * @param {Number} windowSize The number of items in the window * @returns {Observable} A windowed observable based upon the window size. */ ControlledObservable.prototype.windowed = function (windowSize) { return new WindowedObservable(this, windowSize); }; return Rx; }));