    (function(){

    var EXTUTIL = Ext.util,
        TOARRAY = Ext.toArray,
        EACH = Ext.each,
        ISOBJECT = Ext.isObject
        TRUE = true,
        FALSE = false;
    /**
     * @class Ext.util.Observable
     * Abstract base class that provides a common interface for publishing events. Subclasses are expected to
     * to have a property "events" with all the events defined.

     * For example:
     *
     Employee = function(name){
        this.name = name;
        this.addEvents({
            "fired" : true,
            "quit" : true
        });
     }
     Ext.extend(Employee, Ext.util.Observable);

     */
    EXTUTIL.Observable = function(){
        /**
         * @cfg {Object} listeners (optional) A config object containing one or more event handlers to be added to this
         * object during initialization.  This should be a valid listeners config object as specified in the
         * {@link #addListener} example for attaching multiple handlers at once.
         */
        var me = this, e = me.events;
        if(me.listeners){
            me.on(me.listeners);
            delete me.listeners;
        }
        me.events = e || {};
    };

    EXTUTIL.Observable.prototype = function(){
    	var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
            return s.toLowerCase();
        };

    	return {
    	    /**
    	     * Fires the specified event with the passed parameters (minus the event name).
             * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
             * by calling {@link #enableBubble}.
    	     * @param {String} eventName The name of the event to fire.
    	     * @param {Object...} args Variable number of parameters are passed to handlers.
    	     * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
    	     */

    	    fireEvent : function(){
    	        var a = TOARRAY(arguments),
    	        	ename = toLower(a[0]),
    	        	me = this,
    				ret = TRUE,
    	        	ce = me.events[ename],
    	        	q,
    				c;
                if (me.eventsSuspended === TRUE) {
    	            if (q = me.suspendedEventsQueue) {
    	                q.push(a);
    	            }
                }
                else if(ISOBJECT(ce) && ce.bubble){
    				if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
    					return FALSE;
    				}
                    c = me.getBubbleTarget && me.getBubbleTarget();
    				if(c && c.enableBubble) {
    					c.enableBubble(ename);
    					return c.fireEvent.apply(c, a);
    				}
    	        }
                else {
    	            if (ISOBJECT(ce)) {
    	                a.shift();
    	                ret = ce.fire.apply(ce, a);
    	            }
    	        }
    	        return ret;
    	    },

    	    /**
    	     * Appends an event handler to this component
    	     * @param {String}   eventName The type of event to listen for
    	     * @param {Function} handler The method the event invokes
    	     * @param {Object}   scope (optional) The scope in which to execute the handler
    	     * function. The handler function's "this" context.
    	     * @param {Object}   options (optional) An object containing handler configuration
    	     * properties. This may contain any of the following properties:
    	     * scope : ObjectThe scope in which to execute the handler function. The handler function's "this" context.
    	     * delay : NumberThe number of milliseconds to delay the invocation of the handler after the event fires.
    	     * single : BooleanTrue to add a handler to handle just the next firing of the event, and then remove itself.
    	     * buffer : NumberCauses the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
    	     * by the specified number of milliseconds. If the event fires again within that time, the original
    	     * handler is not invoked, but the new handler is scheduled in its place.
    	     * target : ObservableOnly call the handler if the event was fired on the target Observable, not
    	     * if the event was bubbled up from a child Observable.
    	     *

    	     *
    	     * Combining Options

    	     * Using the options argument, it is possible to combine different types of listeners:

    	     *

    	     * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
    	     *
    	el.on('click', this.onClick, this, {
    	    single: true,
    	    delay: 100,
    	    forumId: 4
    	});
    	     *
    	     * Attaching multiple handlers in 1 call

    	      * The method also allows for a single argument to be passed which is a config object containing properties
    	     * which specify multiple handlers.
    	     *
    	     *
    	foo.on({
    	    'click' : {
    	        fn: this.onClick,
    	        scope: this,
    	        delay: 100
    	    },
    	    'mouseover' : {
    	        fn: this.onMouseOver,
    	        scope: this
    	    },
    	    'mouseout' : {
    	        fn: this.onMouseOut,
    	        scope: this
    	    }
    	});
    	     *
    	     * Or a shorthand syntax:

    	     *
    	foo.on({
    	    'click' : this.onClick,
    	    'mouseover' : this.onMouseOver,
    	    'mouseout' : this.onMouseOut,
    	     scope: this
    	});
    	     */
    	    addListener : function(eventName, fn, scope, o){
    		    var me = this,
    		    	e,
    		    	oe,
    		    	isF,
    			ce;
    	        if (ISOBJECT(eventName)) {
    	            o = eventName;
    	            for (e in o){
    		            oe = o[e];
    	                if (!filterOptRe.test(e)) {
    		                me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
    	                }
    	            }
    	        } else {
    		        eventName = toLower(eventName);
    			    ce = me.events[eventName] || TRUE;
    		    	if (typeof ce == "boolean") {
    		            me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
    		        }
    		        ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
    	    	}
    	    },

    	    /**
    	     * Removes a listener
    	     * @param {String}   eventName     The type of event to listen for
    	     * @param {Function} handler        The handler to remove
    	     * @param {Object}   scope  (optional) The scope (this object) for the handler
    	     */
    	    removeListener : function(eventName, fn, scope){
    	        var ce = this.events[toLower(eventName)];
    	        if (ISOBJECT(ce)) {
    	            ce.removeListener(fn, scope);
    	        }
    	    },

    	    /**
    	     * Removes all listeners for this object
    	     */
    	    purgeListeners : function(){
    		    var events = this.events,
    		    	evt,
    		    	key;
    	        for(key in events){
    		        evt = events[key];
    	            if(ISOBJECT(evt)){
                        evt.clearListeners();
                    }
    	        }
    	    },

    	    /**
    	     * Used to define events on this Observable
    	     * @param {Object} object The object with the events defined
    	     */
    	    addEvents : function(o){
    		    var me = this;
    		    me.events = me.events || {};
    	        if (typeof o == 'string') {
    		        EACH(arguments, function(a) {
    			        me.events[a] = me.events[a] || TRUE;
    	            });
    	        } else {
    	            Ext.applyIf(me.events, o);
    	        }
    	    },

    	    /**
    	     * Checks to see if this object has any listeners for a specified event
    	     * @param {String} eventName The name of the event to check for
    	     * @return {Boolean} True if the event is being listened for, else false
    	     */
    	    hasListener : function(eventName){
    	        var e = this.events[eventName];
    	        return ISOBJECT(e) && e.listeners.length > 0;
    	    },

    	    /**
    	     * Suspend the firing of all events. (see {@link #resumeEvents})
    	     * @param queueSuspended {Boolean} Pass as true to queue up suspended events to be fired
    	     * after the {@link #resumeEvents} call instead of discarding all suspended events;
    	     */
    	    suspendEvents : function(queueSuspended){
    	        this.eventsSuspended = TRUE;
    	        if (queueSuspended){
                    this.suspendedEventsQueue = [];
                }
    	    },

    	    /**
    	     * Resume firing events. (see {@link #suspendEvents})
    	     * If events were suspended using the queueSuspended parameter, then all
    	     * events fired during event suspension will be sent to any listeners now.
    	     */
    	    resumeEvents : function(){
    		    var me = this;
    	        me.eventsSuspended = !delete me.suspendedEventQueue;
    	        EACH(me.suspendedEventsQueue, function(e) {
    	            me.fireEvent.apply(me, e);
    	        });
    	    }
    	}
    }();

    var OBSERVABLE = EXTUTIL.Observable.prototype;
    /**
     * Appends an event handler to this element (shorthand for addListener)
     * @param {String}   eventName     The type of event to listen for
     * @param {Function} handler        The method the event invokes
     * @param {Object}   scope (optional) The scope in which to execute the handler
     * function. The handler function's "this" context.
     * @param {Object}   options  (optional)
     * @method
     */
    OBSERVABLE.on = OBSERVABLE.addListener;
    /**
     * Removes a listener (shorthand for removeListener)
     * @param {String}   eventName     The type of event to listen for
     * @param {Function} handler        The handler to remove
     * @param {Object}   scope  (optional) The scope (this object) for the handler
     * @method
     */
    OBSERVABLE.un = OBSERVABLE.removeListener;

    /**
     * Removes all added captures from the Observable.
     * @param {Observable} o The Observable to release
     * @static
     */
    EXTUTIL.Observable.releaseCapture = function(o){
        o.fireEvent = OBSERVABLE.fireEvent;
    };

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

    function createBuffered(h, o, scope){
        var task = new EXTUTIL.DelayedTask();
        return function(){
            task.delay(o.buffer, h, scope, TOARRAY(arguments));
        };
    }

    function createSingle(h, e, fn, scope){
        return function(){
            e.removeListener(fn, scope);
            return h.apply(scope, arguments);
        };
    }

    function createDelayed(h, o, scope){
        return function(){
            var args = TOARRAY(arguments);
            (function(){
                h.apply(scope, args);
            }).defer(o.delay || 10);
        };
    };

    EXTUTIL.Event = function(obj, name){
        this.name = name;
        this.obj = obj;
        this.listeners = [];
    };

    EXTUTIL.Event.prototype = {
        addListener : function(fn, scope, options){
            var me = this,
            	l;
            scope = scope || me.obj;
            if(!me.isListening(fn, scope)){
                l = 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(l);
            }
        },

        createListener: function(fn, scope, o){
            o = o || {}, scope = scope || this.obj;
            var l = {
                fn: fn,
                scope: scope,
                options: o
            }, h = fn;
            if(o.target){
                h = createTargeted(h, o, scope);
            }
            if(o.delay){
                h = createDelayed(h, o, scope);
            }
            if(o.single){
                h = createSingle(h, this, fn, scope);
            }
            if(o.buffer){
                h = createBuffered(h, o, scope);
            }
            l.fireFn = h;
            return l;
        },

        findListener : function(fn, scope){
            var s, ret = -1
            EACH(this.listeners, function(l, i) {
                s = l.scope;
                if(l.fn == fn && (s == scope || s == this.obj)){
                    ret = i;
                    return FALSE;
                }
            },
            this);
            return ret;
        },

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

        removeListener : function(fn, scope){
            var index,
            	me = this,
            	ret = FALSE;
            if((index = me.findListener(fn, scope)) != -1){
                if (me.firing) {
                    me.listeners = me.listeners.slice(0);
                }
                me.listeners.splice(index, 1);
                ret = TRUE;
            }
            return ret;
        },

        clearListeners : function(){
            this.listeners = [];
        },

        fire : function(){
            var me = this,
            	args = TOARRAY(arguments),
            	ret = TRUE;

            EACH(me.listeners, function(l) {
                me.firing = TRUE;
                if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
                    return ret = me.firing = FALSE;
                }
            });
            me.firing = FALSE;
            return ret;
        }
    };
    })();