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