X-Git-Url: http://git.ithinksw.org/extjs.git/blobdiff_plain/f5240829880f87e0cf581c6a296e436fdef0ef80..HEAD:/docs/source/Observable.html diff --git a/docs/source/Observable.html b/docs/source/Observable.html index a35005c6..7c3bcb45 100644 --- a/docs/source/Observable.html +++ b/docs/source/Observable.html @@ -1,539 +1,855 @@ +
- +/*! - * Ext JS Library 3.3.0 - * Copyright(c) 2006-2010 Ext JS, Inc. - * licensing@extjs.com - * http://www.extjs.com/license - */ -(function(){ - -var EXTUTIL = Ext.util, - EACH = Ext.each, - TRUE = true, - FALSE = false; -/** - * @class Ext.util.Observable - * Base class that provides a common interface for publishing events. Subclasses are expected to - * to have a property "events" with all the events defined, and, optionally, a property "listeners" - * with configured listeners defined.
+ +/** + * Base class that provides a common interface for publishing events. Subclasses are expected to to have a property + * "events" with all the events defined, and, optionally, a property "listeners" with configured listeners defined. + * * For example: - *- * This could then be used like this:-Employee = Ext.extend(Ext.util.Observable, { - constructor: function(config){ - this.name = config.name; - this.addEvents({ - "fired" : true, - "quit" : true - }); - - // Copy configured listeners into *this* object so that the base class's - // constructor will add them. - this.listeners = config.listeners; - - // Call our superclass constructor to complete construction process. - Employee.superclass.constructor.call(this, config) - } -}); -
+ * + * Ext.define('Employee', { + * extend: 'Ext.util.Observable', + * constructor: function(config){ + * this.name = config.name; + * this.addEvents({ + * "fired" : true, + * "quit" : true + * }); + * + * // Copy configured listeners into *this* object so that the base class's + * // constructor will add them. + * this.listeners = config.listeners; + * + * // Call our superclass constructor to complete construction process. + * this.callParent(arguments) + * } + * }); + * + * This could then be used like this: + * + * var newEmployee = new Employee({ + * name: employeeName, + * listeners: { + * quit: function() { + * // By default, "this" will be the object that fired the event. + * alert(this.name + " has quit!"); + * } + * } + * }); */ -EXTUTIL.Observable = function(){ - /** - * @cfg {Object} listeners (optional)-var newEmployee = new Employee({ - name: employeeName, - listeners: { - quit: function() { - // By default, "this" will be the object that fired the event. - alert(this.name + " has quit!"); - } - } -}); -
A config object containing one or more event handlers to be added to this - * object during initialization. This should be a valid listeners config object as specified in the - * {@link #addListener} example for attaching multiple handlers at once.
- *DOM events from ExtJs {@link Ext.Component Components}
- *While some ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this - * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s - *
{@link Ext.DataView#click click}
event passing the node clicked on. To access DOM - * events directly from a Component's HTMLElement, listeners must be added to the {@link Ext.Component#getEl Element} after the Component - * has been rendered. A plugin can simplify this step:- */ - var me = this, e = me.events; - if(me.listeners){ - me.on(me.listeners); - delete me.listeners; - } - me.events = e || {}; -}; - -EXTUTIL.Observable.prototype = { - // private - filterOptRe : /^(?:scope|delay|buffer|single)$/, + }, - /** - *-// Plugin is configured with a listeners config object. -// The Component is appended to the argument list of all handler functions. -Ext.DomObserver = Ext.extend(Object, { - constructor: function(config) { - this.listeners = config.listeners ? config.listeners : config; +Ext.define('Ext.util.Observable', { + + /* Begin Definitions */ + + requires: ['Ext.util.Event'], + + statics: { + /** + * Removes **all** added captures from the Observable. + * + * @param {Ext.util.Observable} o The Observable to release + * @static + */ + releaseCapture: function(o) { + o.fireEvent = this.prototype.fireEvent; + }, + + /** + * Starts capture on the specified Observable. All events will be passed to the supplied function with the event + * name + standard signature of the event **before** the event is fired. If the supplied function returns false, + * the event will not fire. + * + * @param {Ext.util.Observable} o The Observable to capture events from. + * @param {Function} fn The function to call when an event is fired. + * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to + * the Observable firing the event. + * @static + */ + capture: function(o, fn, scope) { + o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope); + }, + + /** + * Sets observability on the passed class constructor. + * + * This makes any event fired on any instance of the passed class also fire a single event through + * the **class** allowing for central handling of events on many instances at once. + * + * Usage: + * + * Ext.util.Observable.observe(Ext.data.Connection); + * Ext.data.Connection.on('beforerequest', function(con, options) { + * console.log('Ajax request made to ' + options.url); + * }); + * + * @param {Function} c The class constructor to make observable. + * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}. + * @static + */ + observe: function(cls, listeners) { + if (cls) { + if (!cls.isObservable) { + Ext.applyIf(cls, new this()); + this.capture(cls.prototype, cls.fireEvent, cls); + } + if (Ext.isObject(listeners)) { + cls.on(listeners); + } + return cls; + } + } }, - // Component passes itself into plugin's init method - init: function(c) { - var p, l = this.listeners; - for (p in l) { - if (Ext.isFunction(l[p])) { - l[p] = this.createHandler(l[p], c); - } else { - l[p].fn = this.createHandler(l[p].fn, c); - } + /* End Definitions */ + + /** + * @cfg {Object} listeners + * + * A config object containing one or more event handlers to be added to this object during initialization. This + * should be a valid listeners config object as specified in the {@link #addListener} example for attaching multiple + * handlers at once. + * + * **DOM events from Ext JS {@link Ext.Component Components}** + * + * While _some_ Ext JS Component classes export selected DOM events (e.g. "click", "mouseover" etc), this is usually + * only done when extra value can be added. For example the {@link Ext.view.View DataView}'s **`{@link + * Ext.view.View#itemclick itemclick}`** event passing the node clicked on. To access DOM events directly from a + * child element of a Component, we need to specify the `element` option to identify the Component property to add a + * DOM listener to: + * + * new Ext.panel.Panel({ + * width: 400, + * height: 200, + * dockedItems: [{ + * xtype: 'toolbar' + * }], + * listeners: { + * click: { + * element: 'el', //bind to the underlying el property on the panel + * fn: function(){ console.log('click el'); } + * }, + * dblclick: { + * element: 'body', //bind to the underlying body property on the panel + * fn: function(){ console.log('dblclick body'); } + * } + * } + * }); + */ + // @private + isObservable: true, + + constructor: function(config) { + var me = this; + + Ext.apply(me, config); + if (me.listeners) { + me.on(me.listeners); + delete me.listeners; } + me.events = me.events || {}; + + if (me.bubbleEvents) { + me.enableBubble(me.bubbleEvents); + } + }, - // Add the listeners to the Element immediately following the render call - c.render = c.render.{@link Function#createSequence createSequence}(function() { - var e = c.getEl(); - if (e) { - e.on(l); + // @private + eventOptionsRe : /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate|element|vertical|horizontal|freezeEvent)$/, + + /** + * Adds listeners to any Observable object (or Ext.Element) which are automatically removed when this Component is + * destroyed. + * + * @param {Ext.util.Observable/Ext.Element} item The item to which to add a listener/listeners. + * @param {Object/String} ename The event name, or an object containing event name properties. + * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function. + * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference) + * in which the handler function is executed. + * @param {Object} opt (optional) If the `ename` parameter was an event name, this is the + * {@link Ext.util.Observable#addListener addListener} options. + */ + addManagedListener : function(item, ename, fn, scope, options) { + var me = this, + managedListeners = me.managedListeners = me.managedListeners || [], + config; + + if (typeof ename !== 'string') { + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + me.addManagedListener(item, ename, config.fn || config, config.scope || options.scope, config.fn ? config : options); + } + } } - }); + } + else { + managedListeners.push({ + item: item, + ename: ename, + fn: fn, + scope: scope, + options: options + }); + + item.on(ename, fn, scope, options); + } }, - createHandler: function(fn, c) { - return function(e) { - fn.call(this, e, c); - }; - } -}); + /** + * Removes listeners that were added by the {@link #mon} method. + * + * @param {Ext.util.Observable/Ext.Element} item The item from which to remove a listener/listeners. + * @param {Object/String} ename The event name, or an object containing event name properties. + * @param {Function} fn (optional) If the `ename` parameter was an event name, this is the handler function. + * @param {Object} scope (optional) If the `ename` parameter was an event name, this is the scope (`this` reference) + * in which the handler function is executed. + */ + removeManagedListener : function(item, ename, fn, scope) { + var me = this, + options, + config, + managedListeners, + length, + i; + + if (typeof ename !== 'string') { + options = ename; + for (ename in options) { + if (options.hasOwnProperty(ename)) { + config = options[ename]; + if (!me.eventOptionsRe.test(ename)) { + me.removeManagedListener(item, ename, config.fn || config, config.scope || options.scope); + } + } + } + } -var combo = new Ext.form.ComboBox({ + managedListeners = me.managedListeners ? me.managedListeners.slice() : []; - // Collapse combo when its element is clicked on - plugins: [ new Ext.DomObserver({ - click: function(evt, comp) { - comp.collapse(); + for (i = 0, length = managedListeners.length; i < length; i++) { + me.removeManagedListenerItem(false, managedListeners[i], item, ename, fn, scope); } - })], - store: myStore, - typeAhead: true, - mode: 'local', - triggerAction: 'all' -}); - *
Fires the specified event with the passed parameters (minus the event name).
- *An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) - * by calling {@link #enableBubble}.
+ /** + * Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed + * to {@link #addListener}). + * + * An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget}) by + * calling {@link #enableBubble}. + * * @param {String} eventName The name of the event to fire. * @param {Object...} args Variable number of parameters are passed to handlers. * @return {Boolean} returns false if any of the handlers return false otherwise it returns true. */ - fireEvent : function(){ - var a = Array.prototype.slice.call(arguments, 0), - ename = a[0].toLowerCase(), - me = this, - ret = TRUE, - ce = me.events[ename], - cc, - q, - c; - if (me.eventsSuspended === TRUE) { - if (q = me.eventQueue) { - q.push(a); - } - } - else if(typeof ce == 'object') { - if (ce.bubble){ - if(ce.fire.apply(ce, a.slice(1)) === FALSE) { - return FALSE; + fireEvent: function(eventName) { + var name = eventName.toLowerCase(), + events = this.events, + event = events && events[name], + bubbles = event && event.bubble; + + return this.continueFireEvent(name, Ext.Array.slice(arguments, 1), bubbles); + }, + + /** + * Continue to fire event. + * @private + * + * @param {String} eventName + * @param {Array} args + * @param {Boolean} bubbles + */ + continueFireEvent: function(eventName, args, bubbles) { + var target = this, + queue, event, + ret = true; + + do { + if (target.eventsSuspended === true) { + if ((queue = target.eventQueue)) { + queue.push([eventName, args, bubbles]); } - c = me.getBubbleTarget && me.getBubbleTarget(); - if(c && c.enableBubble) { - cc = c.events[ename]; - if(!cc || typeof cc != 'object' || !cc.bubble) { - c.enableBubble(ename); + return ret; + } else { + event = target.events[eventName]; + // Continue bubbling if event exists and it is `true` or the handler didn't returns false and it + // configure to bubble. + if (event && event != true) { + if ((ret = event.fire.apply(event, args)) === false) { + break; } - return c.fireEvent.apply(c, a); } } - else { - a.shift(); - ret = ce.fire.apply(ce, a); - } - } + } while (bubbles && (target = target.getBubbleParent())); return ret; }, - /** - * Appends an event handler to this object. - * @param {String} eventName The name of the event to listen for. - * @param {Function} handler The method the event invokes. - * @param {Object} scope (optional) The scope (this
reference) in which the handler function is executed. - * If omitted, defaults to the object which fired the event. - * @param {Object} options (optional) An object containing handler configuration. - * properties. This may contain any of the following properties:
this
reference) in which the handler function is executed.
- * If omitted, defaults to the object which fired the event.
- * Combining Options
- * Using the options argument, it is possible to combine different types of listeners:
- *
+ /**
+ * Gets the bubbling parent for an Observable
+ * @private
+ * @return {Ext.util.Observable} The bubble parent. null is returned if no bubble target exists
+ */
+ getBubbleParent: function(){
+ var me = this, parent = me.getBubbleTarget && me.getBubbleTarget();
+ if (parent && parent.isObservable) {
+ return parent;
+ }
+ return null;
+ },
+
+ /**
+ * Appends an event handler to this object.
+ *
+ * @param {String} eventName The name of the event to listen for. May also be an object who's property names are
+ * event names.
+ * @param {Function} fn The method the event invokes. Will be called with arguments given to
+ * {@link #fireEvent} plus the `options` parameter described below.
+ * @param {Object} [scope] The scope (`this` reference) in which the handler function is executed. **If
+ * omitted, defaults to the object which fired the event.**
+ * @param {Object} [options] An object containing handler configuration.
+ *
+ * **Note:** Unlike in ExtJS 3.x, the options object will also be passed as the last argument to every event handler.
+ *
+ * This object may contain any of the following properties:
+ *
+ * - **scope** : Object
+ *
+ * The scope (`this` reference) in which the handler function is executed. **If omitted, defaults to the object
+ * which fired the event.**
+ *
+ * - **delay** : Number
+ *
+ * The number of milliseconds to delay the invocation of the handler after the event fires.
+ *
+ * - **single** : Boolean
+ *
+ * True to add a handler to handle just the next firing of the event, and then remove itself.
+ *
+ * - **buffer** : Number
+ *
+ * Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed by the specified number of
+ * milliseconds. If the event fires again within that time, the original handler is _not_ invoked, but the new
+ * handler is scheduled in its place.
+ *
+ * - **target** : Observable
+ *
+ * Only call the handler if the event was fired on the target Observable, _not_ if the event was bubbled up from a
+ * child Observable.
+ *
+ * - **element** : String
+ *
+ * **This option is only valid for listeners bound to {@link Ext.Component Components}.** The name of a Component
+ * property which references an element to add a listener to.
+ *
+ * This option is useful during Component construction to add DOM event listeners to elements of
+ * {@link Ext.Component Components} which will exist only after the Component is rendered.
+ * For example, to add a click listener to a Panel's body:
+ *
+ * new Ext.panel.Panel({
+ * title: 'The title',
+ * listeners: {
+ * click: this.handlePanelClick,
+ * element: 'body'
+ * }
+ * });
+ *
+ * **Combining Options**
+ *
+ * Using the options argument, it is possible to combine different types of listeners:
+ *
* A delayed, one-time listener.
- *
-myDataView.on('click', this.onClick, this, {
-single: true,
-delay: 100
-});
- *
- * Attaching multiple handlers in 1 call
- * The method also allows for a single argument to be passed which is a config object containing properties
- * which specify multiple handlers.
- *
- *
-myGridPanel.on({
-'click' : {
- fn: this.onClick,
- scope: this,
- delay: 100
-},
-'mouseover' : {
- fn: this.onMouseOver,
- scope: this
-},
-'mouseout' : {
- fn: this.onMouseOut,
- scope: this
-}
-});
- *
- * Or a shorthand syntax:
- *
-myGridPanel.on({
-'click' : this.onClick,
-'mouseover' : this.onMouseOver,
-'mouseout' : this.onMouseOut,
- scope: this
-});
+ *
+ * myPanel.on('hide', this.handleClick, this, {
+ * single: true,
+ * delay: 100
+ * });
+ *
+ * **Attaching multiple handlers in 1 call**
+ *
+ * The method also allows for a single argument to be passed which is a config object containing properties which
+ * specify multiple events. For example:
+ *
+ * myGridPanel.on({
+ * cellClick: this.onCellClick,
+ * mouseover: this.onMouseOver,
+ * mouseout: this.onMouseOut,
+ * scope: this // Important. Ensure "this" is correct during handler execution
+ * });
+ *
+ * One can also specify options for each event handler separately:
+ *
+ * myGridPanel.on({
+ * cellClick: {fn: this.onCellClick, scope: this, single: true},
+ * mouseover: {fn: panel.onMouseOver, scope: panel}
+ * });
+ *
*/
- addListener : function(eventName, fn, scope, o){
+ addListener: function(ename, fn, scope, options) {
var me = this,
- e,
- oe,
- ce;
-
- if (typeof eventName == 'object') {
- o = eventName;
- for (e in o) {
- oe = o[e];
- if (!me.filterOptRe.test(e)) {
- me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
+ config,
+ event;
+
+ if (typeof ename !== 'string') {
+ options = ename;
+ for (ename in options) {
+ if (options.hasOwnProperty(ename)) {
+ config = options[ename];
+ if (!me.eventOptionsRe.test(ename)) {
+ me.addListener(ename, config.fn || config, config.scope || options.scope, config.fn ? config : options);
+ }
}
}
- } else {
- eventName = eventName.toLowerCase();
- ce = me.events[eventName] || TRUE;
- if (typeof ce == 'boolean') {
- me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
+ }
+ else {
+ ename = ename.toLowerCase();
+ me.events[ename] = me.events[ename] || true;
+ event = me.events[ename] || true;
+ if (Ext.isBoolean(event)) {
+ me.events[ename] = event = new Ext.util.Event(me, ename);
}
- ce.addListener(fn, scope, typeof o == 'object' ? o : {});
+ event.addListener(fn, scope, Ext.isObject(options) ? options : {});
}
},
- /**
- * Removes an event handler.
- * @param {String} eventName The type of event the handler was associated with.
- * @param {Function} handler The handler to remove. This must be a reference to the function passed into the {@link #addListener} call.
- * @param {Object} scope (optional) The scope originally specified for the handler.
+ /**
+ * Removes an event handler.
+ *
+ * @param {String} eventName The type of event the handler was associated with.
+ * @param {Function} fn The handler to remove. **This must be a reference to the function passed into the
+ * {@link #addListener} call.**
+ * @param {Object} scope (optional) The scope originally specified for the handler. It must be the same as the
+ * scope argument specified in the original call to {@link #addListener} or the listener will not be removed.
*/
- removeListener : function(eventName, fn, scope){
- var ce = this.events[eventName.toLowerCase()];
- if (typeof ce == 'object') {
- ce.removeListener(fn, scope);
+ removeListener: function(ename, fn, scope) {
+ var me = this,
+ config,
+ event,
+ options;
+
+ if (typeof ename !== 'string') {
+ options = ename;
+ for (ename in options) {
+ if (options.hasOwnProperty(ename)) {
+ config = options[ename];
+ if (!me.eventOptionsRe.test(ename)) {
+ me.removeListener(ename, config.fn || config, config.scope || options.scope);
+ }
+ }
+ }
+ } else {
+ ename = ename.toLowerCase();
+ event = me.events[ename];
+ if (event && event.isEvent) {
+ event.removeListener(fn, scope);
+ }
}
},
- /**
- * Removes all listeners for this object
+ /**
+ * Removes all listeners for this object including the managed listeners
*/
- purgeListeners : function(){
+ clearListeners: function() {
var events = this.events,
- evt,
+ event,
key;
- for(key in events){
- evt = events[key];
- if(typeof evt == 'object'){
- evt.clearListeners();
+
+ for (key in events) {
+ if (events.hasOwnProperty(key)) {
+ event = events[key];
+ if (event.isEvent) {
+ event.clearListeners();
+ }
}
}
+
+ this.clearManagedListeners();
},
- /**
- * Adds the specified events to the list of events which this Observable may fire.
- * @param {Object|String} o Either an object with event names as properties with a value of true
- * or the first event name string if multiple event names are being passed as separate parameters.
- * @param {string} Optional. Event name if multiple event names are being passed as separate parameters.
- * Usage:
-this.addEvents('storeloaded', 'storecleared');
-
+ //<debug>
+ purgeListeners : function() {
+ if (Ext.global.console) {
+ Ext.global.console.warn('Observable: purgeListeners has been deprecated. Please use clearListeners.');
+ }
+ return this.clearListeners.apply(this, arguments);
+ },
+ //</debug>
+
+ /**
+ * Removes all managed listeners for this object.
*/
- addEvents : function(o){
- var me = this;
- me.events = me.events || {};
- if (typeof o == 'string') {
- var a = arguments,
- i = a.length;
- while(i--) {
- me.events[a[i]] = me.events[a[i]] || TRUE;
+ clearManagedListeners : function() {
+ var managedListeners = this.managedListeners || [],
+ i = 0,
+ len = managedListeners.length;
+
+ for (; i < len; i++) {
+ this.removeManagedListenerItem(true, managedListeners[i]);
+ }
+
+ this.managedListeners = [];
+ },
+
+ /**
+ * Remove a single managed listener item
+ * @private
+ * @param {Boolean} isClear True if this is being called during a clear
+ * @param {Object} managedListener The managed listener item
+ * See removeManagedListener for other args
+ */
+ removeManagedListenerItem: function(isClear, managedListener, item, ename, fn, scope){
+ if (isClear || (managedListener.item === item && managedListener.ename === ename && (!fn || managedListener.fn === fn) && (!scope || managedListener.scope === scope))) {
+ managedListener.item.un(managedListener.ename, managedListener.fn, managedListener.scope);
+ if (!isClear) {
+ Ext.Array.remove(this.managedListeners, managedListener);
+ }
+ }
+ },
+
+ //<debug>
+ purgeManagedListeners : function() {
+ if (Ext.global.console) {
+ Ext.global.console.warn('Observable: purgeManagedListeners has been deprecated. Please use clearManagedListeners.');
+ }
+ return this.clearManagedListeners.apply(this, arguments);
+ },
+ //</debug>
+
+ /**
+ * Adds the specified events to the list of events which this Observable may fire.
+ *
+ * @param {Object/String} o Either an object with event names as properties with a value of `true` or the first
+ * event name string if multiple event names are being passed as separate parameters. Usage:
+ *
+ * this.addEvents({
+ * storeloaded: true,
+ * storecleared: true
+ * });
+ *
+ * @param {String...} more (optional) Additional event names if multiple event names are being passed as separate
+ * parameters. Usage:
+ *
+ * this.addEvents('storeloaded', 'storecleared');
+ *
+ */
+ addEvents: function(o) {
+ var me = this,
+ args,
+ len,
+ i;
+
+ me.events = me.events || {};
+ if (Ext.isString(o)) {
+ args = arguments;
+ i = args.length;
+
+ while (i--) {
+ me.events[args[i]] = me.events[args[i]] || true;
}
} else {
Ext.applyIf(me.events, o);
}
},
- /**
- * Checks to see if this object has any listeners for a specified event
+ /**
+ * Checks to see if this object has any listeners for a specified event
+ *
* @param {String} eventName The name of the event to check for
* @return {Boolean} True if the event is being listened for, else false
*/
- hasListener : function(eventName){
- var e = this.events[eventName.toLowerCase()];
- return typeof e == 'object' && e.listeners.length > 0;
+ hasListener: function(ename) {
+ var event = this.events[ename.toLowerCase()];
+ return event && event.isEvent === true && event.listeners.length > 0;
},
- /**
- * Suspend the firing of all events. (see {@link #resumeEvents})
+ /**
+ * Suspends the firing of all events. (see {@link #resumeEvents})
+ *
* @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
- * after the {@link #resumeEvents} call instead of discarding all suspended events;
+ * after the {@link #resumeEvents} call instead of discarding all suspended events.
*/
- suspendEvents : function(queueSuspended){
- this.eventsSuspended = TRUE;
- if(queueSuspended && !this.eventQueue){
+ suspendEvents: function(queueSuspended) {
+ this.eventsSuspended = true;
+ if (queueSuspended && !this.eventQueue) {
this.eventQueue = [];
}
},
- /**
- * Resume firing events. (see {@link #suspendEvents})
- * If events were suspended using the queueSuspended parameter, then all
- * events fired during event suspension will be sent to any listeners now.
+ /**
+ * Resumes firing events (see {@link #suspendEvents}).
+ *
+ * If events were suspended using the `queueSuspended` parameter, then all events fired
+ * during event suspension will be sent to any listeners now.
*/
- resumeEvents : function(){
+ resumeEvents: function() {
var me = this,
- queued = me.eventQueue || [];
- me.eventsSuspended = FALSE;
+ queued = me.eventQueue;
+
+ me.eventsSuspended = false;
delete me.eventQueue;
- EACH(queued, function(e) {
- me.fireEvent.apply(me, e);
- });
- }
-};
-
-var OBSERVABLE = EXTUTIL.Observable.prototype;
-/**
- * Appends an event handler to this object (shorthand for {@link #addListener}.)
- * @param {String} eventName The type of event to listen for
- * @param {Function} handler The method the event invokes
- * @param {Object} scope (optional) The scope (this
reference) in which the handler function is executed.
- * If omitted, defaults to the object which fired the event.
- * @param {Object} options (optional) An object containing handler configuration.
- * @method
- */
-OBSERVABLE.on = OBSERVABLE.addListener;
-/**
- * Removes an event handler (shorthand for {@link #removeListener}.)
- * @param {String} eventName The type of event the handler was associated with.
- * @param {Function} handler The handler to remove. This must be a reference to the function passed into the {@link #addListener} call.
- * @param {Object} scope (optional) The scope originally specified for the handler.
- * @method
- */
-OBSERVABLE.un = OBSERVABLE.removeListener;
-/**
- * Removes all added captures from the Observable.
- * @param {Observable} o The Observable to release
- * @static
- */
-EXTUTIL.Observable.releaseCapture = function(o){
- o.fireEvent = OBSERVABLE.fireEvent;
-};
-
-function createTargeted(h, o, scope){
- return function(){
- if(o.target == arguments[0]){
- h.apply(scope, Array.prototype.slice.call(arguments, 0));
+ if (queued) {
+ Ext.each(queued, function(e) {
+ me.continueFireEvent.apply(me, e);
+ });
}
- };
-};
-
-function createBuffered(h, o, l, scope){
- l.task = new EXTUTIL.DelayedTask();
- return function(){
- l.task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
- };
-};
-
-function createSingle(h, e, fn, scope){
- return function(){
- e.removeListener(fn, scope);
- return h.apply(scope, arguments);
- };
-};
-
-function createDelayed(h, o, l, scope){
- return function(){
- var task = new EXTUTIL.DelayedTask(),
- args = Array.prototype.slice.call(arguments, 0);
- if(!l.tasks) {
- l.tasks = [];
- }
- l.tasks.push(task);
- task.delay(o.delay || 10, function(){
- l.tasks.remove(task);
- h.apply(scope, args);
- }, scope);
- };
-};
-
-EXTUTIL.Event = function(obj, name){
- this.name = name;
- this.obj = obj;
- this.listeners = [];
-};
-
-EXTUTIL.Event.prototype = {
- addListener : function(fn, scope, options){
+ },
+
+ /**
+ * Relays selected events from the specified Observable as if the events were fired by `this`.
+ *
+ * @param {Object} origin The Observable whose events this object is to relay.
+ * @param {String[]} events Array of event names to relay.
+ * @param {String} prefix
+ */
+ relayEvents : function(origin, events, prefix) {
+ prefix = prefix || '';
var me = this,
- l;
- scope = scope || me.obj;
- if(!me.isListening(fn, scope)){
- l = me.createListener(fn, scope, options);
- if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
- me.listeners = me.listeners.slice(0);
- }
- me.listeners.push(l);
+ len = events.length,
+ i = 0,
+ oldName,
+ newName;
+
+ for (; i < len; i++) {
+ oldName = events[i].substr(prefix.length);
+ newName = prefix + oldName;
+ me.events[newName] = me.events[newName] || true;
+ origin.on(oldName, me.createRelayer(newName));
}
},
- createListener: function(fn, scope, o){
- o = o || {};
- scope = scope || this.obj;
- var l = {
- fn: fn,
- scope: scope,
- options: o
- }, h = fn;
- if(o.target){
- h = createTargeted(h, o, scope);
- }
- if(o.delay){
- h = createDelayed(h, o, l, scope);
- }
- if(o.single){
- h = createSingle(h, this, fn, scope);
- }
- if(o.buffer){
- h = createBuffered(h, o, l, scope);
- }
- l.fireFn = h;
- return l;
+ /**
+ * @private
+ * Creates an event handling function which refires the event from this object as the passed event name.
+ * @param newName
+ * @returns {Function}
+ */
+ createRelayer: function(newName){
+ var me = this;
+ return function(){
+ return me.fireEvent.apply(me, [newName].concat(Array.prototype.slice.call(arguments, 0, -1)));
+ };
},
- findListener : function(fn, scope){
- var list = this.listeners,
- i = list.length,
- l;
-
- scope = scope || this.obj;
- while(i--){
- l = list[i];
- if(l){
- if(l.fn == fn && l.scope == scope){
- return i;
+ /**
+ * Enables events fired by this Observable to bubble up an owner hierarchy by calling `this.getBubbleTarget()` if
+ * present. There is no implementation in the Observable base class.
+ *
+ * This is commonly used by Ext.Components to bubble events to owner Containers.
+ * See {@link Ext.Component#getBubbleTarget}. The default implementation in Ext.Component returns the
+ * Component's immediate owner. But if a known target is required, this can be overridden to access the
+ * required target more quickly.
+ *
+ * Example:
+ *
+ * Ext.override(Ext.form.field.Base, {
+ * // Add functionality to Field's initComponent to enable the change event to bubble
+ * initComponent : Ext.Function.createSequence(Ext.form.field.Base.prototype.initComponent, function() {
+ * this.enableBubble('change');
+ * }),
+ *
+ * // We know that we want Field's events to bubble directly to the FormPanel.
+ * getBubbleTarget : function() {
+ * if (!this.formPanel) {
+ * this.formPanel = this.findParentByType('form');
+ * }
+ * return this.formPanel;
+ * }
+ * });
+ *
+ * var myForm = new Ext.formPanel({
+ * title: 'User Details',
+ * items: [{
+ * ...
+ * }],
+ * listeners: {
+ * change: function() {
+ * // Title goes red if form has been modified.
+ * myForm.header.setStyle('color', 'red');
+ * }
+ * }
+ * });
+ *
+ * @param {String/String[]} events The event name to bubble, or an Array of event names.
+ */
+ enableBubble: function(events) {
+ var me = this;
+ if (!Ext.isEmpty(events)) {
+ events = Ext.isArray(events) ? events: Ext.Array.toArray(arguments);
+ Ext.each(events,
+ function(ename) {
+ ename = ename.toLowerCase();
+ var ce = me.events[ename] || true;
+ if (Ext.isBoolean(ce)) {
+ ce = new Ext.util.Event(me, ename);
+ me.events[ename] = ce;
}
- }
+ ce.bubble = true;
+ });
}
- return -1;
- },
+ }
+}, function() {
+
+ this.createAlias({
+ /**
+ * @method
+ * Shorthand for {@link #addListener}.
+ * @alias Ext.util.Observable#addListener
+ */
+ on: 'addListener',
+ /**
+ * @method
+ * Shorthand for {@link #removeListener}.
+ * @alias Ext.util.Observable#removeListener
+ */
+ un: 'removeListener',
+ /**
+ * @method
+ * Shorthand for {@link #addManagedListener}.
+ * @alias Ext.util.Observable#addManagedListener
+ */
+ mon: 'addManagedListener',
+ /**
+ * @method
+ * Shorthand for {@link #removeManagedListener}.
+ * @alias Ext.util.Observable#removeManagedListener
+ */
+ mun: 'removeManagedListener'
+ });
+
+ //deprecated, will be removed in 5.0
+ this.observeClass = this.observe;
+
+ Ext.apply(Ext.util.Observable.prototype, function(){
+ // this is considered experimental (along with beforeMethod, afterMethod, removeMethodListener?)
+ // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
+ // private
+ function getMethodEvent(method){
+ var e = (this.methodEvents = this.methodEvents || {})[method],
+ returnValue,
+ v,
+ cancel,
+ obj = this;
+
+ if (!e) {
+ this.methodEvents[method] = e = {};
+ e.originalFn = this[method];
+ e.methodName = method;
+ e.before = [];
+ e.after = [];
+
+ var makeCall = function(fn, scope, args){
+ if((v = fn.apply(scope || obj, args)) !== undefined){
+ if (typeof v == 'object') {
+ if(v.returnValue !== undefined){
+ returnValue = v.returnValue;
+ }else{
+ returnValue = v;
+ }
+ cancel = !!v.cancel;
+ }
+ else
+ if (v === false) {
+ cancel = true;
+ }
+ else {
+ returnValue = v;
+ }
+ }
+ };
+
+ this[method] = function(){
+ var args = Array.prototype.slice.call(arguments, 0),
+ b, i, len;
+ returnValue = v = undefined;
+ cancel = false;
+
+ for(i = 0, len = e.before.length; i < len; i++){
+ b = e.before[i];
+ makeCall(b.fn, b.scope, args);
+ if (cancel) {
+ return returnValue;
+ }
+ }
- isListening : function(fn, scope){
- return this.findListener(fn, scope) != -1;
- },
+ if((v = e.originalFn.apply(obj, args)) !== undefined){
+ returnValue = v;
+ }
- removeListener : function(fn, scope){
- var index,
- l,
- k,
- me = this,
- ret = FALSE;
- if((index = me.findListener(fn, scope)) != -1){
- if (me.firing) {
- me.listeners = me.listeners.slice(0);
- }
- l = me.listeners[index];
- if(l.task) {
- l.task.cancel();
- delete l.task;
- }
- k = l.tasks && l.tasks.length;
- if(k) {
- while(k--) {
- l.tasks[k].cancel();
- }
- delete l.tasks;
+ for(i = 0, len = e.after.length; i < len; i++){
+ b = e.after[i];
+ makeCall(b.fn, b.scope, args);
+ if (cancel) {
+ return returnValue;
+ }
+ }
+ return returnValue;
+ };
}
- me.listeners.splice(index, 1);
- ret = TRUE;
- }
- return ret;
- },
-
- // Iterate to stop any buffered/delayed events
- clearListeners : function(){
- var me = this,
- l = me.listeners,
- i = l.length;
- while(i--) {
- me.removeListener(l[i].fn, l[i].scope);
+ return e;
}
- },
- fire : function(){
- var me = this,
- listeners = me.listeners,
- len = listeners.length,
- i = 0,
- l;
-
- if(len > 0){
- me.firing = TRUE;
- var args = Array.prototype.slice.call(arguments, 0);
- for (; i < len; i++) {
- l = listeners[i];
- if(l && l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
- return (me.firing = FALSE);
+ return {
+ // these are considered experimental
+ // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
+ // adds an 'interceptor' called before the original method
+ beforeMethod : function(method, fn, scope){
+ getMethodEvent.call(this, method).before.push({
+ fn: fn,
+ scope: scope
+ });
+ },
+
+ // adds a 'sequence' called after the original method
+ afterMethod : function(method, fn, scope){
+ getMethodEvent.call(this, method).after.push({
+ fn: fn,
+ scope: scope
+ });
+ },
+
+ removeMethodListener: function(method, fn, scope){
+ var e = this.getMethodEvent(method),
+ i, len;
+ for(i = 0, len = e.before.length; i < len; i++){
+ if(e.before[i].fn == fn && e.before[i].scope == scope){
+ Ext.Array.erase(e.before, i, 1);
+ return;
+ }
}
- }
- }
- me.firing = FALSE;
- return TRUE;
- }
+ for(i = 0, len = e.after.length; i < len; i++){
+ if(e.after[i].fn == fn && e.after[i].scope == scope){
+ Ext.Array.erase(e.after, i, 1);
+ return;
+ }
+ }
+ },
-};
-})();
-
+ toggleEventLogging: function(toggle) {
+ Ext.util.Observable[toggle ? 'capture' : 'releaseCapture'](this, function(en) {
+ if (Ext.isDefined(Ext.global.console)) {
+ Ext.global.console.log(en, arguments);
+ }
+ });
+ }
+ };
+ }());
+});
+
-
\ No newline at end of file
+