Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / docs / source / Observable.html
index 327b387..7c3bcb4 100644 (file)
+<!DOCTYPE html>
 <html>
 <head>
-  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />    
+  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
   <title>The source code</title>
-    <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
-    <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
+  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
+  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
+  <style type="text/css">
+    .highlight { display: block; background-color: #ddd; }
+  </style>
+  <script type="text/javascript">
+    function highlight() {
+      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
+    }
+  </script>
 </head>
-<body  onload="prettyPrint();">
-    <pre class="prettyprint lang-js">/*!
- * Ext JS Library 3.2.2
- * Copyright(c) 2006-2010 Ext JS, Inc.
- * licensing@extjs.com
- * http://www.extjs.com/license
- */
-(function(){
-
-var EXTUTIL = Ext.util,
-    EACH = Ext.each,
-    TRUE = true,
-    FALSE = false;
-<div id="cls-Ext.util.Observable"></div>/**
- * @class Ext.util.Observable
- * Base class that provides a common interface for publishing events. Subclasses are expected to
- * to have a property "events" with all the events defined, and, optionally, a property "listeners"
- * with configured listeners defined.<br>
+<body onload="prettyPrint(); highlight();">
+  <pre class="prettyprint lang-js"><span id='Ext-util-Observable'>/**
+</span> * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
+ * &quot;events&quot; with all the events defined, and, optionally, a property &quot;listeners&quot; with configured listeners defined.
+ *
  * For example:
- * <pre><code>
-Employee = Ext.extend(Ext.util.Observable, {
-    constructor: function(config){
-        this.name = config.name;
-        this.addEvents({
-            "fired" : true,
-            "quit" : true
-        });
-
-        // Copy configured listeners into *this* object so that the base class&#39;s
-        // constructor will add them.
-        this.listeners = config.listeners;
-
-        // Call our superclass constructor to complete construction process.
-        Employee.superclass.constructor.call(this, config)
-    }
-});
-</code></pre>
- * This could then be used like this:<pre><code>
-var newEmployee = new Employee({
-    name: employeeName,
-    listeners: {
-        quit: function() {
-            // By default, "this" will be the object that fired the event.
-            alert(this.name + " has quit!");
-        }
-    }
-});
-</code></pre>
+ *
+ *     Ext.define('Employee', {
+ *         extend: 'Ext.util.Observable',
+ *         constructor: function(config){
+ *             this.name = config.name;
+ *             this.addEvents({
+ *                 &quot;fired&quot; : true,
+ *                 &quot;quit&quot; : true
+ *             });
+ *
+ *             // Copy configured listeners into *this* object so that the base class's
+ *             // constructor will add them.
+ *             this.listeners = config.listeners;
+ *
+ *             // Call our superclass constructor to complete construction process.
+ *             this.callParent(arguments)
+ *         }
+ *     });
+ *
+ * This could then be used like this:
+ *
+ *     var newEmployee = new Employee({
+ *         name: employeeName,
+ *         listeners: {
+ *             quit: function() {
+ *                 // By default, &quot;this&quot; will be the object that fired the event.
+ *                 alert(this.name + &quot; has quit!&quot;);
+ *             }
+ *         }
+ *     });
  */
-EXTUTIL.Observable = function(){
-    <div id="cfg-Ext.util.Observable-listeners"></div>/**
-     * @cfg {Object} listeners (optional) <p>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.</p>
-     * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
-     * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
-     * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
-     * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
-     * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
-     * has been rendered. A plugin can simplify this step:<pre><code>
-// Plugin is configured with a listeners config object.
-// The Component is appended to the argument list of all handler functions.
-Ext.DomObserver = Ext.extend(Object, {
-    constructor: function(config) {
-        this.listeners = config.listeners ? config.listeners : config;
+Ext.define('Ext.util.Observable', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.util.Event'],
+
+    statics: {
+<span id='Ext-util-Observable-static-method-releaseCapture'>        /**
+</span>         * Removes **all** added captures from the Observable.
+         *
+         * @param {Ext.util.Observable} o The Observable to release
+         * @static
+         */
+        releaseCapture: function(o) {
+            o.fireEvent = this.prototype.fireEvent;
+        },
+
+<span id='Ext-util-Observable-static-method-capture'>        /**
+</span>         * 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 {Ext.util.Observable} o The Observable to capture events from.
+         * @param {Function} fn The function to call when an event is fired.
+         * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
+         * the Observable firing the event.
+         * @static
+         */
+        capture: function(o, fn, scope) {
+            o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
+        },
+
+<span id='Ext-util-Observable-static-method-observe'>        /**
+</span>         * 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.observe(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.
+         * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
+         * @static
+         */
+        observe: function(cls, listeners) {
+            if (cls) {
+                if (!cls.isObservable) {
+                    Ext.applyIf(cls, new this());
+                    this.capture(cls.prototype, cls.fireEvent, cls);
+                }
+                if (Ext.isObject(listeners)) {
+                    cls.on(listeners);
+                }
+                return cls;
+            }
+        }
     },
 
-    // Component passes itself into plugin&#39;s init method
-    init: function(c) {
-        var p, l = this.listeners;
-        for (p in l) {
-            if (Ext.isFunction(l[p])) {
-                l[p] = this.createHandler(l[p], c);
-            } else {
-                l[p].fn = this.createHandler(l[p].fn, c);
-            }
+    /* End Definitions */
+
+<span id='Ext-util-Observable-cfg-listeners'>    /**
+</span>     * @cfg {Object} listeners
+     *
+     * 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.
+     *
+     * **DOM events from Ext JS {@link Ext.Component Components}**
+     *
+     * While _some_ Ext JS Component classes export selected DOM events (e.g. &quot;click&quot;, &quot;mouseover&quot; etc), this is usually
+     * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
+     * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
+     * child element of a Component, we need to specify the `element` option to identify the Component property to add a
+     * DOM listener to:
+     *
+     *     new Ext.panel.Panel({
+     *         width: 400,
+     *         height: 200,
+     *         dockedItems: [{
+     *             xtype: 'toolbar'
+     *         }],
+     *         listeners: {
+     *             click: {
+     *                 element: 'el', //bind to the underlying el property on the panel
+     *                 fn: function(){ console.log('click el'); }
+     *             },
+     *             dblclick: {
+     *                 element: 'body', //bind to the underlying body property on the panel
+     *                 fn: function(){ console.log('dblclick body'); }
+     *             }
+     *         }
+     *     });
+     */
+    // @private
+    isObservable: true,
+
+    constructor: function(config) {
+        var me = this;
+
+        Ext.apply(me, config);
+        if (me.listeners) {
+            me.on(me.listeners);
+            delete me.listeners;
         }
+        me.events = me.events || {};
+
+        if (me.bubbleEvents) {
+            me.enableBubble(me.bubbleEvents);
+        }
+    },
 
-        // Add the listeners to the Element immediately following the render call
-        c.render = c.render.{@link Function#createSequence createSequence}(function() {
-            var e = c.getEl();
-            if (e) {
-                e.on(l);
+    // @private
+    eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
+
+<span id='Ext-util-Observable-method-addManagedListener'>    /**
+</span>     * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
+     * destroyed.
+     *
+     * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
+     * @param {Object/String} ename The event name, or an object containing event name properties.
+     * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
+     * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
+     * in which the handler function is executed.
+     * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
+     * {@link Ext.util.Observable#addListener addListener} options.
+     */
+    addManagedListener : function(item, ename, fn, scope, options) {
+        var me = this,
+            managedListeners = me.managedListeners = me.managedListeners || [],
+            config;
+
+        if (typeof ename !== 'string') {
+            options = ename;
+            for (ename in options) {
+                if (options.hasOwnProperty(ename)) {
+                    config = options[ename];
+                    if (!me.eventOptionsRe.test(ename)) {
+                        me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
+                    }
+                }
             }
-        });
+        }
+        else {
+            managedListeners.push({
+                item: item,
+                ename: ename,
+                fn: fn,
+                scope: scope,
+                options: options
+            });
+
+            item.on(ename, fn, scope, options);
+        }
     },
 
-    createHandler: function(fn, c) {
-        return function(e) {
-            fn.call(this, e, c);
-        };
-    }
-});
+<span id='Ext-util-Observable-method-removeManagedListener'>    /**
+</span>     * Removes listeners that were added by the {@link #mon} method.
+     *
+     * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
+     * @param {Object/String} ename The event name, or an object containing event name properties.
+     * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
+     * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
+     * in which the handler function is executed.
+     */
+    removeManagedListener : function(item, ename, fn, scope) {
+        var me = this,
+            options,
+            config,
+            managedListeners,
+            length,
+            i;
+
+        if (typeof ename !== 'string') {
+            options = ename;
+            for (ename in options) {
+                if (options.hasOwnProperty(ename)) {
+                    config = options[ename];
+                    if (!me.eventOptionsRe.test(ename)) {
+                        me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
+                    }
+                }
+            }
+        }
 
-var combo = new Ext.form.ComboBox({
+        managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
 
-    // Collapse combo when its element is clicked on
-    plugins: [ new Ext.DomObserver({
-        click: function(evt, comp) {
-            comp.collapse();
+        for (i = 0, length = managedListeners.length; i &lt; length; i++) {
+            me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
         }
-    })],
-    store: myStore,
-    typeAhead: true,
-    mode: 'local',
-    triggerAction: 'all'
-});
-     * </code></pre></p>
-     */
-    var me = this, e = me.events;
-    if(me.listeners){
-        me.on(me.listeners);
-        delete me.listeners;
-    }
-    me.events = e || {};
-};
-
-EXTUTIL.Observable.prototype = {
-    // private
-    filterOptRe : /^(?:scope|delay|buffer|single)$/,
+    },
 
-    <div id="method-Ext.util.Observable-fireEvent"></div>/**
-     * <p>Fires the specified event with the passed parameters (minus the event name).</p>
-     * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
-     * by calling {@link #enableBubble}.</p>
+<span id='Ext-util-Observable-method-fireEvent'>    /**
+</span>     * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
+     * to {@link #addListener}).
+     *
+     * 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 = Array.prototype.slice.call(arguments, 0),
-            ename = a[0].toLowerCase(),
-            me = this,
-            ret = TRUE,
-            ce = me.events[ename],
-            cc,
-            q,
-            c;
-        if (me.eventsSuspended === TRUE) {
-            if (q = me.eventQueue) {
-                q.push(a);
-            }
-        }
-        else if(typeof ce == 'object') {
-            if (ce.bubble){
-                if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
-                    return FALSE;
+    fireEvent: function(eventName) {
+        var name = eventName.toLowerCase(),
+            events = this.events,
+            event = events &amp;&amp; events[name],
+            bubbles = event &amp;&amp; event.bubble;
+
+        return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles);
+    },
+
+<span id='Ext-util-Observable-method-continueFireEvent'>    /**
+</span>     * Continue to fire event.
+     * @private
+     *
+     * @param {String} eventName
+     * @param {Array} args
+     * @param {Boolean} bubbles
+     */
+    continueFireEvent: function(eventName, args, bubbles) {
+        var target = this,
+            queue, event,
+            ret = true;
+
+        do {
+            if (target.eventsSuspended === true) {
+                if ((queue = target.eventQueue)) {
+                    queue.push([eventName, args, bubbles]);
                 }
-                c = me.getBubbleTarget && me.getBubbleTarget();
-                if(c && c.enableBubble) {
-                    cc = c.events[ename];
-                    if(!cc || typeof cc != 'object' || !cc.bubble) {
-                        c.enableBubble(ename);
+                return ret;
+            } else {
+                event = target.events[eventName];
+                // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
+                // configure to bubble.
+                if (event &amp;&amp; event != true) {
+                    if ((ret = event.fire.apply(event, args)) === false) {
+                        break;
                     }
-                    return c.fireEvent.apply(c, a);
                 }
             }
-            else {
-                a.shift();
-                ret = ce.fire.apply(ce, a);
-            }
-        }
+        } while (bubbles &amp;&amp; (target = target.getBubbleParent()));
         return ret;
     },
 
-    <div id="method-Ext.util.Observable-addListener"></div>/**
-     * Appends an event handler to this object.
-     * @param {String}   eventName The name of the event to listen for.
-     * @param {Function} handler The method the event invokes.
-     * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
-     * <b>If omitted, defaults to the object which fired the event.</b>
-     * @param {Object}   options (optional) An object containing handler configuration.
-     * properties. This may contain any of the following properties:<ul>
-     * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
-     * <b>If omitted, defaults to the object which fired the event.</b></div></li>
-     * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
-     * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
-     * <li><b>buffer</b> : Number<div class="sub-desc">Causes 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 <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
-     * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
-     * if the event was bubbled up from a child Observable.</div></li>
-     * </ul><br>
-     * <p>
-     * <b>Combining Options</b><br>
-     * Using the options argument, it is possible to combine different types of listeners:<br>
-     * <br>
+<span id='Ext-util-Observable-method-getBubbleParent'>    /**
+</span>     * Gets the bubbling parent for an Observable
+     * @private
+     * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
+     */
+    getBubbleParent: function(){
+        var me = this, parent = me.getBubbleTarget &amp;&amp; me.getBubbleTarget();
+        if (parent &amp;&amp; parent.isObservable) {
+            return parent;
+        }
+        return null;
+    },
+
+<span id='Ext-util-Observable-method-addListener'>    /**
+</span>     * Appends an event handler to this object.
+     *
+     * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
+     * event names.
+     * @param {Function} fn The method the event invokes.  Will be called with arguments given to
+     * {@link #fireEvent} plus the `options` parameter described below.
+     * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
+     * omitted, defaults to the object which fired the event.**
+     * @param {Object} [options] An object containing handler configuration.
+     *
+     * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
+     *
+     * This object may contain any of the following properties:
+     *
+     * - **scope** : Object
+     *
+     *   The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
+     *   which fired the event.**
+     *
+     * - **delay** : Number
+     *
+     *   The number of milliseconds to delay the invocation of the handler after the event fires.
+     *
+     * - **single** : Boolean
+     *
+     *   True to add a handler to handle just the next firing of the event, and then remove itself.
+     *
+     * - **buffer** : Number
+     *
+     *   Causes 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** : Observable
+     *
+     *   Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
+     *   child Observable.
+     *
+     * - **element** : String
+     *
+     *   **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
+     *   property which references an element to add a listener to.
+     *
+     *   This option is useful during Component construction to add DOM event listeners to elements of
+     *   {@link Ext.Component Components} which will exist only after the Component is rendered.
+     *   For example, to add a click listener to a Panel's body:
+     *
+     *       new Ext.panel.Panel({
+     *           title: 'The title',
+     *           listeners: {
+     *               click: this.handlePanelClick,
+     *               element: 'body'
+     *           }
+     *       });
+     *
+     * **Combining Options**
+     *
+     * Using the options argument, it is possible to combine different types of listeners:
+     *
      * A delayed, one-time listener.
-     * <pre><code>
-myDataView.on('click', this.onClick, this, {
-single: true,
-delay: 100
-});</code></pre>
-     * <p>
-     * <b>Attaching multiple handlers in 1 call</b><br>
-     * The method also allows for a single argument to be passed which is a config object containing properties
-     * which specify multiple handlers.
-     * <p>
-     * <pre><code>
-myGridPanel.on({
-'click' : {
-    fn: this.onClick,
-    scope: this,
-    delay: 100
-},
-'mouseover' : {
-    fn: this.onMouseOver,
-    scope: this
-},
-'mouseout' : {
-    fn: this.onMouseOut,
-    scope: this
-}
-});</code></pre>
- * <p>
- * Or a shorthand syntax:<br>
- * <pre><code>
-myGridPanel.on({
-'click' : this.onClick,
-'mouseover' : this.onMouseOver,
-'mouseout' : this.onMouseOut,
- scope: this
-});</code></pre>
+     *
+     *     myPanel.on('hide', this.handleClick, this, {
+     *         single: true,
+     *         delay: 100
+     *     });
+     *
+     * **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 events. For example:
+     *
+     *     myGridPanel.on({
+     *         cellClick: this.onCellClick,
+     *         mouseover: this.onMouseOver,
+     *         mouseout: this.onMouseOut,
+     *         scope: this // Important. Ensure &quot;this&quot; is correct during handler execution
+     *     });
+     *
+     * One can also specify options for each event handler separately:
+     *
+     *     myGridPanel.on({
+     *         cellClick: {fn: this.onCellClick, scope: this, single: true},
+     *         mouseover: {fn: panel.onMouseOver, scope: panel}
+     *     });
+     *
      */
-    addListener : function(eventName, fn, scope, o){
+    addListener: function(ename, fn, scope, options) {
         var me = this,
-            e,
-            oe,
-            ce;
-            
-        if (typeof eventName == 'object') {
-            o = eventName;
-            for (e in o) {
-                oe = o[e];
-                if (!me.filterOptRe.test(e)) {
-                    me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
+            config,
+            event;
+
+        if (typeof ename !== 'string') {
+            options = ename;
+            for (ename in options) {
+                if (options.hasOwnProperty(ename)) {
+                    config = options[ename];
+                    if (!me.eventOptionsRe.test(ename)) {
+                        me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
+                    }
                 }
             }
-        } else {
-            eventName = eventName.toLowerCase();
-            ce = me.events[eventName] || TRUE;
-            if (typeof ce == 'boolean') {
-                me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
+        }
+        else {
+            ename = ename.toLowerCase();
+            me.events[ename] = me.events[ename] || true;
+            event = me.events[ename] || true;
+            if (Ext.isBoolean(event)) {
+                me.events[ename] = event = new Ext.util.Event(me, ename);
             }
-            ce.addListener(fn, scope, typeof o == 'object' ? o : {});
+            event.addListener(fn, scope, Ext.isObject(options) ? options : {});
         }
     },
 
-    <div id="method-Ext.util.Observable-removeListener"></div>/**
-     * Removes an event handler.
-     * @param {String}   eventName The type of event the handler was associated with.
-     * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
-     * @param {Object}   scope     (optional) The scope originally specified for the handler.
+<span id='Ext-util-Observable-method-removeListener'>    /**
+</span>     * Removes an event handler.
+     *
+     * @param {String} eventName The type of event the handler was associated with.
+     * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
+     * {@link #addListener} call.**
+     * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
+     * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
      */
-    removeListener : function(eventName, fn, scope){
-        var ce = this.events[eventName.toLowerCase()];
-        if (typeof ce == 'object') {
-            ce.removeListener(fn, scope);
+    removeListener: function(ename, fn, scope) {
+        var me = this,
+            config,
+            event,
+            options;
+
+        if (typeof ename !== 'string') {
+            options = ename;
+            for (ename in options) {
+                if (options.hasOwnProperty(ename)) {
+                    config = options[ename];
+                    if (!me.eventOptionsRe.test(ename)) {
+                        me.removeListener(ename, config.fn || config, config.scope || options.scope);
+                    }
+                }
+            }
+        } else {
+            ename = ename.toLowerCase();
+            event = me.events[ename];
+            if (event &amp;&amp; event.isEvent) {
+                event.removeListener(fn, scope);
+            }
         }
     },
 
-    <div id="method-Ext.util.Observable-purgeListeners"></div>/**
-     * Removes all listeners for this object
+<span id='Ext-util-Observable-method-clearListeners'>    /**
+</span>     * Removes all listeners for this object including the managed listeners
      */
-    purgeListeners : function(){
+    clearListeners: function() {
         var events = this.events,
-            evt,
+            event,
             key;
-        for(key in events){
-            evt = events[key];
-            if(typeof evt == 'object'){
-                evt.clearListeners();
+
+        for (key in events) {
+            if (events.hasOwnProperty(key)) {
+                event = events[key];
+                if (event.isEvent) {
+                    event.clearListeners();
+                }
             }
         }
+
+        this.clearManagedListeners();
     },
 
-    <div id="method-Ext.util.Observable-addEvents"></div>/**
-     * Adds the specified events to the list of events which this Observable may fire.
-     * @param {Object|String} o Either an object with event names as properties with a value of <code>true</code>
-     * or the first event name string if multiple event names are being passed as separate parameters.
-     * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
-     * Usage:<pre><code>
-this.addEvents('storeloaded', 'storecleared');
-</code></pre>
+    //&lt;debug&gt;
+    purgeListeners : function() {
+        if (Ext.global.console) {
+            Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
+        }
+        return this.clearListeners.apply(this, arguments);
+    },
+    //&lt;/debug&gt;
+
+<span id='Ext-util-Observable-method-clearManagedListeners'>    /**
+</span>     * Removes all managed listeners for this object.
      */
-    addEvents : function(o){
-        var me = this;
-        me.events = me.events || {};
-        if (typeof o == 'string') {
-            var a = arguments,
-                i = a.length;
-            while(i--) {
-                me.events[a[i]] = me.events[a[i]] || TRUE;
+    clearManagedListeners : function() {
+        var managedListeners = this.managedListeners || [],
+            i = 0,
+            len = managedListeners.length;
+
+        for (; i &lt; len; i++) {
+            this.removeManagedListenerItem(true, managedListeners[i]);
+        }
+
+        this.managedListeners = [];
+    },
+
+<span id='Ext-util-Observable-method-removeManagedListenerItem'>    /**
+</span>     * Remove a single managed listener item
+     * @private
+     * @param {Boolean} isClear True if this is being called during a clear
+     * @param {Object} managedListener The managed listener item
+     * See removeManagedListener for other args
+     */
+    removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
+        if (isClear || (managedListener.item === item &amp;&amp; managedListener.ename === ename &amp;&amp; (!fn || managedListener.fn === fn) &amp;&amp; (!scope || managedListener.scope === scope))) {
+            managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
+            if (!isClear) {
+                Ext.Array.remove(this.managedListeners, managedListener);
+            }
+        }
+    },
+
+    //&lt;debug&gt;
+    purgeManagedListeners : function() {
+        if (Ext.global.console) {
+            Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
+        }
+        return this.clearManagedListeners.apply(this, arguments);
+    },
+    //&lt;/debug&gt;
+
+<span id='Ext-util-Observable-method-addEvents'>    /**
+</span>     * Adds the specified events to the list of events which this Observable may fire.
+     *
+     * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
+     * event name string if multiple event names are being passed as separate parameters. Usage:
+     *
+     *     this.addEvents({
+     *         storeloaded: true,
+     *         storecleared: true
+     *     });
+     *
+     * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate
+     * parameters. Usage:
+     *
+     *     this.addEvents('storeloaded', 'storecleared');
+     *
+     */
+    addEvents: function(o) {
+        var me = this,
+            args,
+            len,
+            i;
+
+            me.events = me.events || {};
+        if (Ext.isString(o)) {
+            args = arguments;
+            i = args.length;
+
+            while (i--) {
+                me.events[args[i]] = me.events[args[i]] || true;
             }
         } else {
             Ext.applyIf(me.events, o);
         }
     },
 
-    <div id="method-Ext.util.Observable-hasListener"></div>/**
-     * Checks to see if this object has any listeners for a specified event
+<span id='Ext-util-Observable-method-hasListener'>    /**
+</span>     * 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.toLowerCase()];
-        return typeof e == 'object' && e.listeners.length > 0;
+    hasListener: function(ename) {
+        var event = this.events[ename.toLowerCase()];
+        return event &amp;&amp; event.isEvent === true &amp;&amp; event.listeners.length &gt; 0;
     },
 
-    <div id="method-Ext.util.Observable-suspendEvents"></div>/**
-     * Suspend the firing of all events. (see {@link #resumeEvents})
+<span id='Ext-util-Observable-method-suspendEvents'>    /**
+</span>     * Suspends the firing of all events. (see {@link #resumeEvents})
+     *
      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
-     * after the {@link #resumeEvents} call instead of discarding all suspended events;
+     * after the {@link #resumeEvents} call instead of discarding all suspended events.
      */
-    suspendEvents : function(queueSuspended){
-        this.eventsSuspended = TRUE;
-        if(queueSuspended && !this.eventQueue){
+    suspendEvents: function(queueSuspended) {
+        this.eventsSuspended = true;
+        if (queueSuspended &amp;&amp; !this.eventQueue) {
             this.eventQueue = [];
         }
     },
 
-    <div id="method-Ext.util.Observable-resumeEvents"></div>/**
-     * Resume firing events. (see {@link #suspendEvents})
-     * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
-     * events fired during event suspension will be sent to any listeners now.
+<span id='Ext-util-Observable-method-resumeEvents'>    /**
+</span>     * Resumes 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(){
+    resumeEvents: function() {
         var me = this,
-            queued = me.eventQueue || [];
-        me.eventsSuspended = FALSE;
+            queued = me.eventQueue;
+
+        me.eventsSuspended = false;
         delete me.eventQueue;
-        EACH(queued, function(e) {
-            me.fireEvent.apply(me, e);
-        });
-    }
-};
-
-var OBSERVABLE = EXTUTIL.Observable.prototype;
-<div id="method-Ext.util.Observable-on"></div>/**
- * Appends an event handler to this object (shorthand for {@link #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 (<code><b>this</b></code> reference) in which the handler function is executed.
- * <b>If omitted, defaults to the object which fired the event.</b>
- * @param {Object}   options       (optional) An object containing handler configuration.
- * @method
- */
-OBSERVABLE.on = OBSERVABLE.addListener;
-<div id="method-Ext.util.Observable-un"></div>/**
- * Removes an event handler (shorthand for {@link #removeListener}.)
- * @param {String}   eventName     The type of event the handler was associated with.
- * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
- * @param {Object}   scope         (optional) The scope originally specified for the handler.
- * @method
- */
-OBSERVABLE.un = OBSERVABLE.removeListener;
 
-<div id="method-Ext.util.Observable-Observable.releaseCapture"></div>/**
- * Removes <b>all</b> 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, Array.prototype.slice.call(arguments, 0));
+        if (queued) {
+            Ext.each(queued, function(e) {
+                me.continueFireEvent.apply(me, e);
+            });
         }
-    };
-};
-
-function createBuffered(h, o, l, scope){
-    l.task = new EXTUTIL.DelayedTask();
-    return function(){
-        l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
-    };
-};
-
-function createSingle(h, e, fn, scope){
-    return function(){
-        e.removeListener(fn, scope);
-        return h.apply(scope, arguments);
-    };
-};
-
-function createDelayed(h, o, l, scope){
-    return function(){
-        var task = new EXTUTIL.DelayedTask();
-        if(!l.tasks) {
-            l.tasks = [];
-        }
-        l.tasks.push(task);
-        task.delay(o.delay || 10, h, scope, Array.prototype.slice.call(arguments, 0));
-    };
-};
-
-EXTUTIL.Event = function(obj, name){
-    this.name = name;
-    this.obj = obj;
-    this.listeners = [];
-};
-
-EXTUTIL.Event.prototype = {
-    addListener : function(fn, scope, options){
+    },
+
+<span id='Ext-util-Observable-method-relayEvents'>    /**
+</span>     * Relays selected events from the specified Observable as if the events were fired by `this`.
+     *
+     * @param {Object} origin The Observable whose events this object is to relay.
+     * @param {String[]} events Array of event names to relay.
+     * @param {String} prefix
+     */
+    relayEvents : function(origin, events, prefix) {
+        prefix = prefix || '';
         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);
+            len = events.length,
+            i = 0,
+            oldName,
+            newName;
+
+        for (; i &lt; len; i++) {
+            oldName = events[i].substr(prefix.length);
+            newName = prefix + oldName;
+            me.events[newName] = me.events[newName] || true;
+            origin.on(oldName, me.createRelayer(newName));
         }
     },
 
-    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, l, scope);
-        }
-        if(o.single){
-            h = createSingle(h, this, fn, scope);
-        }
-        if(o.buffer){
-            h = createBuffered(h, o, l, scope);
-        }
-        l.fireFn = h;
-        return l;
+<span id='Ext-util-Observable-method-createRelayer'>    /**
+</span>     * @private
+     * Creates an event handling function which refires the event from this object as the passed event name.
+     * @param newName
+     * @returns {Function}
+     */
+    createRelayer: function(newName){
+        var me = this;
+        return function(){
+            return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
+        };
     },
 
-    findListener : function(fn, scope){
-        var list = this.listeners,
-            i = list.length,
-            l;
-
-        scope = scope || this.obj;
-        while(i--){
-            l = list[i];
-            if(l){
-                if(l.fn == fn && l.scope == scope){
-                    return i;
+<span id='Ext-util-Observable-method-enableBubble'>    /**
+</span>     * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
+     * present. There is no implementation in the Observable base class.
+     *
+     * This is commonly used by Ext.Components to bubble events to owner Containers.
+     * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
+     * Component's immediate owner. But if a known target is required, this can be overridden to access the
+     * required target more quickly.
+     *
+     * Example:
+     *
+     *     Ext.override(Ext.form.field.Base, {
+     *         //  Add functionality to Field's initComponent to enable the change event to bubble
+     *         initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
+     *             this.enableBubble('change');
+     *         }),
+     *
+     *         //  We know that we want Field's events to bubble directly to the FormPanel.
+     *         getBubbleTarget : function() {
+     *             if (!this.formPanel) {
+     *                 this.formPanel = this.findParentByType('form');
+     *             }
+     *             return this.formPanel;
+     *         }
+     *     });
+     *
+     *     var myForm = new Ext.formPanel({
+     *         title: 'User Details',
+     *         items: [{
+     *             ...
+     *         }],
+     *         listeners: {
+     *             change: function() {
+     *                 // Title goes red if form has been modified.
+     *                 myForm.header.setStyle('color', 'red');
+     *             }
+     *         }
+     *     });
+     *
+     * @param {String/String[]} events The event name to bubble, or an Array of event names.
+     */
+    enableBubble: function(events) {
+        var me = this;
+        if (!Ext.isEmpty(events)) {
+            events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
+            Ext.each(events,
+            function(ename) {
+                ename = ename.toLowerCase();
+                var ce = me.events[ename] || true;
+                if (Ext.isBoolean(ce)) {
+                    ce = new Ext.util.Event(me, ename);
+                    me.events[ename] = ce;
                 }
-            }
+                ce.bubble = true;
+            });
         }
-        return -1;
-    },
+    }
+}, function() {
+
+    this.createAlias({
+<span id='Ext-util-Observable-method-on'>        /**
+</span>         * @method
+         * Shorthand for {@link #addListener}.
+         * @alias Ext.util.Observable#addListener
+         */
+        on: 'addListener',
+<span id='Ext-util-Observable-method-un'>        /**
+</span>         * @method
+         * Shorthand for {@link #removeListener}.
+         * @alias Ext.util.Observable#removeListener
+         */
+        un: 'removeListener',
+<span id='Ext-util-Observable-method-mon'>        /**
+</span>         * @method
+         * Shorthand for {@link #addManagedListener}.
+         * @alias Ext.util.Observable#addManagedListener
+         */
+        mon: 'addManagedListener',
+<span id='Ext-util-Observable-method-mun'>        /**
+</span>         * @method
+         * Shorthand for {@link #removeManagedListener}.
+         * @alias Ext.util.Observable#removeManagedListener
+         */
+        mun: 'removeManagedListener'
+    });
+
+    //deprecated, will be removed in 5.0
+    this.observeClass = this.observe;
+
+    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((v = fn.apply(scope || obj, args)) !== undefined){
+                        if (typeof v == 'object') {
+                            if(v.returnValue !== undefined){
+                                returnValue = v.returnValue;
+                            }else{
+                                returnValue = v;
+                            }
+                            cancel = !!v.cancel;
+                        }
+                        else
+                            if (v === false) {
+                                cancel = true;
+                            }
+                            else {
+                                returnValue = v;
+                            }
+                    }
+                };
+
+                this[method] = function(){
+                    var args = Array.prototype.slice.call(arguments, 0),
+                        b, i, len;
+                    returnValue = v = undefined;
+                    cancel = false;
+
+                    for(i = 0, len = e.before.length; i &lt; len; i++){
+                        b = e.before[i];
+                        makeCall(b.fn, b.scope, args);
+                        if (cancel) {
+                            return returnValue;
+                        }
+                    }
 
-    isListening : function(fn, scope){
-        return this.findListener(fn, scope) != -1;
-    },
+                    if((v = e.originalFn.apply(obj, args)) !== undefined){
+                        returnValue = v;
+                    }
 
-    removeListener : function(fn, scope){
-        var index,
-            l,
-            k,
-            me = this,
-            ret = FALSE;
-        if((index = me.findListener(fn, scope)) != -1){
-            if (me.firing) {
-                me.listeners = me.listeners.slice(0);
-            }
-            l = me.listeners[index];
-            if(l.task) {
-                l.task.cancel();
-                delete l.task;
-            }
-            k = l.tasks && l.tasks.length;
-            if(k) {
-                while(k--) {
-                    l.tasks[k].cancel();
-                }
-                delete l.tasks;
+                    for(i = 0, len = e.after.length; i &lt; len; i++){
+                        b = e.after[i];
+                        makeCall(b.fn, b.scope, args);
+                        if (cancel) {
+                            return returnValue;
+                        }
+                    }
+                    return returnValue;
+                };
             }
-            me.listeners.splice(index, 1);
-            ret = TRUE;
-        }
-        return ret;
-    },
-
-    // Iterate to stop any buffered/delayed events
-    clearListeners : function(){
-        var me = this,
-            l = me.listeners,
-            i = l.length;
-        while(i--) {
-            me.removeListener(l[i].fn, l[i].scope);
+            return e;
         }
-    },
 
-    fire : function(){
-        var me = this,
-            listeners = me.listeners,
-            len = listeners.length,
-            i = 0,
-            l;
-
-        if(len > 0){
-            me.firing = TRUE;
-            var args = Array.prototype.slice.call(arguments, 0);
-            for (; i < len; i++) {
-                l = listeners[i];
-                if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
-                    return (me.firing = FALSE);
+        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 = this.getMethodEvent(method),
+                    i, len;
+                for(i = 0, len = e.before.length; i &lt; len; i++){
+                    if(e.before[i].fn == fn &amp;&amp; e.before[i].scope == scope){
+                        Ext.Array.erase(e.before, i, 1);
+                        return;
+                    }
                 }
-            }
-        }
-        me.firing = FALSE;
-        return TRUE;
-    }
+                for(i = 0, len = e.after.length; i &lt; len; i++){
+                    if(e.after[i].fn == fn &amp;&amp; e.after[i].scope == scope){
+                        Ext.Array.erase(e.after, i, 1);
+                        return;
+                    }
+                }
+            },
 
-};
-})();
-</pre>    
+            toggleEventLogging: function(toggle) {
+                Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
+                    if (Ext.isDefined(Ext.global.console)) {
+                        Ext.global.console.log(en, arguments);
+                    }
+                });
+            }
+        };
+    }());
+});
+</pre>
 </body>
-</html>
\ No newline at end of file
+</html>