X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/c930e9176a5a85509c5b0230e2bff5c22a591432..7a654f8d43fdb43d78b63d90528bed6e86b608cc:/docs/source/Observable.html diff --git a/docs/source/Observable.html b/docs/source/Observable.html index b8119a9f..f96cba34 100644 --- a/docs/source/Observable.html +++ b/docs/source/Observable.html @@ -1,486 +1,738 @@ - - - The source code - - - - -
(function(){
-
-var EXTUTIL = Ext.util,
-    TOARRAY = Ext.toArray,
-    EACH = Ext.each,
-    ISOBJECT = Ext.isObject,
-    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;
 
         // Call our superclass constructor to complete construction process.
-        Employee.superclass.constructor.call(config)
+        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.
 
-        // 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);
+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;
             }
-        });
+        }
     },
 
-    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'
-});
-     * 

- */ - var me = this, e = me.events; - if(me.listeners){ - me.on(me.listeners); - delete me.listeners; - } - me.events = e || {}; -}; - -EXTUTIL.Observable.prototype = function(){ - var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){ - return s.toLowerCase(); - }; - - return { -
/** - *

Fires the specified event with the passed parameters (minus the event name).

- *

An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) - * by calling {@link #enableBubble}.

- * @param {String} eventName The name of the event to fire. - * @param {Object...} args Variable number of parameters are passed to handlers. - * @return {Boolean} returns false if any of the handlers return false otherwise it returns true. - */ + me.events = me.events || {}; - fireEvent : function(){ - var a = TOARRAY(arguments), - ename = toLower(a[0]), - me = this, - ret = TRUE, - ce = me.events[ename], - q, - c; - if (me.eventsSuspended === TRUE) { - if (q = me.suspendedEventsQueue) { - q.push(a); - } - } - else if(ISOBJECT(ce) && ce.bubble){ - if(ce.fire.apply(ce, a.slice(1)) === FALSE) { - return FALSE; - } - c = me.getBubbleTarget && me.getBubbleTarget(); - if(c && c.enableBubble) { - c.enableBubble(ename); - return c.fireEvent.apply(c, a); + 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. + */ + 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 { - if (ISOBJECT(ce)) { - a.shift(); - ret = ce.fire.apply(ce, a); + } + 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); + } } } - return ret; - }, + } -
/** - * 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 (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. - * 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 - * 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:
- *
- * A delayed, one-time listener. - *


-myDataView.on('click', this.onClick, 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 handlers. - *

- *


-myGridPanel.on({
-    'click' : {
-        fn: this.onClick,
-        scope: this,
-        delay: 100
+        managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
+        length = managedListeners.length;
+
+        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);
+            }
+        }
     },
-    'mouseover' : {
-        fn: this.onMouseOver,
-        scope: this
+
+    /**
+     * <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 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 (event && Ext.isObject(event) && event.bubble) {
+            if (event.fire.apply(event, args.slice(1)) === false) {
+                return false;
+            }
+            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;
     },
-    'mouseout' : {
-        fn: this.onMouseOut,
-        scope: this
+
+    /**
+     * 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 (<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>
+     * <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'
     }
-});
- *

- * Or a shorthand syntax:
- *


+});
+</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.
+     * <pre><code>
+myPanel.on('hide', this.handleClick, 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 events. For example:<pre><code>
 myGridPanel.on({
-    'click' : this.onClick,
-    'mouseover' : this.onMouseOver,
-    'mouseout' : this.onMouseOut,
-     scope: this
-});
- */ - addListener : function(eventName, fn, scope, o){ - var me = this, - e, - oe, - isF, - ce; - if (ISOBJECT(eventName)) { - o = eventName; - for (e in o){ - oe = o[e]; - if (!filterOptRe.test(e)) { - me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o); + cellClick: this.onCellClick, + mouseover: this.onMouseOver, + mouseout: this.onMouseOut, + scope: this // Important. Ensure "this" is correct during handler execution +}); +</code></pre>. + * <p> + */ + addListener: function(ename, fn, scope, options) { + var me = this, + 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 = toLower(eventName); - ce = me.events[eventName] || TRUE; - if (typeof ce == "boolean") { - me.events[eventName] = ce = new EXTUTIL.Event(me, eventName); - } - ce.addListener(fn, scope, ISOBJECT(o) ? o : {}); } - }, - -
/** - * Removes 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 {Object} scope (optional) The scope originally specified for the handler. - */ - removeListener : function(eventName, fn, scope){ - var ce = this.events[toLower(eventName)]; - if (ISOBJECT(ce)) { - ce.removeListener(fn, scope); + } + 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); } - }, + event.addListener(fn, scope, Ext.isObject(options) ? options : {}); + } + }, -
/** - * Removes all listeners for this object - */ - purgeListeners : function(){ - var events = this.events, - evt, - key; - for(key in events){ - evt = events[key]; - if(ISOBJECT(evt)){ - evt.clearListeners(); + /** + * 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. + */ + 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); + } } } - }, - -
/** - * Used to define events on this Observable - * @param {Object} object The object with the events defined - */ - addEvents : function(o){ - var me = this; - me.events = me.events || {}; - if (typeof o == 'string') { - EACH(arguments, function(a) { - me.events[a] = me.events[a] || TRUE; - }); - } else { - Ext.applyIf(me.events, o); + } else { + ename = ename.toLowerCase(); + event = me.events[ename]; + if (event.isEvent) { + event.removeListener(fn, scope); } - }, - -
/** - * Checks to see if this object has any listeners for a specified event - * @param {String} eventName The name of the event to check for - * @return {Boolean} True if the event is being listened for, else false - */ - hasListener : function(eventName){ - var e = this.events[eventName]; - return ISOBJECT(e) && e.listeners.length > 0; - }, + } + }, -
/** - * Suspend the firing of all events. (see {@link #resumeEvents}) - * @param {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.suspendedEventsQueue = []; + /** + * Removes all listeners for this object including the managed listeners + */ + clearListeners: function() { + var events = this.events, + event, + key; + + for (key in events) { + if (events.hasOwnProperty(key)) { + event = events[key]; + if (event.isEvent) { + event.clearListeners(); + } } - }, - -
/** - * Resume firing events. (see {@link #suspendEvents}) - * If events were suspended using the queueSuspended parameter, then all - * events fired during event suspension will be sent to any listeners now. - */ - resumeEvents : function(){ - var me = this; - me.eventsSuspended = !delete me.suspendedEventQueue; - EACH(me.suspendedEventsQueue, function(e) { - me.fireEvent.apply(me, e); - }); } - } -}(); - -var OBSERVABLE = EXTUTIL.Observable.prototype; -
/** - * Appends an event handler to this 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, TOARRAY(arguments)); + 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); } - }; -}; - -function createBuffered(h, o, scope){ - var task = new EXTUTIL.DelayedTask(); - return function(){ - task.delay(o.buffer, h, scope, TOARRAY(arguments)); - }; -} -function createSingle(h, e, fn, scope){ - return function(){ - e.removeListener(fn, scope); - return h.apply(scope, arguments); - }; -} + this.managedListeners = []; + }, -function createDelayed(h, o, scope){ - return function(){ - var args = TOARRAY(arguments); - (function(){ - h.apply(scope, args); - }).defer(o.delay || 10); - }; -}; - -EXTUTIL.Event = function(obj, name){ - this.name = name; - this.obj = obj; - this.listeners = []; -}; - -EXTUTIL.Event.prototype = { - addListener : function(fn, scope, options){ + //<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} [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, - 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); + 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; } - me.listeners.push(l); + } else { + Ext.applyIf(me.events, o); } }, - createListener: function(fn, scope, o){ - o = o || {}, scope = scope || this.obj; - var l = { - fn: fn, - scope: scope, - options: o - }, h = fn; - if(o.target){ - h = createTargeted(h, o, scope); - } - if(o.delay){ - h = createDelayed(h, o, scope); - } - if(o.single){ - h = createSingle(h, this, fn, scope); - } - if(o.buffer){ - h = createBuffered(h, o, scope); - } - l.fireFn = h; - return l; + /** + * 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(ename) { + var event = this.events[ename.toLowerCase()]; + return event && event.isEvent === true && event.listeners.length > 0; }, - findListener : function(fn, scope){ - var s, ret = -1; - EACH(this.listeners, function(l, i) { - s = l.scope; - if(l.fn == fn && (s == scope || s == this.obj)){ - ret = i; - return FALSE; - } - }, - this); - return ret; + /** + * 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) { + this.eventQueue = []; + } }, - isListening : function(fn, scope){ - return this.findListener(fn, scope) != -1; + /** + * 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() { + var me = this, + queued = me.eventQueue || []; + + me.eventsSuspended = false; + delete me.eventQueue; + + Ext.each(queued, + function(e) { + me.fireEvent.apply(me, e); + }); }, - removeListener : function(fn, scope){ - var index, - me = this, - ret = FALSE; - if((index = me.findListener(fn, scope)) != -1){ - if (me.firing) { - me.listeners = me.listeners.slice(0); - } - me.listeners.splice(index, 1); - ret = TRUE; + /** + * 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, + 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)); } - return ret; }, - clearListeners : function(){ - this.listeners = []; + /** + * @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))); + }; }, - fire : function(){ - var me = this, - args = TOARRAY(arguments), - ret = TRUE; + /** + * <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; +} +}); - EACH(me.listeners, function(l) { - me.firing = TRUE; - if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) { - return ret = me.firing = FALSE; - } - }); - me.firing = FALSE; - return ret; +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'); } -}; -})();
- - \ No newline at end of file +} +}); +</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; + }); + } + } +}, 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 + */ + + /** + * 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 + */ + + 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; + } + } + + if((v = e.originalFn.apply(obj, args)) !== undefined){ + returnValue = v; + } + + 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; + } + + 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