Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / Observable.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-util-Observable'>/**
19 </span> * @class Ext.util.Observable
20  * Base class that provides a common interface for publishing events. Subclasses are expected to
21  * to have a property &quot;events&quot; with all the events defined, and, optionally, a property &quot;listeners&quot;
22  * with configured listeners defined.&lt;br&gt;
23  * For example:
24  * &lt;pre&gt;&lt;code&gt;
25 Ext.define('Employee', {
26     extend: 'Ext.util.Observable',
27     constructor: function(config){
28         this.name = config.name;
29         this.addEvents({
30             &quot;fired&quot; : true,
31             &quot;quit&quot; : true
32         });
33
34         // Copy configured listeners into *this* object so that the base class&amp;#39;s
35         // constructor will add them.
36         this.listeners = config.listeners;
37
38         // Call our superclass constructor to complete construction process.
39         Employee.superclass.constructor.call(this, config)
40     }
41 });
42 &lt;/code&gt;&lt;/pre&gt;
43  * This could then be used like this:&lt;pre&gt;&lt;code&gt;
44 var newEmployee = new Employee({
45     name: employeeName,
46     listeners: {
47         quit: function() {
48             // By default, &quot;this&quot; will be the object that fired the event.
49             alert(this.name + &quot; has quit!&quot;);
50         }
51     }
52 });
53 &lt;/code&gt;&lt;/pre&gt;
54  */
55
56 Ext.define('Ext.util.Observable', {
57
58     /* Begin Definitions */
59
60     requires: ['Ext.util.Event'],
61
62     statics: {
63 <span id='Ext-util-Observable-method-releaseCapture'>        /**
64 </span>         * Removes &lt;b&gt;all&lt;/b&gt; added captures from the Observable.
65          * @param {Observable} o The Observable to release
66          * @static
67          */
68         releaseCapture: function(o) {
69             o.fireEvent = this.prototype.fireEvent;
70         },
71
72 <span id='Ext-util-Observable-method-capture'>        /**
73 </span>         * Starts capture on the specified Observable. All events will be passed
74          * to the supplied function with the event name + standard signature of the event
75          * &lt;b&gt;before&lt;/b&gt; the event is fired. If the supplied function returns false,
76          * the event will not fire.
77          * @param {Observable} o The Observable to capture events from.
78          * @param {Function} fn The function to call when an event is fired.
79          * @param {Object} scope (optional) The scope (&lt;code&gt;this&lt;/code&gt; reference) in which the function is executed. Defaults to the Observable firing the event.
80          * @static
81          */
82         capture: function(o, fn, scope) {
83             o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
84         },
85
86 <span id='Ext-util-Observable-method-observe'>        /**
87 </span>Sets observability on the passed class constructor.
88
89 This makes any event fired on any instance of the passed class also fire a single event through
90 the __class__ allowing for central handling of events on many instances at once.
91
92 Usage:
93
94     Ext.util.Observable.observe(Ext.data.Connection);
95     Ext.data.Connection.on('beforerequest', function(con, options) {
96         console.log('Ajax request made to ' + options.url);
97     });
98
99          * @param {Function} c The class constructor to make observable.
100          * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
101          * @static
102          * @markdown
103          */
104         observe: function(cls, listeners) {
105             if (cls) {
106                 if (!cls.isObservable) {
107                     Ext.applyIf(cls, new this());
108                     this.capture(cls.prototype, cls.fireEvent, cls);
109                 }
110                 if (Ext.isObject(listeners)) {
111                     cls.on(listeners);
112                 }
113                 return cls;
114             }
115         }
116     },
117
118     /* End Definitions */
119
120 <span id='Ext-util-Observable-cfg-listeners'>    /**
121 </span>    * @cfg {Object} listeners (optional) &lt;p&gt;A config object containing one or more event handlers to be added to this
122     * object during initialization.  This should be a valid listeners config object as specified in the
123     * {@link #addListener} example for attaching multiple handlers at once.&lt;/p&gt;
124     * &lt;br&gt;&lt;p&gt;&lt;b&gt;&lt;u&gt;DOM events from ExtJs {@link Ext.Component Components}&lt;/u&gt;&lt;/b&gt;&lt;/p&gt;
125     * &lt;br&gt;&lt;p&gt;While &lt;i&gt;some&lt;/i&gt; ExtJs Component classes export selected DOM events (e.g. &quot;click&quot;, &quot;mouseover&quot; etc), this
126     * is usually only done when extra value can be added. For example the {@link Ext.view.View DataView}'s
127     * &lt;b&gt;&lt;code&gt;{@link Ext.view.View#click click}&lt;/code&gt;&lt;/b&gt; event passing the node clicked on. To access DOM
128     * events directly from a child element of a Component, we need to specify the &lt;code&gt;element&lt;/code&gt; option to
129     * identify the Component property to add a DOM listener to:
130     * &lt;pre&gt;&lt;code&gt;
131 new Ext.panel.Panel({
132     width: 400,
133     height: 200,
134     dockedItems: [{
135         xtype: 'toolbar'
136     }],
137     listeners: {
138         click: {
139             element: 'el', //bind to the underlying el property on the panel
140             fn: function(){ console.log('click el'); }
141         },
142         dblclick: {
143             element: 'body', //bind to the underlying body property on the panel
144             fn: function(){ console.log('dblclick body'); }
145         }
146     }
147 });
148 &lt;/code&gt;&lt;/pre&gt;
149     * &lt;/p&gt;
150     */
151     // @private
152     isObservable: true,
153
154     constructor: function(config) {
155         var me = this;
156
157         Ext.apply(me, config);
158         if (me.listeners) {
159             me.on(me.listeners);
160             delete me.listeners;
161         }
162         me.events = me.events || {};
163
164         if (me.bubbleEvents) {
165             me.enableBubble(me.bubbleEvents);
166         }
167     },
168
169     // @private
170     eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal)$/,
171
172 <span id='Ext-util-Observable-method-addManagedListener'>    /**
173 </span>     * &lt;p&gt;Adds listeners to any Observable object (or Element) which are automatically removed when this Component
174      * is destroyed.
175      * @param {Observable/Element} item The item to which to add a listener/listeners.
176      * @param {Object/String} ename The event name, or an object containing event name properties.
177      * @param {Function} fn Optional. If the &lt;code&gt;ename&lt;/code&gt; parameter was an event name, this
178      * is the handler function.
179      * @param {Object} scope Optional. If the &lt;code&gt;ename&lt;/code&gt; parameter was an event name, this
180      * is the scope (&lt;code&gt;this&lt;/code&gt; reference) in which the handler function is executed.
181      * @param {Object} opt Optional. If the &lt;code&gt;ename&lt;/code&gt; parameter was an event name, this
182      * is the {@link Ext.util.Observable#addListener addListener} options.
183      */
184     addManagedListener : function(item, ename, fn, scope, options) {
185         var me = this,
186             managedListeners = me.managedListeners = me.managedListeners || [],
187             config;
188
189         if (Ext.isObject(ename)) {
190             options = ename;
191             for (ename in options) {
192                 if (options.hasOwnProperty(ename)) {
193                     config = options[ename];
194                     if (!me.eventOptionsRe.test(ename)) {
195                         me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
196                     }
197                 }
198             }
199         }
200         else {
201             managedListeners.push({
202                 item: item,
203                 ename: ename,
204                 fn: fn,
205                 scope: scope,
206                 options: options
207             });
208
209             item.on(ename, fn, scope, options);
210         }
211     },
212
213 <span id='Ext-util-Observable-method-removeManagedListener'>    /**
214 </span>     * Removes listeners that were added by the {@link #mon} method.
215      * @param {Observable|Element} item The item from which to remove a listener/listeners.
216      * @param {Object|String} ename The event name, or an object containing event name properties.
217      * @param {Function} fn Optional. If the &lt;code&gt;ename&lt;/code&gt; parameter was an event name, this
218      * is the handler function.
219      * @param {Object} scope Optional. If the &lt;code&gt;ename&lt;/code&gt; parameter was an event name, this
220      * is the scope (&lt;code&gt;this&lt;/code&gt; reference) in which the handler function is executed.
221      */
222      removeManagedListener : function(item, ename, fn, scope) {
223         var me = this,
224             options,
225             config,
226             managedListeners,
227             length,
228             i;
229
230         if (Ext.isObject(ename)) {
231             options = ename;
232             for (ename in options) {
233                 if (options.hasOwnProperty(ename)) {
234                     config = options[ename];
235                     if (!me.eventOptionsRe.test(ename)) {
236                         me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
237                     }
238                 }
239             }
240         }
241
242         managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
243
244         for (i = 0, length = managedListeners.length; i &lt; length; i++) {
245             me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
246         }
247     },
248
249 <span id='Ext-util-Observable-method-fireEvent'>    /**
250 </span>     * &lt;p&gt;Fires the specified event with the passed parameters (minus the event name).&lt;/p&gt;
251      * &lt;p&gt;An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
252      * by calling {@link #enableBubble}.&lt;/p&gt;
253      * @param {String} eventName The name of the event to fire.
254      * @param {Object...} args Variable number of parameters are passed to handlers.
255      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
256      */
257     fireEvent: function() {
258         var me = this,
259             args = Ext.Array.toArray(arguments),
260             ename = args[0].toLowerCase(),
261             ret = true,
262             event = me.events[ename],
263             queue = me.eventQueue,
264             parent;
265
266         if (me.eventsSuspended === true) {
267             if (queue) {
268                 queue.push(args);
269             }
270         } else if (event &amp;&amp; Ext.isObject(event) &amp;&amp; event.bubble) {
271             if (event.fire.apply(event, args.slice(1)) === false) {
272                 return false;
273             }
274             parent = me.getBubbleTarget &amp;&amp; me.getBubbleTarget();
275             if (parent &amp;&amp; parent.isObservable) {
276                 if (!parent.events[ename] || !Ext.isObject(parent.events[ename]) || !parent.events[ename].bubble) {
277                     parent.enableBubble(ename);
278                 }
279                 return parent.fireEvent.apply(parent, args);
280             }
281         } else if (event &amp;&amp; Ext.isObject(event)) {
282             args.shift();
283             ret = event.fire.apply(event, args);
284         }
285         return ret;
286     },
287
288 <span id='Ext-util-Observable-method-addListener'>    /**
289 </span>     * Appends an event handler to this object.
290      * @param {String}   eventName The name of the event to listen for. May also be an object who's property names are event names. See
291      * @param {Function} handler The method the event invokes.
292      * @param {Object}   scope (optional) The scope (&lt;code&gt;&lt;b&gt;this&lt;/b&gt;&lt;/code&gt; reference) in which the handler function is executed.
293      * &lt;b&gt;If omitted, defaults to the object which fired the event.&lt;/b&gt;
294      * @param {Object}   options (optional) An object containing handler configuration.
295      * properties. This may contain any of the following properties:&lt;ul&gt;
296      * &lt;li&gt;&lt;b&gt;scope&lt;/b&gt; : Object&lt;div class=&quot;sub-desc&quot;&gt;The scope (&lt;code&gt;&lt;b&gt;this&lt;/b&gt;&lt;/code&gt; reference) in which the handler function is executed.
297      * &lt;b&gt;If omitted, defaults to the object which fired the event.&lt;/b&gt;&lt;/div&gt;&lt;/li&gt;
298      * &lt;li&gt;&lt;b&gt;delay&lt;/b&gt; : Number&lt;div class=&quot;sub-desc&quot;&gt;The number of milliseconds to delay the invocation of the handler after the event fires.&lt;/div&gt;&lt;/li&gt;
299      * &lt;li&gt;&lt;b&gt;single&lt;/b&gt; : Boolean&lt;div class=&quot;sub-desc&quot;&gt;True to add a handler to handle just the next firing of the event, and then remove itself.&lt;/div&gt;&lt;/li&gt;
300      * &lt;li&gt;&lt;b&gt;buffer&lt;/b&gt; : Number&lt;div class=&quot;sub-desc&quot;&gt;Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
301      * by the specified number of milliseconds. If the event fires again within that time, the original
302      * handler is &lt;em&gt;not&lt;/em&gt; invoked, but the new handler is scheduled in its place.&lt;/div&gt;&lt;/li&gt;
303      * &lt;li&gt;&lt;b&gt;target&lt;/b&gt; : Observable&lt;div class=&quot;sub-desc&quot;&gt;Only call the handler if the event was fired on the target Observable, &lt;i&gt;not&lt;/i&gt;
304      * if the event was bubbled up from a child Observable.&lt;/div&gt;&lt;/li&gt;
305      * &lt;li&gt;&lt;b&gt;element&lt;/b&gt; : String&lt;div class=&quot;sub-desc&quot;&gt;&lt;b&gt;This option is only valid for listeners bound to {@link Ext.Component Components}.&lt;/b&gt;
306      * The name of a Component property which references an element to add a listener to.
307      * &lt;p&gt;This option is useful during Component construction to add DOM event listeners to elements of {@link Ext.Component Components} which
308      * will exist only after the Component is rendered. For example, to add a click listener to a Panel's body:&lt;pre&gt;&lt;code&gt;
309 new Ext.panel.Panel({
310     title: 'The title',
311     listeners: {
312         click: this.handlePanelClick,
313         element: 'body'
314     }
315 });
316 &lt;/code&gt;&lt;/pre&gt;&lt;/p&gt;
317      * &lt;p&gt;When added in this way, the options available are the options applicable to {@link Ext.core.Element#addListener}&lt;/p&gt;&lt;/div&gt;&lt;/li&gt;
318      * &lt;/ul&gt;&lt;br&gt;
319      * &lt;p&gt;
320      * &lt;b&gt;Combining Options&lt;/b&gt;&lt;br&gt;
321      * Using the options argument, it is possible to combine different types of listeners:&lt;br&gt;
322      * &lt;br&gt;
323      * A delayed, one-time listener.
324      * &lt;pre&gt;&lt;code&gt;
325 myPanel.on('hide', this.handleClick, this, {
326 single: true,
327 delay: 100
328 });&lt;/code&gt;&lt;/pre&gt;
329      * &lt;p&gt;
330      * &lt;b&gt;Attaching multiple handlers in 1 call&lt;/b&gt;&lt;br&gt;
331      * The method also allows for a single argument to be passed which is a config object containing properties
332      * which specify multiple events. For example:&lt;pre&gt;&lt;code&gt;
333 myGridPanel.on({
334     cellClick: this.onCellClick,
335     mouseover: this.onMouseOver,
336     mouseout: this.onMouseOut,
337     scope: this // Important. Ensure &quot;this&quot; is correct during handler execution
338 });
339 &lt;/code&gt;&lt;/pre&gt;.
340      * &lt;p&gt;
341      */
342     addListener: function(ename, fn, scope, options) {
343         var me = this,
344             config,
345             event;
346
347         if (Ext.isObject(ename)) {
348             options = ename;
349             for (ename in options) {
350                 if (options.hasOwnProperty(ename)) {
351                     config = options[ename];
352                     if (!me.eventOptionsRe.test(ename)) {
353                         me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
354                     }
355                 }
356             }
357         }
358         else {
359             ename = ename.toLowerCase();
360             me.events[ename] = me.events[ename] || true;
361             event = me.events[ename] || true;
362             if (Ext.isBoolean(event)) {
363                 me.events[ename] = event = new Ext.util.Event(me, ename);
364             }
365             event.addListener(fn, scope, Ext.isObject(options) ? options : {});
366         }
367     },
368
369 <span id='Ext-util-Observable-method-removeListener'>    /**
370 </span>     * Removes an event handler.
371      * @param {String}   eventName The type of event the handler was associated with.
372      * @param {Function} handler   The handler to remove. &lt;b&gt;This must be a reference to the function passed into the {@link #addListener} call.&lt;/b&gt;
373      * @param {Object}   scope     (optional) The scope originally specified for the handler.
374      */
375     removeListener: function(ename, fn, scope) {
376         var me = this,
377             config,
378             event,
379             options;
380
381         if (Ext.isObject(ename)) {
382             options = ename;
383             for (ename in options) {
384                 if (options.hasOwnProperty(ename)) {
385                     config = options[ename];
386                     if (!me.eventOptionsRe.test(ename)) {
387                         me.removeListener(ename, config.fn || config, config.scope || options.scope);
388                     }
389                 }
390             }
391         } else {
392             ename = ename.toLowerCase();
393             event = me.events[ename];
394             if (event &amp;&amp; event.isEvent) {
395                 event.removeListener(fn, scope);
396             }
397         }
398     },
399
400 <span id='Ext-util-Observable-method-clearListeners'>    /**
401 </span>     * Removes all listeners for this object including the managed listeners
402      */
403     clearListeners: function() {
404         var events = this.events,
405             event,
406             key;
407
408         for (key in events) {
409             if (events.hasOwnProperty(key)) {
410                 event = events[key];
411                 if (event.isEvent) {
412                     event.clearListeners();
413                 }
414             }
415         }
416
417         this.clearManagedListeners();
418     },
419
420     //&lt;debug&gt;
421     purgeListeners : function() {
422         if (Ext.global.console) {
423             Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
424         }
425         return this.clearListeners.apply(this, arguments);
426     },
427     //&lt;/debug&gt;
428
429 <span id='Ext-util-Observable-method-clearManagedListeners'>    /**
430 </span>     * Removes all managed listeners for this object.
431      */
432     clearManagedListeners : function() {
433         var managedListeners = this.managedListeners || [],
434             i = 0,
435             len = managedListeners.length;
436
437         for (; i &lt; len; i++) {
438             this.removeManagedListenerItem(true, managedListeners[i]);
439         }
440
441         this.managedListeners = [];
442     },
443     
444 <span id='Ext-util-Observable-method-removeManagedListenerItem'>    /**
445 </span>     * Remove a single managed listener item
446      * @private
447      * @param {Boolean} isClear True if this is being called during a clear
448      * @param {Object} managedListener The managed listener item
449      * See removeManagedListener for other args
450      */
451     removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
452         if (isClear || (managedListener.item === item &amp;&amp; managedListener.ename === ename &amp;&amp; (!fn || managedListener.fn === fn) &amp;&amp; (!scope || managedListener.scope === scope))) {
453             managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
454             if (!isClear) {
455                 Ext.Array.remove(this.managedListeners, managedListener);
456             }    
457         }
458     },
459
460     //&lt;debug&gt;
461     purgeManagedListeners : function() {
462         if (Ext.global.console) {
463             Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
464         }
465         return this.clearManagedListeners.apply(this, arguments);
466     },
467     //&lt;/debug&gt;
468
469 <span id='Ext-util-Observable-method-addEvents'>    /**
470 </span>     * Adds the specified events to the list of events which this Observable may fire.
471      * @param {Object/String} o Either an object with event names as properties with a value of &lt;code&gt;true&lt;/code&gt;
472      * or the first event name string if multiple event names are being passed as separate parameters.
473      * @param {String} [additional] Optional additional event names if multiple event names are being passed as separate parameters.
474      * Usage:&lt;pre&gt;&lt;code&gt;
475 this.addEvents('storeloaded', 'storecleared');
476 &lt;/code&gt;&lt;/pre&gt;
477      */
478     addEvents: function(o) {
479         var me = this,
480             args,
481             len,
482             i;
483             
484             me.events = me.events || {};
485         if (Ext.isString(o)) {
486             args = arguments;
487             i = args.length;
488             
489             while (i--) {
490                 me.events[args[i]] = me.events[args[i]] || true;
491             }
492         } else {
493             Ext.applyIf(me.events, o);
494         }
495     },
496
497 <span id='Ext-util-Observable-method-hasListener'>    /**
498 </span>     * Checks to see if this object has any listeners for a specified event
499      * @param {String} eventName The name of the event to check for
500      * @return {Boolean} True if the event is being listened for, else false
501      */
502     hasListener: function(ename) {
503         var event = this.events[ename.toLowerCase()];
504         return event &amp;&amp; event.isEvent === true &amp;&amp; event.listeners.length &gt; 0;
505     },
506
507 <span id='Ext-util-Observable-method-suspendEvents'>    /**
508 </span>     * Suspend the firing of all events. (see {@link #resumeEvents})
509      * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
510      * after the {@link #resumeEvents} call instead of discarding all suspended events;
511      */
512     suspendEvents: function(queueSuspended) {
513         this.eventsSuspended = true;
514         if (queueSuspended &amp;&amp; !this.eventQueue) {
515             this.eventQueue = [];
516         }
517     },
518
519 <span id='Ext-util-Observable-method-resumeEvents'>    /**
520 </span>     * Resume firing events. (see {@link #suspendEvents})
521      * If events were suspended using the &lt;code&gt;&lt;b&gt;queueSuspended&lt;/b&gt;&lt;/code&gt; parameter, then all
522      * events fired during event suspension will be sent to any listeners now.
523      */
524     resumeEvents: function() {
525         var me = this,
526             queued = me.eventQueue || [];
527
528         me.eventsSuspended = false;
529         delete me.eventQueue;
530
531         Ext.each(queued,
532         function(e) {
533             me.fireEvent.apply(me, e);
534         });
535     },
536
537 <span id='Ext-util-Observable-method-relayEvents'>    /**
538 </span>     * Relays selected events from the specified Observable as if the events were fired by &lt;code&gt;&lt;b&gt;this&lt;/b&gt;&lt;/code&gt;.
539      * @param {Object} origin The Observable whose events this object is to relay.
540      * @param {Array} events Array of event names to relay.
541      */
542     relayEvents : function(origin, events, prefix) {
543         prefix = prefix || '';
544         var me = this,
545             len = events.length,
546             i = 0,
547             oldName,
548             newName;
549
550         for (; i &lt; len; i++) {
551             oldName = events[i].substr(prefix.length);
552             newName = prefix + oldName;
553             me.events[newName] = me.events[newName] || true;
554             origin.on(oldName, me.createRelayer(newName));
555         }
556     },
557
558 <span id='Ext-util-Observable-method-createRelayer'>    /**
559 </span>     * @private
560      * Creates an event handling function which refires the event from this object as the passed event name.
561      * @param newName
562      * @returns {Function}
563      */
564     createRelayer: function(newName){
565         var me = this;
566         return function(){
567             return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
568         };
569     },
570
571 <span id='Ext-util-Observable-method-enableBubble'>    /**
572 </span>     * &lt;p&gt;Enables events fired by this Observable to bubble up an owner hierarchy by calling
573      * &lt;code&gt;this.getBubbleTarget()&lt;/code&gt; if present. There is no implementation in the Observable base class.&lt;/p&gt;
574      * &lt;p&gt;This is commonly used by Ext.Components to bubble events to owner Containers. See {@link Ext.Component#getBubbleTarget}. The default
575      * implementation in Ext.Component returns the Component's immediate owner. But if a known target is required, this can be overridden to
576      * access the required target more quickly.&lt;/p&gt;
577      * &lt;p&gt;Example:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;
578 Ext.override(Ext.form.field.Base, {
579 //  Add functionality to Field&amp;#39;s initComponent to enable the change event to bubble
580 initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
581     this.enableBubble('change');
582 }),
583
584 //  We know that we want Field&amp;#39;s events to bubble directly to the FormPanel.
585 getBubbleTarget : function() {
586     if (!this.formPanel) {
587         this.formPanel = this.findParentByType('form');
588     }
589     return this.formPanel;
590 }
591 });
592
593 var myForm = new Ext.formPanel({
594 title: 'User Details',
595 items: [{
596     ...
597 }],
598 listeners: {
599     change: function() {
600         // Title goes red if form has been modified.
601         myForm.header.setStyle('color', 'red');
602     }
603 }
604 });
605 &lt;/code&gt;&lt;/pre&gt;
606      * @param {String/Array} events The event name to bubble, or an Array of event names.
607      */
608     enableBubble: function(events) {
609         var me = this;
610         if (!Ext.isEmpty(events)) {
611             events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
612             Ext.each(events,
613             function(ename) {
614                 ename = ename.toLowerCase();
615                 var ce = me.events[ename] || true;
616                 if (Ext.isBoolean(ce)) {
617                     ce = new Ext.util.Event(me, ename);
618                     me.events[ename] = ce;
619                 }
620                 ce.bubble = true;
621             });
622         }
623     }
624 }, function() {
625 <span id='Ext-util-Observable-method-un'>    /**
626 </span>     * Removes an event handler (shorthand for {@link #removeListener}.)
627      * @param {String}   eventName     The type of event the handler was associated with.
628      * @param {Function} handler       The handler to remove. &lt;b&gt;This must be a reference to the function passed into the {@link #addListener} call.&lt;/b&gt;
629      * @param {Object}   scope         (optional) The scope originally specified for the handler.
630      * @method un
631      */
632
633 <span id='Ext-util-Observable-method-on'>    /**
634 </span>     * Appends an event handler to this object (shorthand for {@link #addListener}.)
635      * @param {String}   eventName     The type of event to listen for
636      * @param {Function} handler       The method the event invokes
637      * @param {Object}   scope         (optional) The scope (&lt;code&gt;&lt;b&gt;this&lt;/b&gt;&lt;/code&gt; reference) in which the handler function is executed.
638      * &lt;b&gt;If omitted, defaults to the object which fired the event.&lt;/b&gt;
639      * @param {Object}   options       (optional) An object containing handler configuration.
640      * @method on
641      */
642
643     this.createAlias({
644         on: 'addListener',
645         un: 'removeListener',
646         mon: 'addManagedListener',
647         mun: 'removeManagedListener'
648     });
649
650     //deprecated, will be removed in 5.0
651     this.observeClass = this.observe;
652
653     Ext.apply(Ext.util.Observable.prototype, function(){
654         // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
655         // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
656         // private
657         function getMethodEvent(method){
658             var e = (this.methodEvents = this.methodEvents || {})[method],
659                 returnValue,
660                 v,
661                 cancel,
662                 obj = this;
663
664             if (!e) {
665                 this.methodEvents[method] = e = {};
666                 e.originalFn = this[method];
667                 e.methodName = method;
668                 e.before = [];
669                 e.after = [];
670
671                 var makeCall = function(fn, scope, args){
672                     if((v = fn.apply(scope || obj, args)) !== undefined){
673                         if (typeof v == 'object') {
674                             if(v.returnValue !== undefined){
675                                 returnValue = v.returnValue;
676                             }else{
677                                 returnValue = v;
678                             }
679                             cancel = !!v.cancel;
680                         }
681                         else
682                             if (v === false) {
683                                 cancel = true;
684                             }
685                             else {
686                                 returnValue = v;
687                             }
688                     }
689                 };
690
691                 this[method] = function(){
692                     var args = Array.prototype.slice.call(arguments, 0),
693                         b, i, len;
694                     returnValue = v = undefined;
695                     cancel = false;
696
697                     for(i = 0, len = e.before.length; i &lt; len; i++){
698                         b = e.before[i];
699                         makeCall(b.fn, b.scope, args);
700                         if (cancel) {
701                             return returnValue;
702                         }
703                     }
704
705                     if((v = e.originalFn.apply(obj, args)) !== undefined){
706                         returnValue = v;
707                     }
708
709                     for(i = 0, len = e.after.length; i &lt; len; i++){
710                         b = e.after[i];
711                         makeCall(b.fn, b.scope, args);
712                         if (cancel) {
713                             return returnValue;
714                         }
715                     }
716                     return returnValue;
717                 };
718             }
719             return e;
720         }
721
722         return {
723             // these are considered experimental
724             // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
725             // adds an 'interceptor' called before the original method
726             beforeMethod : function(method, fn, scope){
727                 getMethodEvent.call(this, method).before.push({
728                     fn: fn,
729                     scope: scope
730                 });
731             },
732
733             // adds a 'sequence' called after the original method
734             afterMethod : function(method, fn, scope){
735                 getMethodEvent.call(this, method).after.push({
736                     fn: fn,
737                     scope: scope
738                 });
739             },
740
741             removeMethodListener: function(method, fn, scope){
742                 var e = this.getMethodEvent(method),
743                     i, len;
744                 for(i = 0, len = e.before.length; i &lt; len; i++){
745                     if(e.before[i].fn == fn &amp;&amp; e.before[i].scope == scope){
746                         e.before.splice(i, 1);
747                         return;
748                     }
749                 }
750                 for(i = 0, len = e.after.length; i &lt; len; i++){
751                     if(e.after[i].fn == fn &amp;&amp; e.after[i].scope == scope){
752                         e.after.splice(i, 1);
753                         return;
754                     }
755                 }
756             },
757
758             toggleEventLogging: function(toggle) {
759                 Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
760                     if (Ext.isDefined(Ext.global.console)) {
761                         Ext.global.console.log(en, arguments);
762                     }
763                 });
764             }
765         };
766     }());
767 });
768 </pre>
769 </body>
770 </html>