4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5 <title>The source code</title>
6 <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
7 <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
8 <style type="text/css">
9 .highlight { display: block; background-color: #ddd; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
17 <body onload="prettyPrint(); highlight();">
18 <pre class="prettyprint lang-js"><span id='Ext-util-Observable'>/**
19 </span> * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property
20 * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined.
24 * Ext.define('Employee', {
25 * extend: 'Ext.util.Observable',
26 * constructor: function(config){
27 * this.name = config.name;
29 * "fired" : true,
30 * "quit" : true
33 * // Copy configured listeners into *this* object so that the base class's
34 * // constructor will add them.
35 * this.listeners = config.listeners;
37 * // Call our superclass constructor to complete construction process.
38 * this.callParent(arguments)
42 * This could then be used like this:
44 * var newEmployee = new Employee({
48 * // By default, "this" will be the object that fired the event.
49 * alert(this.name + " has quit!");
54 Ext.define('Ext.util.Observable', {
56 /* Begin Definitions */
58 requires: ['Ext.util.Event'],
61 <span id='Ext-util-Observable-static-method-releaseCapture'> /**
62 </span> * Removes **all** added captures from the Observable.
64 * @param {Ext.util.Observable} o The Observable to release
67 releaseCapture: function(o) {
68 o.fireEvent = this.prototype.fireEvent;
71 <span id='Ext-util-Observable-static-method-capture'> /**
72 </span> * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
73 * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
74 * the event will not fire.
76 * @param {Ext.util.Observable} o The Observable to capture events from.
77 * @param {Function} fn The function to call when an event is fired.
78 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
79 * the Observable firing the event.
82 capture: function(o, fn, scope) {
83 o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
86 <span id='Ext-util-Observable-static-method-observe'> /**
87 </span> * Sets observability on the passed class constructor.
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.
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);
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}.
103 observe: function(cls, listeners) {
105 if (!cls.isObservable) {
106 Ext.applyIf(cls, new this());
107 this.capture(cls.prototype, cls.fireEvent, cls);
109 if (Ext.isObject(listeners)) {
117 /* End Definitions */
119 <span id='Ext-util-Observable-cfg-listeners'> /**
120 </span> * @cfg {Object} listeners
122 * A config object containing one or more event handlers to be added to this object during initialization. This
123 * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple
126 * **DOM events from Ext JS {@link Ext.Component Components}**
128 * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually
129 * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link
130 * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a
131 * child element of a Component, we need to specify the `element` option to identify the Component property to add a
134 * new Ext.panel.Panel({
142 * element: 'el', //bind to the underlying el property on the panel
143 * fn: function(){ console.log('click el'); }
146 * element: 'body', //bind to the underlying body property on the panel
147 * fn: function(){ console.log('dblclick body'); }
155 constructor: function(config) {
158 Ext.apply(me, config);
163 me.events = me.events || {};
165 if (me.bubbleEvents) {
166 me.enableBubble(me.bubbleEvents);
171 eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/,
173 <span id='Ext-util-Observable-method-addManagedListener'> /**
174 </span> * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is
177 * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners.
178 * @param {Object/String} ename The event name, or an object containing event name properties.
179 * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
180 * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
181 * in which the handler function is executed.
182 * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the
183 * {@link Ext.util.Observable#addListener addListener} options.
185 addManagedListener : function(item, ename, fn, scope, options) {
187 managedListeners = me.managedListeners = me.managedListeners || [],
190 if (typeof ename !== 'string') {
192 for (ename in options) {
193 if (options.hasOwnProperty(ename)) {
194 config = options[ename];
195 if (!me.eventOptionsRe.test(ename)) {
196 me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
202 managedListeners.push({
210 item.on(ename, fn, scope, options);
214 <span id='Ext-util-Observable-method-removeManagedListener'> /**
215 </span> * Removes listeners that were added by the {@link #mon} method.
217 * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners.
218 * @param {Object/String} ename The event name, or an object containing event name properties.
219 * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function.
220 * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference)
221 * in which the handler function is executed.
223 removeManagedListener : function(item, ename, fn, scope) {
231 if (typeof ename !== 'string') {
233 for (ename in options) {
234 if (options.hasOwnProperty(ename)) {
235 config = options[ename];
236 if (!me.eventOptionsRe.test(ename)) {
237 me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope);
243 managedListeners = me.managedListeners ? me.managedListeners.slice() : [];
245 for (i = 0, length = managedListeners.length; i < length; i++) {
246 me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope);
250 <span id='Ext-util-Observable-method-fireEvent'> /**
251 </span> * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
252 * to {@link #addListener}).
254 * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by
255 * calling {@link #enableBubble}.
257 * @param {String} eventName The name of the event to fire.
258 * @param {Object...} args Variable number of parameters are passed to handlers.
259 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
261 fireEvent: function(eventName) {
262 var name = eventName.toLowerCase(),
263 events = this.events,
264 event = events && events[name],
265 bubbles = event && event.bubble;
267 return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles);
270 <span id='Ext-util-Observable-method-continueFireEvent'> /**
271 </span> * Continue to fire event.
274 * @param {String} eventName
275 * @param {Array} args
276 * @param {Boolean} bubbles
278 continueFireEvent: function(eventName, args, bubbles) {
284 if (target.eventsSuspended === true) {
285 if ((queue = target.eventQueue)) {
286 queue.push([eventName, args, bubbles]);
290 event = target.events[eventName];
291 // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it
292 // configure to bubble.
293 if (event && event != true) {
294 if ((ret = event.fire.apply(event, args)) === false) {
299 } while (bubbles && (target = target.getBubbleParent()));
303 <span id='Ext-util-Observable-method-getBubbleParent'> /**
304 </span> * Gets the bubbling parent for an Observable
306 * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
308 getBubbleParent: function(){
309 var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
310 if (parent && parent.isObservable) {
316 <span id='Ext-util-Observable-method-addListener'> /**
317 </span> * Appends an event handler to this object.
319 * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
321 * @param {Function} fn The method the event invokes. Will be called with arguments given to
322 * {@link #fireEvent} plus the `options` parameter described below.
323 * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
324 * omitted, defaults to the object which fired the event.**
325 * @param {Object} [options] An object containing handler configuration.
327 * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
329 * This object may contain any of the following properties:
331 * - **scope** : Object
333 * The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
334 * which fired the event.**
336 * - **delay** : Number
338 * The number of milliseconds to delay the invocation of the handler after the event fires.
340 * - **single** : Boolean
342 * True to add a handler to handle just the next firing of the event, and then remove itself.
344 * - **buffer** : Number
346 * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
347 * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
348 * handler is scheduled in its place.
350 * - **target** : Observable
352 * Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
355 * - **element** : String
357 * **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
358 * property which references an element to add a listener to.
360 * This option is useful during Component construction to add DOM event listeners to elements of
361 * {@link Ext.Component Components} which will exist only after the Component is rendered.
362 * For example, to add a click listener to a Panel's body:
364 * new Ext.panel.Panel({
365 * title: 'The title',
367 * click: this.handlePanelClick,
372 * **Combining Options**
374 * Using the options argument, it is possible to combine different types of listeners:
376 * A delayed, one-time listener.
378 * myPanel.on('hide', this.handleClick, this, {
383 * **Attaching multiple handlers in 1 call**
385 * The method also allows for a single argument to be passed which is a config object containing properties which
386 * specify multiple events. For example:
389 * cellClick: this.onCellClick,
390 * mouseover: this.onMouseOver,
391 * mouseout: this.onMouseOut,
392 * scope: this // Important. Ensure "this" is correct during handler execution
395 * One can also specify options for each event handler separately:
398 * cellClick: {fn: this.onCellClick, scope: this, single: true},
399 * mouseover: {fn: panel.onMouseOver, scope: panel}
403 addListener: function(ename, fn, scope, options) {
408 if (typeof ename !== 'string') {
410 for (ename in options) {
411 if (options.hasOwnProperty(ename)) {
412 config = options[ename];
413 if (!me.eventOptionsRe.test(ename)) {
414 me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
420 ename = ename.toLowerCase();
421 me.events[ename] = me.events[ename] || true;
422 event = me.events[ename] || true;
423 if (Ext.isBoolean(event)) {
424 me.events[ename] = event = new Ext.util.Event(me, ename);
426 event.addListener(fn, scope, Ext.isObject(options) ? options : {});
430 <span id='Ext-util-Observable-method-removeListener'> /**
431 </span> * Removes an event handler.
433 * @param {String} eventName The type of event the handler was associated with.
434 * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
435 * {@link #addListener} call.**
436 * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
437 * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
439 removeListener: function(ename, fn, scope) {
445 if (typeof ename !== 'string') {
447 for (ename in options) {
448 if (options.hasOwnProperty(ename)) {
449 config = options[ename];
450 if (!me.eventOptionsRe.test(ename)) {
451 me.removeListener(ename, config.fn || config, config.scope || options.scope);
456 ename = ename.toLowerCase();
457 event = me.events[ename];
458 if (event && event.isEvent) {
459 event.removeListener(fn, scope);
464 <span id='Ext-util-Observable-method-clearListeners'> /**
465 </span> * Removes all listeners for this object including the managed listeners
467 clearListeners: function() {
468 var events = this.events,
472 for (key in events) {
473 if (events.hasOwnProperty(key)) {
476 event.clearListeners();
481 this.clearManagedListeners();
485 purgeListeners : function() {
486 if (Ext.global.console) {
487 Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
489 return this.clearListeners.apply(this, arguments);
493 <span id='Ext-util-Observable-method-clearManagedListeners'> /**
494 </span> * Removes all managed listeners for this object.
496 clearManagedListeners : function() {
497 var managedListeners = this.managedListeners || [],
499 len = managedListeners.length;
501 for (; i < len; i++) {
502 this.removeManagedListenerItem(true, managedListeners[i]);
505 this.managedListeners = [];
508 <span id='Ext-util-Observable-method-removeManagedListenerItem'> /**
509 </span> * Remove a single managed listener item
511 * @param {Boolean} isClear True if this is being called during a clear
512 * @param {Object} managedListener The managed listener item
513 * See removeManagedListener for other args
515 removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
516 if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
517 managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
519 Ext.Array.remove(this.managedListeners, managedListener);
525 purgeManagedListeners : function() {
526 if (Ext.global.console) {
527 Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
529 return this.clearManagedListeners.apply(this, arguments);
533 <span id='Ext-util-Observable-method-addEvents'> /**
534 </span> * Adds the specified events to the list of events which this Observable may fire.
536 * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
537 * event name string if multiple event names are being passed as separate parameters. Usage:
544 * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate
547 * this.addEvents('storeloaded', 'storecleared');
550 addEvents: function(o) {
556 me.events = me.events || {};
557 if (Ext.isString(o)) {
562 me.events[args[i]] = me.events[args[i]] || true;
565 Ext.applyIf(me.events, o);
569 <span id='Ext-util-Observable-method-hasListener'> /**
570 </span> * Checks to see if this object has any listeners for a specified event
572 * @param {String} eventName The name of the event to check for
573 * @return {Boolean} True if the event is being listened for, else false
575 hasListener: function(ename) {
576 var event = this.events[ename.toLowerCase()];
577 return event && event.isEvent === true && event.listeners.length > 0;
580 <span id='Ext-util-Observable-method-suspendEvents'> /**
581 </span> * Suspends the firing of all events. (see {@link #resumeEvents})
583 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
584 * after the {@link #resumeEvents} call instead of discarding all suspended events.
586 suspendEvents: function(queueSuspended) {
587 this.eventsSuspended = true;
588 if (queueSuspended && !this.eventQueue) {
589 this.eventQueue = [];
593 <span id='Ext-util-Observable-method-resumeEvents'> /**
594 </span> * Resumes firing events (see {@link #suspendEvents}).
596 * If events were suspended using the `queueSuspended` parameter, then all events fired
597 * during event suspension will be sent to any listeners now.
599 resumeEvents: function() {
601 queued = me.eventQueue;
603 me.eventsSuspended = false;
604 delete me.eventQueue;
607 Ext.each(queued, function(e) {
608 me.continueFireEvent.apply(me, e);
613 <span id='Ext-util-Observable-method-relayEvents'> /**
614 </span> * Relays selected events from the specified Observable as if the events were fired by `this`.
616 * @param {Object} origin The Observable whose events this object is to relay.
617 * @param {String[]} events Array of event names to relay.
618 * @param {String} prefix
620 relayEvents : function(origin, events, prefix) {
621 prefix = prefix || '';
628 for (; i < len; i++) {
629 oldName = events[i].substr(prefix.length);
630 newName = prefix + oldName;
631 me.events[newName] = me.events[newName] || true;
632 origin.on(oldName, me.createRelayer(newName));
636 <span id='Ext-util-Observable-method-createRelayer'> /**
638 * Creates an event handling function which refires the event from this object as the passed event name.
640 * @returns {Function}
642 createRelayer: function(newName){
645 return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
649 <span id='Ext-util-Observable-method-enableBubble'> /**
650 </span> * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
651 * present. There is no implementation in the Observable base class.
653 * This is commonly used by Ext.Components to bubble events to owner Containers.
654 * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
655 * Component's immediate owner. But if a known target is required, this can be overridden to access the
656 * required target more quickly.
660 * Ext.override(Ext.form.field.Base, {
661 * // Add functionality to Field's initComponent to enable the change event to bubble
662 * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
663 * this.enableBubble('change');
666 * // We know that we want Field's events to bubble directly to the FormPanel.
667 * getBubbleTarget : function() {
668 * if (!this.formPanel) {
669 * this.formPanel = this.findParentByType('form');
671 * return this.formPanel;
675 * var myForm = new Ext.formPanel({
676 * title: 'User Details',
681 * change: function() {
682 * // Title goes red if form has been modified.
683 * myForm.header.setStyle('color', 'red');
688 * @param {String/String[]} events The event name to bubble, or an Array of event names.
690 enableBubble: function(events) {
692 if (!Ext.isEmpty(events)) {
693 events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
696 ename = ename.toLowerCase();
697 var ce = me.events[ename] || true;
698 if (Ext.isBoolean(ce)) {
699 ce = new Ext.util.Event(me, ename);
700 me.events[ename] = ce;
709 <span id='Ext-util-Observable-method-on'> /**
711 * Shorthand for {@link #addListener}.
712 * @alias Ext.util.Observable#addListener
715 <span id='Ext-util-Observable-method-un'> /**
717 * Shorthand for {@link #removeListener}.
718 * @alias Ext.util.Observable#removeListener
720 un: 'removeListener',
721 <span id='Ext-util-Observable-method-mon'> /**
723 * Shorthand for {@link #addManagedListener}.
724 * @alias Ext.util.Observable#addManagedListener
726 mon: 'addManagedListener',
727 <span id='Ext-util-Observable-method-mun'> /**
729 * Shorthand for {@link #removeManagedListener}.
730 * @alias Ext.util.Observable#removeManagedListener
732 mun: 'removeManagedListener'
735 //deprecated, will be removed in 5.0
736 this.observeClass = this.observe;
738 Ext.apply(Ext.util.Observable.prototype, function(){
739 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
740 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
742 function getMethodEvent(method){
743 var e = (this.methodEvents = this.methodEvents || {})[method],
750 this.methodEvents[method] = e = {};
751 e.originalFn = this[method];
752 e.methodName = method;
756 var makeCall = function(fn, scope, args){
757 if((v = fn.apply(scope || obj, args)) !== undefined){
758 if (typeof v == 'object') {
759 if(v.returnValue !== undefined){
760 returnValue = v.returnValue;
776 this[method] = function(){
777 var args = Array.prototype.slice.call(arguments, 0),
779 returnValue = v = undefined;
782 for(i = 0, len = e.before.length; i < len; i++){
784 makeCall(b.fn, b.scope, args);
790 if((v = e.originalFn.apply(obj, args)) !== undefined){
794 for(i = 0, len = e.after.length; i < len; i++){
796 makeCall(b.fn, b.scope, args);
808 // these are considered experimental
809 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
810 // adds an 'interceptor' called before the original method
811 beforeMethod : function(method, fn, scope){
812 getMethodEvent.call(this, method).before.push({
818 // adds a 'sequence' called after the original method
819 afterMethod : function(method, fn, scope){
820 getMethodEvent.call(this, method).after.push({
826 removeMethodListener: function(method, fn, scope){
827 var e = this.getMethodEvent(method),
829 for(i = 0, len = e.before.length; i < len; i++){
830 if(e.before[i].fn == fn && e.before[i].scope == scope){
831 Ext.Array.erase(e.before, i, 1);
835 for(i = 0, len = e.after.length; i < len; i++){
836 if(e.after[i].fn == fn && e.after[i].scope == scope){
837 Ext.Array.erase(e.after, i, 1);
843 toggleEventLogging: function(toggle) {
844 Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
845 if (Ext.isDefined(Ext.global.console)) {
846 Ext.global.console.log(en, arguments);