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; }
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 * Employee.superclass.constructor.call(this, config)
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-method-releaseCapture'> /**
62 </span> * Removes **all** added captures from the Observable.
64 * @param {Observable} o The Observable to release
67 releaseCapture: function(o) {
68 o.fireEvent = this.prototype.fireEvent;
71 <span id='Ext-util-Observable-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 {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-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 ExtJS {@link Ext.Component Components}**
128 * While _some_ ExtJs 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)$/,
173 <span id='Ext-util-Observable-method-addManagedListener'> /**
174 </span> * Adds listeners to any Observable object (or Element) which are automatically removed when this Component is
177 * @param {Observable/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 {Observable|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() {
263 args = Ext.Array.toArray(arguments),
264 ename = args[0].toLowerCase(),
266 event = me.events[ename],
267 queue = me.eventQueue,
270 if (me.eventsSuspended === true) {
274 } else if (event && event !== true) {
276 if (event.fire.apply(event, args.slice(1)) === false) {
279 parent = me.getBubbleTarget && me.getBubbleTarget();
280 if (parent && parent.isObservable) {
281 if (!parent.events[ename] || parent.events[ename] === true || !parent.events[ename].bubble) {
282 parent.enableBubble(ename);
284 return parent.fireEvent.apply(parent, args);
289 ret = event.fire.apply(event, args);
295 <span id='Ext-util-Observable-method-addListener'> /**
296 </span> * Appends an event handler to this object.
298 * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
300 * @param {Function} handler The method the event invokes. Will be called with arguments given to
301 * {@link #fireEvent} plus the `options` parameter described below.
302 * @param {Object} scope (optional) The scope (`this` reference) in which the handler function is executed. **If
303 * omitted, defaults to the object which fired the event.**
304 * @param {Object} options (optional) An object containing handler configuration.
306 * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
308 * This object may contain any of the following properties:
310 * - **scope** : Object
312 * The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
313 * which fired the event.**
315 * - **delay** : Number
317 * The number of milliseconds to delay the invocation of the handler after the event fires.
319 * - **single** : Boolean
321 * True to add a handler to handle just the next firing of the event, and then remove itself.
323 * - **buffer** : Number
325 * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
326 * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
327 * handler is scheduled in its place.
329 * - **target** : Observable
331 * Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
334 * - **element** : String
336 * **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
337 * property which references an element to add a listener to.
339 * This option is useful during Component construction to add DOM event listeners to elements of
340 * {@link Ext.Component Components} which will exist only after the Component is rendered.
341 * For example, to add a click listener to a Panel's body:
343 * new Ext.panel.Panel({
344 * title: 'The title',
346 * click: this.handlePanelClick,
351 * **Combining Options**
353 * Using the options argument, it is possible to combine different types of listeners:
355 * A delayed, one-time listener.
357 * myPanel.on('hide', this.handleClick, this, {
362 * **Attaching multiple handlers in 1 call**
364 * The method also allows for a single argument to be passed which is a config object containing properties which
365 * specify multiple events. For example:
368 * cellClick: this.onCellClick,
369 * mouseover: this.onMouseOver,
370 * mouseout: this.onMouseOut,
371 * scope: this // Important. Ensure "this" is correct during handler execution
374 * One can also specify options for each event handler separately:
377 * cellClick: {fn: this.onCellClick, scope: this, single: true},
378 * mouseover: {fn: panel.onMouseOver, scope: panel}
382 addListener: function(ename, fn, scope, options) {
387 if (typeof ename !== 'string') {
389 for (ename in options) {
390 if (options.hasOwnProperty(ename)) {
391 config = options[ename];
392 if (!me.eventOptionsRe.test(ename)) {
393 me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
399 ename = ename.toLowerCase();
400 me.events[ename] = me.events[ename] || true;
401 event = me.events[ename] || true;
402 if (Ext.isBoolean(event)) {
403 me.events[ename] = event = new Ext.util.Event(me, ename);
405 event.addListener(fn, scope, Ext.isObject(options) ? options : {});
409 <span id='Ext-util-Observable-method-removeListener'> /**
410 </span> * Removes an event handler.
412 * @param {String} eventName The type of event the handler was associated with.
413 * @param {Function} handler The handler to remove. **This must be a reference to the function passed into the
414 * {@link #addListener} call.**
415 * @param {Object} scope (optional) The scope originally specified for the handler.
417 removeListener: function(ename, fn, scope) {
423 if (typeof ename !== 'string') {
425 for (ename in options) {
426 if (options.hasOwnProperty(ename)) {
427 config = options[ename];
428 if (!me.eventOptionsRe.test(ename)) {
429 me.removeListener(ename, config.fn || config, config.scope || options.scope);
434 ename = ename.toLowerCase();
435 event = me.events[ename];
436 if (event && event.isEvent) {
437 event.removeListener(fn, scope);
442 <span id='Ext-util-Observable-method-clearListeners'> /**
443 </span> * Removes all listeners for this object including the managed listeners
445 clearListeners: function() {
446 var events = this.events,
450 for (key in events) {
451 if (events.hasOwnProperty(key)) {
454 event.clearListeners();
459 this.clearManagedListeners();
463 purgeListeners : function() {
464 if (Ext.global.console) {
465 Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
467 return this.clearListeners.apply(this, arguments);
471 <span id='Ext-util-Observable-method-clearManagedListeners'> /**
472 </span> * Removes all managed listeners for this object.
474 clearManagedListeners : function() {
475 var managedListeners = this.managedListeners || [],
477 len = managedListeners.length;
479 for (; i < len; i++) {
480 this.removeManagedListenerItem(true, managedListeners[i]);
483 this.managedListeners = [];
486 <span id='Ext-util-Observable-method-removeManagedListenerItem'> /**
487 </span> * Remove a single managed listener item
489 * @param {Boolean} isClear True if this is being called during a clear
490 * @param {Object} managedListener The managed listener item
491 * See removeManagedListener for other args
493 removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
494 if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
495 managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
497 Ext.Array.remove(this.managedListeners, managedListener);
503 purgeManagedListeners : function() {
504 if (Ext.global.console) {
505 Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
507 return this.clearManagedListeners.apply(this, arguments);
511 <span id='Ext-util-Observable-method-addEvents'> /**
512 </span> * Adds the specified events to the list of events which this Observable may fire.
514 * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
515 * event name string if multiple event names are being passed as separate parameters. Usage:
522 * @param {String...} more Optional additional event names if multiple event names are being passed as separate
525 * this.addEvents('storeloaded', 'storecleared');
528 addEvents: function(o) {
534 me.events = me.events || {};
535 if (Ext.isString(o)) {
540 me.events[args[i]] = me.events[args[i]] || true;
543 Ext.applyIf(me.events, o);
547 <span id='Ext-util-Observable-method-hasListener'> /**
548 </span> * Checks to see if this object has any listeners for a specified event
550 * @param {String} eventName The name of the event to check for
551 * @return {Boolean} True if the event is being listened for, else false
553 hasListener: function(ename) {
554 var event = this.events[ename.toLowerCase()];
555 return event && event.isEvent === true && event.listeners.length > 0;
558 <span id='Ext-util-Observable-method-suspendEvents'> /**
559 </span> * Suspends the firing of all events. (see {@link #resumeEvents})
561 * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
562 * after the {@link #resumeEvents} call instead of discarding all suspended events.
564 suspendEvents: function(queueSuspended) {
565 this.eventsSuspended = true;
566 if (queueSuspended && !this.eventQueue) {
567 this.eventQueue = [];
571 <span id='Ext-util-Observable-method-resumeEvents'> /**
572 </span> * Resumes firing events (see {@link #suspendEvents}).
574 * If events were suspended using the `**queueSuspended**` parameter, then all events fired
575 * during event suspension will be sent to any listeners now.
577 resumeEvents: function() {
579 queued = me.eventQueue || [];
581 me.eventsSuspended = false;
582 delete me.eventQueue;
586 me.fireEvent.apply(me, e);
590 <span id='Ext-util-Observable-method-relayEvents'> /**
591 </span> * Relays selected events from the specified Observable as if the events were fired by `this`.
593 * @param {Object} origin The Observable whose events this object is to relay.
594 * @param {[String]} events Array of event names to relay.
595 * @param {Object} prefix
597 relayEvents : function(origin, events, prefix) {
598 prefix = prefix || '';
605 for (; i < len; i++) {
606 oldName = events[i].substr(prefix.length);
607 newName = prefix + oldName;
608 me.events[newName] = me.events[newName] || true;
609 origin.on(oldName, me.createRelayer(newName));
613 <span id='Ext-util-Observable-method-createRelayer'> /**
615 * Creates an event handling function which refires the event from this object as the passed event name.
617 * @returns {Function}
619 createRelayer: function(newName){
622 return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
626 <span id='Ext-util-Observable-method-enableBubble'> /**
627 </span> * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
628 * present. There is no implementation in the Observable base class.
630 * This is commonly used by Ext.Components to bubble events to owner Containers.
631 * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
632 * Component's immediate owner. But if a known target is required, this can be overridden to access the
633 * required target more quickly.
637 * Ext.override(Ext.form.field.Base, {
638 * // Add functionality to Field's initComponent to enable the change event to bubble
639 * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
640 * this.enableBubble('change');
643 * // We know that we want Field's events to bubble directly to the FormPanel.
644 * getBubbleTarget : function() {
645 * if (!this.formPanel) {
646 * this.formPanel = this.findParentByType('form');
648 * return this.formPanel;
652 * var myForm = new Ext.formPanel({
653 * title: 'User Details',
658 * change: function() {
659 * // Title goes red if form has been modified.
660 * myForm.header.setStyle('color', 'red');
665 * @param {String/[String]} events The event name to bubble, or an Array of event names.
667 enableBubble: function(events) {
669 if (!Ext.isEmpty(events)) {
670 events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
673 ename = ename.toLowerCase();
674 var ce = me.events[ename] || true;
675 if (Ext.isBoolean(ce)) {
676 ce = new Ext.util.Event(me, ename);
677 me.events[ename] = ce;
686 <span id='Ext-util-Observable-method-on'> /**
688 * Shorthand for {@link #addListener}.
689 * @alias Ext.util.Observable#addListener
692 <span id='Ext-util-Observable-method-un'> /**
694 * Shorthand for {@link #removeListener}.
695 * @alias Ext.util.Observable#removeListener
697 un: 'removeListener',
698 <span id='Ext-util-Observable-method-mon'> /**
700 * Shorthand for {@link #addManagedListener}.
701 * @alias Ext.util.Observable#addManagedListener
703 mon: 'addManagedListener',
704 <span id='Ext-util-Observable-method-mun'> /**
706 * Shorthand for {@link #removeManagedListener}.
707 * @alias Ext.util.Observable#removeManagedListener
709 mun: 'removeManagedListener'
712 //deprecated, will be removed in 5.0
713 this.observeClass = this.observe;
715 Ext.apply(Ext.util.Observable.prototype, function(){
716 // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
717 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
719 function getMethodEvent(method){
720 var e = (this.methodEvents = this.methodEvents || {})[method],
727 this.methodEvents[method] = e = {};
728 e.originalFn = this[method];
729 e.methodName = method;
733 var makeCall = function(fn, scope, args){
734 if((v = fn.apply(scope || obj, args)) !== undefined){
735 if (typeof v == 'object') {
736 if(v.returnValue !== undefined){
737 returnValue = v.returnValue;
753 this[method] = function(){
754 var args = Array.prototype.slice.call(arguments, 0),
756 returnValue = v = undefined;
759 for(i = 0, len = e.before.length; i < len; i++){
761 makeCall(b.fn, b.scope, args);
767 if((v = e.originalFn.apply(obj, args)) !== undefined){
771 for(i = 0, len = e.after.length; i < len; i++){
773 makeCall(b.fn, b.scope, args);
785 // these are considered experimental
786 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
787 // adds an 'interceptor' called before the original method
788 beforeMethod : function(method, fn, scope){
789 getMethodEvent.call(this, method).before.push({
795 // adds a 'sequence' called after the original method
796 afterMethod : function(method, fn, scope){
797 getMethodEvent.call(this, method).after.push({
803 removeMethodListener: function(method, fn, scope){
804 var e = this.getMethodEvent(method),
806 for(i = 0, len = e.before.length; i < len; i++){
807 if(e.before[i].fn == fn && e.before[i].scope == scope){
808 Ext.Array.erase(e.before, i, 1);
812 for(i = 0, len = e.after.length; i < len; i++){
813 if(e.after[i].fn == fn && e.after[i].scope == scope){
814 Ext.Array.erase(e.after, i, 1);
820 toggleEventLogging: function(toggle) {
821 Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
822 if (Ext.isDefined(Ext.global.console)) {
823 Ext.global.console.log(en, arguments);