/**
 * @class Ext.util.Observable
 */
Ext.apply(Ext.util.Observable.prototype, function(){    
    // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
    // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
    // private
    function getMethodEvent(method){
        var e = (this.methodEvents = this.methodEvents ||
        {})[method], returnValue, v, cancel, obj = this;
        
        if (!e) {
            this.methodEvents[method] = e = {};
            e.originalFn = this[method];
            e.methodName = method;
            e.before = [];
            e.after = [];
            
            var makeCall = function(fn, scope, args){
                if (!Ext.isEmpty(v = fn.apply(scope || obj, args))) {
                    if (Ext.isObject(v)) {
                        returnValue = !Ext.isEmpty(v.returnValue) ? v.returnValue : v;
                        cancel = !!v.cancel;
                    }
                    else 
                        if (v === false) {
                            cancel = true;
                        }
                        else {
                            returnValue = v;
                        }
                }
            };
            
            this[method] = function(){
                var args = Ext.toArray(arguments);
                returnValue = v = undefined;
                cancel = false;
                
                Ext.each(e.before, function(b){
                    makeCall(b.fn, b.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                });
                
                if (!Ext.isEmpty(v = e.originalFn.apply(obj, args))) {
                    returnValue = v;
                }
                Ext.each(e.after, function(a){
                    makeCall(a.fn, a.scope, args);
                    if (cancel) {
                        return returnValue;
                    }
                });
                return returnValue;
            };
        }
        return e;
    }
    
    return {
        // these are considered experimental
        // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
        // adds an "interceptor" called before the original method
        beforeMethod: function(method, fn, scope){
            getMethodEvent.call(this, method).before.push({
                fn: fn,
                scope: scope
            });
        },
        
        // adds a "sequence" called after the original method
        afterMethod: function(method, fn, scope){
            getMethodEvent.call(this, method).after.push({
                fn: fn,
                scope: scope
            });
        },
        
        removeMethodListener: function(method, fn, scope){
            var e = getMethodEvent.call(this, method), found = false;
            Ext.each(e.before, function(b, i, arr){
                if (b.fn == fn && b.scope == scope) {
                    arr.splice(i, 1);
                    found = true;
                    return false;
                }
            });
            if (!found) {
                Ext.each(e.after, function(a, i, arr){
                    if (a.fn == fn && a.scope == scope) {
                        arr.splice(i, 1);
                        return false;
                    }
                });
            }
        },
        
        
/** * Relays selected events from the specified Observable as if the events were fired by this. * @param {Object} o The Observable whose events this object is to relay. * @param {Array} events Array of event names to relay. */ relayEvents: function(o, events){ var me = this; function createHandler(ename){ return function(){ return me.fireEvent.apply(me, [ename].concat(Ext.toArray(arguments))); }; } Ext.each(events, function(ename){ me.events[ename] = me.events[ename] || true; o.on(ename, createHandler(ename), me); }); },
/** * Used to enable bubbling of events * @param {Object} events */ enableBubble: function(events){ var me = this; events = Ext.isArray(events) ? events : Ext.toArray(arguments); Ext.each(events, function(ename){ ename = ename.toLowerCase(); var ce = me.events[ename] || true; if (typeof ce == "boolean") { ce = new Ext.util.Event(me, ename); me.events[ename] = ce; } ce.bubble = true; }); } }; }());
/** * Starts capture on the specified Observable. All events will be passed * to the supplied function with the event name + standard signature of the event * before the event is fired. If the supplied function returns false, * the event will not fire. * @param {Observable} o The Observable to capture * @param {Function} fn The function to call * @param {Object} scope (optional) The scope (this object) for the fn * @static */ Ext.util.Observable.capture = function(o, fn, scope){ o.fireEvent = o.fireEvent.createInterceptor(fn, scope); };
/** * Sets observability on the passed class constructor.

*

This makes any event fired on any instance of the passed class also fire a single event through * the class allowing for central handling of events on many instances at once.

*

Usage:


Ext.util.Observable.observeClass(Ext.data.Connection);
Ext.data.Connection.on('beforerequest', function(con, options) {
    console.log("Ajax request made to " + options.url);
});
* @param {Function} c The class constructor to make observable. * @static */ Ext.util.Observable.observeClass = function(c){ Ext.apply(c, new Ext.util.Observable()); c.prototype.fireEvent = function(){ return (c.fireEvent.apply(c, arguments) !== false) && (Ext.util.Observable.prototype.fireEvent.apply(this, arguments) !== false); }; };