X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/0494b8d9b9bb03ab6c22b34dae81261e3cd7e3e6..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/Observable.html diff --git a/docs/source/Observable.html b/docs/source/Observable.html index 8cf2a532..f96cba34 100644 --- a/docs/source/Observable.html +++ b/docs/source/Observable.html @@ -1,39 +1,19 @@ - - - - The source code - - - - -
/*!
- * Ext JS Library 3.3.1
- * Copyright(c) 2006-2010 Sencha Inc.
- * licensing@sencha.com
- * http://www.sencha.com/license
- */
-(function(){
-
-var EXTUTIL = Ext.util,
-    EACH = Ext.each,
-    TRUE = true,
-    FALSE = false;
-
/** - * @class Ext.util.Observable +Sencha Documentation Project
/**
+ * @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.
+ * to have a property "events" with all the events defined, and, optionally, a property "listeners" + * with configured listeners defined.<br> * For example: - *

+ * <pre><code>
 Employee = Ext.extend(Ext.util.Observable, {
     constructor: function(config){
         this.name = config.name;
         this.addEvents({
-            "fired" : true,
-            "quit" : true
+            "fired" : true,
+            "quit" : true
         });
 
-        // Copy configured listeners into *this* object so that the base class's
+        // Copy configured listeners into *this* object so that the base class&#39;s
         // constructor will add them.
         this.listeners = config.listeners;
 
@@ -41,499 +21,718 @@ Employee = Ext.extend(Ext.util.Observable, {
         Employee.superclass.constructor.call(this, config)
     }
 });
-
- * This could then be used like this:

+</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!");
+            // By default, "this" will be the object that fired the event.
+            alert(this.name + " has quit!");
         }
     }
 });
-
+</code></pre> */ -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.

- *

DOM events from ExtJs {@link Ext.Component Components}

- *

While some 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 - * {@link Ext.DataView#click click} event passing the node clicked on. To access DOM - * events directly from a Component's HTMLElement, listeners must be added to the {@link Ext.Component#getEl Element} after the Component - * has been rendered. A plugin can simplify this step:


-// 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;
-    },
 
-    // Component passes itself into plugin'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);
+Ext.define('Ext.util.Observable', {
+
+    /* Begin Definitions */
+
+    requires: ['Ext.util.Event'],
+
+    statics: {
+        /**
+         * Removes <b>all</b> added captures from the Observable.
+         * @param {Observable} o The Observable to release
+         * @static
+         */
+        releaseCapture: function(o) {
+            o.fireEvent = this.prototype.fireEvent;
+        },
+
+        /**
+         * Starts capture on the specified Observable. All events will be passed
+         * to the supplied function with the event name + standard signature of the event
+         * <b>before</b> the event is fired. If the supplied function returns false,
+         * the event will not fire.
+         * @param {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 (<code>this</code> 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);
+        },
+
+        /**
+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
+         * @markdown
+         */
+        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;
             }
         }
-
-        // 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);
-            }
-        });
     },
 
-    createHandler: function(fn, c) {
-        return function(e) {
-            fn.call(this, e, c);
-        };
+    /* End Definitions */
+
+    /**
+    * @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.view.View DataView}'s
+    * <b><code>{@link Ext.view.View#click click}</code></b> event passing the node clicked on. To access DOM
+    * events directly from a child element of a Component, we need to specify the <code>element</code> option to
+    * identify the Component property to add a DOM listener to:
+    * <pre><code>
+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'); }
+        }
     }
 });
+</code></pre>
+    * </p>
+    */
+    // @private
+    isObservable: true,
 
-var combo = new Ext.form.ComboBox({
+    constructor: function(config) {
+        var me = this;
 
-    // Collapse combo when its element is clicked on
-    plugins: [ new Ext.DomObserver({
-        click: function(evt, comp) {
-            comp.collapse();
+        Ext.apply(me, config);
+        if (me.listeners) {
+            me.on(me.listeners);
+            delete me.listeners;
         }
-    })],
-    store: myStore,
-    typeAhead: true,
-    mode: 'local',
-    triggerAction: 'all'
-});
-     * 

+ me.events = me.events || {}; + + if (me.bubbleEvents) { + me.enableBubble(me.bubbleEvents); + } + }, + + // @private + eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/, + + /** + * <p>Adds listeners to any Observable object (or Element) which are automatically removed when this Component + * is destroyed. + * @param {Observable/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 <code>ename</code> parameter was an event name, this + * is the handler function. + * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this + * is the scope (<code>this</code> reference) in which the handler function is executed. + * @param {Object} opt Optional. If the <code>ename</code> parameter was an event name, this + * is the {@link Ext.util.Observable#addListener addListener} options. */ - var me = this, e = me.events; - if(me.listeners){ - me.on(me.listeners); - delete me.listeners; - } - me.events = e || {}; -}; + addManagedListener : function(item, ename, fn, scope, options) { + var me = this, + managedListeners = me.managedListeners = me.managedListeners || [], + config; + + if (Ext.isObject(ename)) { + 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); + } + }, + + /** + * Removes listeners that were added by the {@link #mon} method. + * @param {Observable|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 <code>ename</code> parameter was an event name, this + * is the handler function. + * @param {Object} scope Optional. If the <code>ename</code> parameter was an event name, this + * is the scope (<code>this</code> reference) in which the handler function is executed. + */ + removeManagedListener : function(item, ename, fn, scope) { + var me = this, + options, + config, + managedListeners, + managedListener, + length, + i; + + if (Ext.isObject(ename)) { + 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); + } + } + } + } + + managedListeners = me.managedListeners ? me.managedListeners.slice() : []; + length = managedListeners.length; -EXTUTIL.Observable.prototype = { - // private - filterOptRe : /^(?:scope|delay|buffer|single)$/, + for (i = 0; i < length; i++) { + managedListener = managedListeners[i]; + if (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope)) { + Ext.Array.remove(me.managedListeners, managedListener); + item.un(managedListener.ename, managedListener.fn, managedListener.scope); + } + } + }, -
/** - *

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}.

+ /** + * <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> * @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); + fireEvent: function() { + var me = this, + args = Ext.Array.toArray(arguments), + ename = args[0].toLowerCase(), + ret = true, + event = me.events[ename], + queue = me.eventQueue, + parent; + + if (me.eventsSuspended === true) { + if (queue) { + queue.push(args); } - } - else if(typeof ce == 'object') { - if (ce.bubble){ - if(ce.fire.apply(ce, a.slice(1)) === FALSE) { - return FALSE; - } - c = me.getBubbleTarget && me.getBubbleTarget(); - if(c && c.enableBubble) { - cc = c.events[ename]; - if(!cc || typeof cc != 'object' || !cc.bubble) { - c.enableBubble(ename); - } - return c.fireEvent.apply(c, a); - } + } else if (event && Ext.isObject(event) && event.bubble) { + if (event.fire.apply(event, args.slice(1)) === false) { + return false; } - else { - a.shift(); - ret = ce.fire.apply(ce, a); + parent = me.getBubbleTarget && me.getBubbleTarget(); + if (parent && parent.isObservable) { + if (!parent.events[ename] || !Ext.isObject(parent.events[ename]) || !parent.events[ename].bubble) { + parent.enableBubble(ename); + } + return parent.fireEvent.apply(parent, args); } + } else if (event && Ext.isObject(event)) { + args.shift(); + ret = event.fire.apply(event, args); } return ret; }, -
/** - * Appends an event handler to this object. - * @param {String} eventName The name of the event to listen for. + /** + * 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. See * @param {Function} handler The method the event invokes. - * @param {Object} scope (optional) The scope (this reference) in which the handler function is executed. - * If omitted, defaults to the object which fired the event. + * @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:
    - *
  • 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 + * 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 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.
  • - *

- *

- * Combining Options
- * Using the options argument, it is possible to combine different types of listeners:
- *
+ * 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> + * <li><b>element</b> : String<div class="sub-desc"><b>This option is only valid for listeners bound to {@link Ext.Component Components}.</b> + * The name of a Component property which references an element to add a listener to. + * <p>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:<pre><code> +new Ext.panel.Panel({ + title: 'The title', + listeners: { + click: this.handlePanelClick, + element: 'body' + } +}); +</code></pre></p> + * <p>When added in this way, the options available are the options applicable to {@link Ext.core.Element#addListener}</p></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> * A delayed, one-time listener. - *


-myDataView.on('click', this.onClick, this, {
+     * <pre><code>
+myPanel.on('hide', this.handleClick, this, {
 single: true,
 delay: 100
-});
- *

- * Attaching multiple handlers in 1 call
+});</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. - *

- *


-myGridPanel.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:
- *


+     * which specify multiple events. For example:<pre><code>
 myGridPanel.on({
-'click' : this.onClick,
-'mouseover' : this.onMouseOver,
-'mouseout' : this.onMouseOut,
- scope: this
-});
+ cellClick: this.onCellClick, + mouseover: this.onMouseOver, + mouseout: this.onMouseOut, + scope: this // Important. Ensure "this" is correct during handler execution +}); +</code></pre>. + * <p> */ - 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 (Ext.isObject(ename)) { + 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 : {}); } }, -
/** - * Removes an event handler. + /** + * Removes an event handler. * @param {String} eventName The type of event the handler was associated with. - * @param {Function} handler The handler to remove. This must be a reference to the function passed into the {@link #addListener} call. + * @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. */ - 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 (Ext.isObject(ename)) { + 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.isEvent) { + event.removeListener(fn, scope); + } } }, -
/** - * Removes all listeners for this object + /** + * 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(); + }, + + //<debug> + purgeListeners : function() { + console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.'); + return this.clearListeners.apply(this, arguments); + }, + //</debug> + + /** + * Removes all managed listeners for this object. + */ + clearManagedListeners : function() { + var managedListeners = this.managedListeners || [], + i = 0, + len = managedListeners.length, + managedListener; + + for (; i < len; i++) { + managedListener = managedListeners[i]; + managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope); + } + + this.managedListeners = []; }, -
/** - * 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 + //<debug> + purgeManagedListeners : function() { + console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.'); + return this.clearManagedListeners.apply(this, arguments); + }, + //</debug> + + /** + * 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:

+     * @param {String} [additional] Optional additional event names if multiple event names are being passed as separate parameters.
+     * Usage:<pre><code>
 this.addEvents('storeloaded', 'storecleared');
-
+</code></pre> */ - 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; + 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); } }, -
/** - * Checks to see if this object has any listeners for a specified event + /** + * 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 && event.isEvent === true && event.listeners.length > 0; }, -
/** - * Suspend the firing of all events. (see {@link #resumeEvents}) + /** + * Suspend 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; */ - suspendEvents : function(queueSuspended){ - this.eventsSuspended = TRUE; - if(queueSuspended && !this.eventQueue){ + suspendEvents: function(queueSuspended) { + this.eventsSuspended = true; + if (queueSuspended && !this.eventQueue) { this.eventQueue = []; } }, -
/** - * Resume firing events. (see {@link #suspendEvents}) - * If events were suspended using the queueSuspended parameter, then all + /** + * Resume firing events. (see {@link #suspendEvents}) + * If events were suspended using the <code><b>queueSuspended</b></code> 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; + + me.eventsSuspended = false; delete me.eventQueue; - EACH(queued, function(e) { + + Ext.each(queued, + function(e) { me.fireEvent.apply(me, e); }); - } -}; - -var OBSERVABLE = EXTUTIL.Observable.prototype; -
/** - * 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 (this reference) in which the handler function is executed. - * If omitted, defaults to the object which fired the event. - * @param {Object} options (optional) An object containing handler configuration. - * @method - */ -OBSERVABLE.on = OBSERVABLE.addListener; -
/** - * 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. 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. - * @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, Array.prototype.slice.call(arguments, 0)); - } - }; -}; - -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(), - args = Array.prototype.slice.call(arguments, 0); - if(!l.tasks) { - l.tasks = []; - } - l.tasks.push(task); - task.delay(o.delay || 10, function(){ - l.tasks.remove(task); - h.apply(scope, args); - }, scope); - }; -}; - -EXTUTIL.Event = function(obj, name){ - this.name = name; - this.obj = obj; - this.listeners = []; -}; - -EXTUTIL.Event.prototype = { - addListener : function(fn, scope, options){ + /** + * Relays selected events from the specified Observable as if the events were fired by <code><b>this</b></code>. + * @param {Object} origin The Observable whose events this object is to relay. + * @param {Array} events Array of event names to relay. + */ + 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 < 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; + /** + * @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; + /** + * <p>Enables events fired by this Observable to bubble up an owner hierarchy by calling + * <code>this.getBubbleTarget()</code> if present. There is no implementation in the Observable base class.</p> + * <p>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.</p> + * <p>Example:</p><pre><code> +Ext.override(Ext.form.field.Base, { +// Add functionality to Field&#39;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&#39;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'); + } +} +}); +</code></pre> + * @param {String/Array} 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() { + /** + * 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 un + */ - isListening : function(fn, scope){ - return this.findListener(fn, scope) != -1; - }, + /** + * 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 on + */ - 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; - } - me.listeners.splice(index, 1); - ret = TRUE; - } - return ret; - }, + this.createAlias({ + on: 'addListener', + un: 'removeListener', + mon: 'addManagedListener', + 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 < len; i++){ + b = e.before[i]; + makeCall(b.fn, b.scope, args); + if (cancel) { + return returnValue; + } + } - // 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); - } - }, + if((v = e.originalFn.apply(obj, args)) !== undefined){ + returnValue = v; + } - 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); - } + for(i = 0, len = e.after.length; i < len; i++){ + b = e.after[i]; + makeCall(b.fn, b.scope, args); + if (cancel) { + return returnValue; + } + } + return returnValue; + }; } + return e; } - me.firing = FALSE; - return TRUE; - } -}; -})(); -
- - \ No newline at end of file + 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 < len; i++){ + if(e.before[i].fn == fn && e.before[i].scope == scope){ + e.before.splice(i, 1); + return; + } + } + for(i = 0, len = e.after.length; i < len; i++){ + if(e.after[i].fn == fn && e.after[i].scope == scope){ + e.after.splice(i, 1); + return; + } + } + }, + + toggleEventLogging: function(toggle) { + Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) { + if (Ext.isDefined(Ext.global.console)) { + Ext.global.console.log(en, arguments); + } + }); + } + }; + }()); +}); +
\ No newline at end of file