    var ControlledSubject = Rx.ControlledSubject = (function (_super) {

        function subscribe (observer) {
            return this.subject.subscribe(observer);
        }

        inherits(ControlledSubject, _super);

        function ControlledSubject(enableQueue) {
            if (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 () {
                checkDisposed.call(this);
                this.hasCompleted = true;

                if (!this.enableQueue || this.queue.length === 0) {
                    this.subject.onCompleted();
                }
            },
            onError: function (error) {
                checkDisposed.call(this);
                this.hasFailed = true;
                this.error = error;

                if (!this.enableQueue || this.queue.length === 0) {
                    this.subject.onError(error);
                }   
            },
            onNext: function (value) {
                checkDisposed.call(this);
                var hasRequested = false;

                if (this.requestedCount === 0) {
                    if (this.enableQueue) {
                        this.queue.push(value);
                    }
                } else {
                    if (this.requestedCount !== -1) {
                        if (this.requestedCount-- === 0) {
                            this.disposeCurrentRequest();
                        }
                    }
                    hasRequested = true;
                }

                if (hasRequested) {
                    this.subject.onNext(value);
                }
            },
            _processRequest: function (numberOfItems) {
                if (this.enableQueue) {
                    //console.log('queue length', this.queue.length);

                    while (this.queue.length >= numberOfItems && numberOfItems > 0) {
                        //console.log('number of items', numberOfItems);
                        this.subject.onNext(this.queue.shift());
                        numberOfItems--;
                    }

                    if (this.queue.length !== 0) {
                        return { numberOfItems: numberOfItems, returnValue: true };
                    } else {
                        return { 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) {
                checkDisposed.call(this);
                this.disposeCurrentRequest();
                var self = this,
                    r = this._processRequest(number);

                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;
            },

            dispose: function () {
                this.isDisposed = true;
                this.error = null;
                this.subject.dispose();
                this.requestedDisposable.dispose();
            }
        });

        return ControlledSubject;
    }(Observable));