text: // 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;
  }));
  