    Ext.require('Ext.util.DelayedTask', function() {

        /**
         * Represents single event type that an Observable object listens to.
         * All actual listeners are tracked inside here.  When the event fires,
         * it calls all the registered listener functions.
         *
         * @private
         */
        Ext.util.Event = Ext.extend(Object, (function() {
            var noOptions = {};

            function createTargeted(handler, listener, o, scope){
                return function(){
                    if (o.target === arguments[0]){
                        handler.apply(scope, arguments);
                    }
                };
            }

            function createBuffered(handler, listener, o, scope) {
                listener.task = new Ext.util.DelayedTask();
                return function() {
                    listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
                };
            }

            function createDelayed(handler, listener, o, scope) {
                return function() {
                    var task = new Ext.util.DelayedTask();
                    if (!listener.tasks) {
                        listener.tasks = [];
                    }
                    listener.tasks.push(task);
                    task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
                };
            }

            function createSingle(handler, listener, o, scope) {
                return function() {
                    var event = listener.ev;

                    if (event.removeListener(listener.fn, scope) && event.observable) {
                        // Removing from a regular Observable-owned, named event (not an anonymous
                        // event such as Ext's readyEvent): Decrement the listeners count
                        event.observable.hasListeners[event.name]--;
                    }

                    return handler.apply(scope, arguments);
                };
            }

            return {
                /**
                 * @property {Boolean} isEvent
                 * `true` in this class to identify an object as an instantiated Event, or subclass thereof.
                 */
                isEvent: true,

                constructor: function(observable, name) {
                    this.name = name;
                    this.observable = observable;
                    this.listeners = [];
                },

                addListener: function(fn, scope, options) {
                    var me = this,
                        listener;
                        scope = scope || me.observable;

                    //<debug error>
                    if (!fn) {
                        Ext.Error.raise({
                            sourceClass: Ext.getClassName(this.observable),
                            sourceMethod: "addListener",
                            msg: "The specified callback function is undefined"
                        });
                    }
                    //</debug>

                    if (!me.isListening(fn, scope)) {
                        listener = me.createListener(fn, scope, options);
                        if (me.firing) {
                            // if we are currently firing this event, don't disturb the listener loop
                            me.listeners = me.listeners.slice(0);
                        }
                        me.listeners.push(listener);
                    }
                },

                createListener: function(fn, scope, options) {
                    options = options || noOptions;
                    scope = scope || this.observable;

                    var listener = {
                            fn: fn,
                            scope: scope,
                            o: options,
                            ev: this
                        },
                        handler = fn;

                    // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
                    // because the event removal that the single listener does destroys the listener's DelayedTask(s)
                    if (options.single) {
                        handler = createSingle(handler, listener, options, scope);
                    }
                    if (options.target) {
                        handler = createTargeted(handler, listener, options, scope);
                    }
                    if (options.delay) {
                        handler = createDelayed(handler, listener, options, scope);
                    }
                    if (options.buffer) {
                        handler = createBuffered(handler, listener, options, scope);
                    }

                    listener.fireFn = handler;
                    return listener;
                },

                findListener: function(fn, scope) {
                    var listeners = this.listeners,
                    i = listeners.length,
                    listener,
                    s;

                    while (i--) {
                        listener = listeners[i];
                        if (listener) {
                            s = listener.scope;

                            // Compare the listener's scope with *JUST THE PASSED SCOPE* if one is passed, and only fall back to the owning Observable if none is passed.
                            // We cannot use the test (s == scope || s == this.observable)
                            // Otherwise, if the Observable itself adds Ext.emptyFn as a listener, and then Ext.emptyFn is added under another scope, there will be a false match.
                            if (listener.fn == fn && (s == (scope || this.observable))) {
                                return i;
                            }
                        }
                    }

                    return - 1;
                },

                isListening: function(fn, scope) {
                    return this.findListener(fn, scope) !== -1;
                },

                removeListener: function(fn, scope) {
                    var me = this,
                        index,
                        listener,
                        k;
                    index = me.findListener(fn, scope);
                    if (index != -1) {
                        listener = me.listeners[index];

                        if (me.firing) {
                            me.listeners = me.listeners.slice(0);
                        }

                        // cancel and remove a buffered handler that hasn't fired yet
                        if (listener.task) {
                            listener.task.cancel();
                            delete listener.task;
                        }

                        // cancel and remove all delayed handlers that haven't fired yet
                        k = listener.tasks && listener.tasks.length;
                        if (k) {
                            while (k--) {
                                listener.tasks[k].cancel();
                            }
                            delete listener.tasks;
                        }

                        // remove this listener from the listeners array
                        Ext.Array.erase(me.listeners, index, 1);
                        return true;
                    }

                    return false;
                },

                // Iterate to stop any buffered/delayed events
                clearListeners: function() {
                    var listeners = this.listeners,
                        i = listeners.length;

                    while (i--) {
                        this.removeListener(listeners[i].fn, listeners[i].scope);
                    }
                },

                fire: function() {
                    var me = this,
                        listeners = me.listeners,
                        count = listeners.length,
                        i,
                        args,
                        listener;

                    if (count > 0) {
                        me.firing = true;
                        for (i = 0; i < count; i++) {
                            listener = listeners[i];
                            args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
                            if (listener.o) {
                                args.push(listener.o);
                            }
                            if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
                                return (me.firing = false);
                            }
                        }
                    }
                    me.firing = false;
                    return true;
                }
            };
        }()));
    });