940 lines
32 KiB
JavaScript
940 lines
32 KiB
JavaScript
/**
|
|
* Mixin that provides a common interface for publishing events. Classes using this mixin can use the {@link #fireEvent}
|
|
* and {@link #fireAction} methods to notify listeners of events on the class.
|
|
*
|
|
* Classes can also define a {@link #listeners} config to add an event handler to the current object. See
|
|
* {@link #addListener} for more details.
|
|
*
|
|
* ## Example
|
|
*
|
|
* Ext.define('Employee', {
|
|
* mixins: ['Ext.mixin.Observable'],
|
|
*
|
|
* config: {
|
|
* fullName: ''
|
|
* },
|
|
*
|
|
* constructor: function(config) {
|
|
* this.initConfig(config); // We need to initialize the config options when the class is instantiated
|
|
* },
|
|
*
|
|
* quitJob: function() {
|
|
* this.fireEvent('quit');
|
|
* }
|
|
* });
|
|
*
|
|
* var newEmployee = Ext.create('Employee', {
|
|
*
|
|
* fullName: 'Ed Spencer',
|
|
*
|
|
* listeners: {
|
|
* quit: function() { // This function will be called when the 'quit' event is fired
|
|
* // By default, "this" will be the object that fired the event.
|
|
* console.log(this.getFullName() + " has quit!");
|
|
* }
|
|
* }
|
|
* });
|
|
*
|
|
* newEmployee.quitJob(); // Will log 'Ed Spencer has quit!'
|
|
*
|
|
* @aside guide events
|
|
*/
|
|
Ext.define('Ext.mixin.Observable', {
|
|
|
|
requires: ['Ext.event.Dispatcher'],
|
|
|
|
extend: 'Ext.mixin.Mixin',
|
|
|
|
mixins: ['Ext.mixin.Identifiable'],
|
|
|
|
mixinConfig: {
|
|
id: 'observable',
|
|
hooks: {
|
|
destroy: 'destroy'
|
|
}
|
|
},
|
|
|
|
alternateClassName: 'Ext.util.Observable',
|
|
|
|
// @private
|
|
isObservable: true,
|
|
|
|
observableType: 'observable',
|
|
|
|
validIdRegex: /^([\w\-]+)$/,
|
|
|
|
observableIdPrefix: '#',
|
|
|
|
listenerOptionsRegex: /^(?:delegate|single|delay|buffer|args|prepend)$/,
|
|
|
|
config: {
|
|
/**
|
|
* @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.
|
|
*
|
|
* See the [Event guide](#!/guide/events) for more
|
|
*
|
|
* __Note:__ It is bad practice to specify a listener's `config` when you are defining a class using `Ext.define()`.
|
|
* Instead, only specify listeners when you are instantiating your class with `Ext.create()`.
|
|
* @accessor
|
|
*/
|
|
listeners: null,
|
|
|
|
/**
|
|
* @cfg {String/String[]} bubbleEvents The event name to bubble, or an Array of event names.
|
|
* @accessor
|
|
*/
|
|
bubbleEvents: null
|
|
},
|
|
|
|
constructor: function(config) {
|
|
this.initConfig(config);
|
|
},
|
|
|
|
applyListeners: function(listeners) {
|
|
if (listeners) {
|
|
this.addListener(listeners);
|
|
}
|
|
},
|
|
|
|
applyBubbleEvents: function(bubbleEvents) {
|
|
if (bubbleEvents) {
|
|
this.enableBubble(bubbleEvents);
|
|
}
|
|
},
|
|
|
|
getOptimizedObservableId: function() {
|
|
return this.observableId;
|
|
},
|
|
|
|
getObservableId: function() {
|
|
if (!this.observableId) {
|
|
var id = this.getUniqueId();
|
|
|
|
//<debug error>
|
|
if (!id.match(this.validIdRegex)) {
|
|
Ext.Logger.error("Invalid unique id of '" + id + "' for this object", this);
|
|
}
|
|
//</debug>
|
|
|
|
this.observableId = this.observableIdPrefix + id;
|
|
|
|
this.getObservableId = this.getOptimizedObservableId;
|
|
}
|
|
|
|
return this.observableId;
|
|
},
|
|
|
|
getOptimizedEventDispatcher: function() {
|
|
return this.eventDispatcher;
|
|
},
|
|
|
|
getEventDispatcher: function() {
|
|
if (!this.eventDispatcher) {
|
|
this.eventDispatcher = Ext.event.Dispatcher.getInstance();
|
|
this.getEventDispatcher = this.getOptimizedEventDispatcher;
|
|
|
|
this.getListeners();
|
|
this.getBubbleEvents();
|
|
}
|
|
|
|
return this.eventDispatcher;
|
|
},
|
|
|
|
getManagedListeners: function(object, eventName) {
|
|
var id = object.getUniqueId(),
|
|
managedListeners = this.managedListeners;
|
|
|
|
if (!managedListeners) {
|
|
this.managedListeners = managedListeners = {};
|
|
}
|
|
|
|
if (!managedListeners[id]) {
|
|
managedListeners[id] = {};
|
|
object.doAddListener('destroy', 'clearManagedListeners', this, {
|
|
single: true,
|
|
args: [object]
|
|
});
|
|
}
|
|
|
|
if (!managedListeners[id][eventName]) {
|
|
managedListeners[id][eventName] = [];
|
|
}
|
|
|
|
return managedListeners[id][eventName];
|
|
},
|
|
|
|
getUsedSelectors: function() {
|
|
var selectors = this.usedSelectors;
|
|
|
|
if (!selectors) {
|
|
selectors = this.usedSelectors = [];
|
|
selectors.$map = {};
|
|
}
|
|
|
|
return selectors;
|
|
},
|
|
|
|
/**
|
|
* Fires the specified event with the passed parameters (minus the event name, plus the `options` object passed
|
|
* to {@link #addListener}).
|
|
*
|
|
* The first argument is the name of the event. Every other argument passed will be available when you listen for
|
|
* the event.
|
|
*
|
|
* ## Example
|
|
*
|
|
* Firstly, we set up a listener for our new event.
|
|
*
|
|
* this.on('myevent', function(arg1, arg2, arg3, arg4, options, e) {
|
|
* console.log(arg1); // true
|
|
* console.log(arg2); // 2
|
|
* console.log(arg3); // { test: 'foo' }
|
|
* console.log(arg4); // 14
|
|
* console.log(options); // the options added when adding the listener
|
|
* console.log(e); // the event object with information about the event
|
|
* });
|
|
*
|
|
* And then we can fire off the event.
|
|
*
|
|
* this.fireEvent('myevent', true, 2, { test: 'foo' }, 14);
|
|
*
|
|
* An event may be set to bubble up an Observable parent hierarchy 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(eventName) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
|
|
return this.doFireEvent(eventName, args);
|
|
},
|
|
|
|
/**
|
|
* Fires the specified event with the passed parameters and execute a function (action)
|
|
* at the end if there are no listeners that return `false`.
|
|
*
|
|
* @param {String} eventName The name of the event to fire.
|
|
* @param {Array} args Arguments to pass to handers.
|
|
* @param {Function} fn Action.
|
|
* @param {Object} scope Scope of fn.
|
|
* @return {Object}
|
|
*/
|
|
fireAction: function(eventName, args, fn, scope, options, order) {
|
|
var fnType = typeof fn,
|
|
action;
|
|
|
|
if (args === undefined) {
|
|
args = [];
|
|
}
|
|
|
|
if (fnType != 'undefined') {
|
|
action = {
|
|
fn: fn,
|
|
isLateBinding: fnType == 'string',
|
|
scope: scope || this,
|
|
options: options || {},
|
|
order: order
|
|
};
|
|
}
|
|
|
|
return this.doFireEvent(eventName, args, action);
|
|
},
|
|
|
|
doFireEvent: function(eventName, args, action, connectedController) {
|
|
if (this.eventFiringSuspended) {
|
|
return;
|
|
}
|
|
|
|
var id = this.getObservableId(),
|
|
dispatcher = this.getEventDispatcher();
|
|
|
|
return dispatcher.dispatchEvent(this.observableType, id, eventName, args, action, connectedController);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param name
|
|
* @param fn
|
|
* @param scope
|
|
* @param options
|
|
* @return {Boolean}
|
|
*/
|
|
doAddListener: function(name, fn, scope, options, order) {
|
|
var isManaged = (scope && scope !== this && scope.isIdentifiable),
|
|
usedSelectors = this.getUsedSelectors(),
|
|
usedSelectorsMap = usedSelectors.$map,
|
|
selector = this.getObservableId(),
|
|
isAdded, managedListeners, delegate;
|
|
|
|
if (!options) {
|
|
options = {};
|
|
}
|
|
|
|
if (!scope) {
|
|
scope = this;
|
|
}
|
|
|
|
if (options.delegate) {
|
|
delegate = options.delegate;
|
|
// See https://sencha.jira.com/browse/TOUCH-1579
|
|
selector += ' ' + delegate;
|
|
}
|
|
|
|
if (!(selector in usedSelectorsMap)) {
|
|
usedSelectorsMap[selector] = true;
|
|
usedSelectors.push(selector);
|
|
}
|
|
|
|
isAdded = this.addDispatcherListener(selector, name, fn, scope, options, order);
|
|
|
|
if (isAdded && isManaged) {
|
|
managedListeners = this.getManagedListeners(scope, name);
|
|
managedListeners.push({
|
|
delegate: delegate,
|
|
scope: scope,
|
|
fn: fn,
|
|
order: order
|
|
});
|
|
}
|
|
|
|
return isAdded;
|
|
},
|
|
|
|
addDispatcherListener: function(selector, name, fn, scope, options, order) {
|
|
return this.getEventDispatcher().addListener(this.observableType, selector, name, fn, scope, options, order);
|
|
},
|
|
|
|
doRemoveListener: function(name, fn, scope, options, order) {
|
|
var isManaged = (scope && scope !== this && scope.isIdentifiable),
|
|
selector = this.getObservableId(),
|
|
isRemoved,
|
|
managedListeners, i, ln, listener, delegate;
|
|
|
|
if (options && options.delegate) {
|
|
delegate = options.delegate;
|
|
// See https://sencha.jira.com/browse/TOUCH-1579
|
|
selector += ' ' + delegate;
|
|
}
|
|
|
|
if (!scope) {
|
|
scope = this;
|
|
}
|
|
|
|
isRemoved = this.removeDispatcherListener(selector, name, fn, scope, order);
|
|
|
|
if (isRemoved && isManaged) {
|
|
managedListeners = this.getManagedListeners(scope, name);
|
|
|
|
for (i = 0,ln = managedListeners.length; i < ln; i++) {
|
|
listener = managedListeners[i];
|
|
|
|
if (listener.fn === fn && listener.scope === scope && listener.delegate === delegate && listener.order === order) {
|
|
managedListeners.splice(i, 1);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isRemoved;
|
|
},
|
|
|
|
removeDispatcherListener: function(selector, name, fn, scope, order) {
|
|
return this.getEventDispatcher().removeListener(this.observableType, selector, name, fn, scope, order);
|
|
},
|
|
|
|
clearManagedListeners: function(object) {
|
|
var managedListeners = this.managedListeners,
|
|
id, namedListeners, listeners, eventName, i, ln, listener, options;
|
|
|
|
if (!managedListeners) {
|
|
return this;
|
|
}
|
|
|
|
if (object) {
|
|
if (typeof object != 'string') {
|
|
id = object.getUniqueId();
|
|
}
|
|
else {
|
|
id = object;
|
|
}
|
|
|
|
namedListeners = managedListeners[id];
|
|
|
|
for (eventName in namedListeners) {
|
|
if (namedListeners.hasOwnProperty(eventName)) {
|
|
listeners = namedListeners[eventName];
|
|
|
|
for (i = 0,ln = listeners.length; i < ln; i++) {
|
|
listener = listeners[i];
|
|
|
|
options = {};
|
|
|
|
if (listener.delegate) {
|
|
options.delegate = listener.delegate;
|
|
}
|
|
|
|
if (this.doRemoveListener(eventName, listener.fn, listener.scope, options, listener.order)) {
|
|
i--;
|
|
ln--;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete managedListeners[id];
|
|
return this;
|
|
}
|
|
|
|
for (id in managedListeners) {
|
|
if (managedListeners.hasOwnProperty(id)) {
|
|
this.clearManagedListeners(id);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param operation
|
|
* @param eventName
|
|
* @param fn
|
|
* @param scope
|
|
* @param options
|
|
* @param order
|
|
* @return {Object}
|
|
*/
|
|
changeListener: function(actionFn, eventName, fn, scope, options, order) {
|
|
var eventNames,
|
|
listeners,
|
|
listenerOptionsRegex,
|
|
actualOptions,
|
|
name, value, i, ln, listener, valueType;
|
|
|
|
if (typeof fn != 'undefined') {
|
|
// Support for array format to add multiple listeners
|
|
if (typeof eventName != 'string') {
|
|
for (i = 0,ln = eventName.length; i < ln; i++) {
|
|
name = eventName[i];
|
|
|
|
actionFn.call(this, name, fn, scope, options, order);
|
|
}
|
|
|
|
return this;
|
|
}
|
|
|
|
actionFn.call(this, eventName, fn, scope, options, order);
|
|
}
|
|
else if (Ext.isArray(eventName)) {
|
|
listeners = eventName;
|
|
|
|
for (i = 0,ln = listeners.length; i < ln; i++) {
|
|
listener = listeners[i];
|
|
|
|
actionFn.call(this, listener.event, listener.fn, listener.scope, listener, listener.order);
|
|
}
|
|
}
|
|
else {
|
|
listenerOptionsRegex = this.listenerOptionsRegex;
|
|
options = eventName;
|
|
eventNames = [];
|
|
listeners = [];
|
|
actualOptions = {};
|
|
|
|
for (name in options) {
|
|
value = options[name];
|
|
|
|
if (name === 'scope') {
|
|
scope = value;
|
|
continue;
|
|
}
|
|
else if (name === 'order') {
|
|
order = value;
|
|
continue;
|
|
}
|
|
|
|
if (!listenerOptionsRegex.test(name)) {
|
|
valueType = typeof value;
|
|
|
|
if (valueType != 'string' && valueType != 'function') {
|
|
actionFn.call(this, name, value.fn, value.scope || scope, value, value.order || order);
|
|
continue;
|
|
}
|
|
|
|
eventNames.push(name);
|
|
listeners.push(value);
|
|
}
|
|
else {
|
|
actualOptions[name] = value;
|
|
}
|
|
}
|
|
|
|
for (i = 0,ln = eventNames.length; i < ln; i++) {
|
|
actionFn.call(this, eventNames[i], listeners[i], scope, actualOptions, order);
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* Appends an event handler to this object. You can review the available handlers by looking at the 'events'
|
|
* section of the documentation for the component you are working with.
|
|
*
|
|
* ## Combining Options
|
|
*
|
|
* Using the options argument, it is possible to combine different types of listeners:
|
|
*
|
|
* A delayed, one-time listener:
|
|
*
|
|
* container.on('tap', this.handleTap, 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:
|
|
*
|
|
* container.on({
|
|
* tap : this.onTap,
|
|
* swipe: this.onSwipe,
|
|
*
|
|
* scope: this // Important. Ensure "this" is correct during handler execution
|
|
* });
|
|
*
|
|
* One can also specify options for each event handler separately:
|
|
*
|
|
* container.on({
|
|
* tap : { fn: this.onTap, scope: this, single: true },
|
|
* swipe: { fn: button.onSwipe, scope: button }
|
|
* });
|
|
*
|
|
* See the [Events Guide](#!/guide/events) for more.
|
|
*
|
|
* @param {String/String[]/Object} 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.
|
|
*
|
|
* This object may contain any of the following properties:
|
|
|
|
* @param {Object} [options.scope] The scope (`this` reference) in which the handler function is executed. If omitted, defaults to the object
|
|
* which fired the event.
|
|
* @param {Number} [options.delay] The number of milliseconds to delay the invocation of the handler after the event fires.
|
|
* @param {Boolean} [options.single] `true` to add a handler to handle just the next firing of the event, and then remove itself.
|
|
* @param {String} [options.order=current] The order of when the listener should be added into the listener queue.
|
|
*
|
|
* If you set an order of `before` and the event you are listening to is preventable, you can return `false` and it will stop the event.
|
|
*
|
|
* Available options are `before`, `current` and `after`.
|
|
*
|
|
* @param {Number} [options.buffer] Causes the handler to be 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.
|
|
* @param {String} [options.element] Allows you to add a listener onto a element of this component using the elements reference.
|
|
*
|
|
* Ext.create('Ext.Component', {
|
|
* listeners: {
|
|
* element: 'element',
|
|
* tap: function() {
|
|
* alert('element tap!');
|
|
* }
|
|
* }
|
|
* });
|
|
*
|
|
* All components have the `element` reference, which is the outer most element of the component. {@link Ext.Container} also has the
|
|
* `innerElement` element which contains all children. In most cases `element` is adequate.
|
|
*
|
|
* @param {String} [options.delegate] Uses {@link Ext.ComponentQuery} to delegate events to a specified query selector within this item.
|
|
*
|
|
* // Create a container with a two children; a button and a toolbar
|
|
* var container = Ext.create('Ext.Container', {
|
|
* items: [
|
|
* {
|
|
* xtype: 'toolbar',
|
|
* docked: 'top',
|
|
* title: 'My Toolbar'
|
|
* },
|
|
* {
|
|
* xtype: 'button',
|
|
* text: 'My Button'
|
|
* }
|
|
* ]
|
|
* });
|
|
*
|
|
* container.on({
|
|
* // Ext.Buttons have an xtype of 'button', so we use that are a selector for our delegate
|
|
* delegate: 'button',
|
|
*
|
|
* tap: function() {
|
|
* alert('Button tapped!');
|
|
* }
|
|
* });
|
|
*
|
|
* @param {String} [order='current'] The order of when the listener should be added into the listener queue.
|
|
* Possible values are `before`, `current` and `after`.
|
|
*/
|
|
addListener: function(eventName, fn, scope, options, order) {
|
|
return this.changeListener(this.doAddListener, eventName, fn, scope, options, order);
|
|
},
|
|
|
|
toggleListener: function(toggle, eventName, fn, scope, options, order) {
|
|
return this.changeListener(toggle ? this.doAddListener : this.doRemoveListener, eventName, fn, scope, options, order);
|
|
},
|
|
|
|
/**
|
|
* Appends a before-event handler. Returning `false` from the handler will stop the event.
|
|
*
|
|
* Same as {@link #addListener} with `order` set to `'before'`.
|
|
*
|
|
* @param {String/String[]/Object} eventName The name of the event to listen for.
|
|
* @param {Function} fn The method the event invokes.
|
|
* @param {Object} [scope] The scope for `fn`.
|
|
* @param {Object} [options] An object containing handler configuration.
|
|
*/
|
|
addBeforeListener: function(eventName, fn, scope, options) {
|
|
return this.addListener(eventName, fn, scope, options, 'before');
|
|
},
|
|
|
|
/**
|
|
* Appends an after-event handler.
|
|
*
|
|
* Same as {@link #addListener} with `order` set to `'after'`.
|
|
*
|
|
* @param {String/String[]/Object} eventName The name of the event to listen for.
|
|
* @param {Function} fn The method the event invokes.
|
|
* @param {Object} [scope] The scope for `fn`.
|
|
* @param {Object} [options] An object containing handler configuration.
|
|
*/
|
|
addAfterListener: function(eventName, fn, scope, options) {
|
|
return this.addListener(eventName, fn, scope, options, 'after');
|
|
},
|
|
|
|
/**
|
|
* Removes an event handler.
|
|
*
|
|
* @param {String/String[]/Object} 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] 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.
|
|
* @param {Object} [options] Extra options object. See {@link #addListener} for details.
|
|
* @param {String} [order='current'] The order of the listener to remove.
|
|
* Possible values are `before`, `current` and `after`.
|
|
*/
|
|
removeListener: function(eventName, fn, scope, options, order) {
|
|
return this.changeListener(this.doRemoveListener, eventName, fn, scope, options, order);
|
|
},
|
|
|
|
/**
|
|
* Removes a before-event handler.
|
|
*
|
|
* Same as {@link #removeListener} with `order` set to `'before'`.
|
|
*
|
|
* @param {String/String[]/Object} eventName The name of the event the handler was associated with.
|
|
* @param {Function} fn The handler to remove.
|
|
* @param {Object} [scope] The scope originally specified for `fn`.
|
|
* @param {Object} [options] Extra options object.
|
|
*/
|
|
removeBeforeListener: function(eventName, fn, scope, options) {
|
|
return this.removeListener(eventName, fn, scope, options, 'before');
|
|
},
|
|
|
|
/**
|
|
* Removes a before-event handler.
|
|
*
|
|
* Same as {@link #removeListener} with `order` set to `'after'`.
|
|
*
|
|
* @param {String/String[]/Object} eventName The name of the event the handler was associated with.
|
|
* @param {Function} fn The handler to remove.
|
|
* @param {Object} [scope] The scope originally specified for `fn`.
|
|
* @param {Object} [options] Extra options object.
|
|
*/
|
|
removeAfterListener: function(eventName, fn, scope, options) {
|
|
return this.removeListener(eventName, fn, scope, options, 'after');
|
|
},
|
|
|
|
/**
|
|
* Removes all listeners for this object.
|
|
*/
|
|
clearListeners: function() {
|
|
var usedSelectors = this.getUsedSelectors(),
|
|
dispatcher = this.getEventDispatcher(),
|
|
i, ln, selector;
|
|
|
|
for (i = 0,ln = usedSelectors.length; i < ln; i++) {
|
|
selector = usedSelectors[i];
|
|
|
|
dispatcher.clearListeners(this.observableType, selector);
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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) {
|
|
return this.getEventDispatcher().hasListener(this.observableType, this.getObservableId(), eventName);
|
|
},
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
suspendEvents: function(queueSuspended) {
|
|
this.eventFiringSuspended = true;
|
|
},
|
|
|
|
/**
|
|
* 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() {
|
|
this.eventFiringSuspended = false;
|
|
},
|
|
|
|
/**
|
|
* Relays selected events from the specified Observable as if the events were fired by `this`.
|
|
* @param {Object} object The Observable whose events this object is to relay.
|
|
* @param {String/Array/Object} events Array of event names to relay.
|
|
*/
|
|
relayEvents: function(object, events, prefix) {
|
|
var i, ln, oldName, newName;
|
|
|
|
if (typeof prefix == 'undefined') {
|
|
prefix = '';
|
|
}
|
|
|
|
if (typeof events == 'string') {
|
|
events = [events];
|
|
}
|
|
|
|
if (Ext.isArray(events)) {
|
|
for (i = 0,ln = events.length; i < ln; i++) {
|
|
oldName = events[i];
|
|
newName = prefix + oldName;
|
|
|
|
object.addListener(oldName, this.createEventRelayer(newName), this);
|
|
}
|
|
}
|
|
else {
|
|
for (oldName in events) {
|
|
if (events.hasOwnProperty(oldName)) {
|
|
newName = prefix + events[oldName];
|
|
|
|
object.addListener(oldName, this.createEventRelayer(newName), this);
|
|
}
|
|
}
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* @param args
|
|
* @param fn
|
|
*/
|
|
relayEvent: function(args, fn, scope, options, order) {
|
|
var fnType = typeof fn,
|
|
controller = args[args.length - 1],
|
|
eventName = controller.getInfo().eventName,
|
|
action;
|
|
|
|
args = Array.prototype.slice.call(args, 0, -2);
|
|
args[0] = this;
|
|
|
|
if (fnType != 'undefined') {
|
|
action = {
|
|
fn: fn,
|
|
scope: scope || this,
|
|
options: options || {},
|
|
order: order,
|
|
isLateBinding: fnType == 'string'
|
|
};
|
|
}
|
|
|
|
return this.doFireEvent(eventName, args, action, controller);
|
|
},
|
|
|
|
/**
|
|
* @private
|
|
* Creates an event handling function which re-fires the event from this object as the passed event name.
|
|
* @param newName
|
|
* @return {Function}
|
|
*/
|
|
createEventRelayer: function(newName){
|
|
return function() {
|
|
return this.doFireEvent(newName, Array.prototype.slice.call(arguments, 0, -2));
|
|
}
|
|
},
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* @param {String/String[]} events The event name to bubble, or an Array of event names.
|
|
*/
|
|
enableBubble: function(events) {
|
|
var isBubblingEnabled = this.isBubblingEnabled,
|
|
i, ln, name;
|
|
|
|
if (!isBubblingEnabled) {
|
|
isBubblingEnabled = this.isBubblingEnabled = {};
|
|
}
|
|
|
|
if (typeof events == 'string') {
|
|
events = Ext.Array.clone(arguments);
|
|
}
|
|
|
|
for (i = 0,ln = events.length; i < ln; i++) {
|
|
name = events[i];
|
|
|
|
if (!isBubblingEnabled[name]) {
|
|
isBubblingEnabled[name] = true;
|
|
this.addListener(name, this.createEventBubbler(name), this);
|
|
}
|
|
}
|
|
},
|
|
|
|
createEventBubbler: function(name) {
|
|
return function doBubbleEvent() {
|
|
var bubbleTarget = ('getBubbleTarget' in this) ? this.getBubbleTarget() : null;
|
|
|
|
if (bubbleTarget && bubbleTarget !== this && bubbleTarget.isObservable) {
|
|
bubbleTarget.fireAction(name, Array.prototype.slice.call(arguments, 0, -2), doBubbleEvent, bubbleTarget, null, 'after');
|
|
}
|
|
}
|
|
},
|
|
|
|
getBubbleTarget: function() {
|
|
return false;
|
|
},
|
|
|
|
destroy: function() {
|
|
if (this.observableId) {
|
|
this.fireEvent('destroy', this);
|
|
this.clearListeners();
|
|
this.clearManagedListeners();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* @ignore
|
|
*/
|
|
addEvents: Ext.emptyFn
|
|
|
|
}, function() {
|
|
this.createAlias({
|
|
/**
|
|
* @method
|
|
* Alias for {@link #addListener}.
|
|
* @inheritdoc Ext.mixin.Observable#addListener
|
|
*/
|
|
on: 'addListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #removeListener}.
|
|
* @inheritdoc Ext.mixin.Observable#removeListener
|
|
*/
|
|
un: 'removeListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #addBeforeListener}.
|
|
* @inheritdoc Ext.mixin.Observable#addBeforeListener
|
|
*/
|
|
onBefore: 'addBeforeListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #addAfterListener}.
|
|
* @inheritdoc Ext.mixin.Observable#addAfterListener
|
|
*/
|
|
onAfter: 'addAfterListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #removeBeforeListener}.
|
|
* @inheritdoc Ext.mixin.Observable#removeBeforeListener
|
|
*/
|
|
unBefore: 'removeBeforeListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #removeAfterListener}.
|
|
* @inheritdoc Ext.mixin.Observable#removeAfterListener
|
|
*/
|
|
unAfter: 'removeAfterListener'
|
|
});
|
|
|
|
//<deprecated product=touch since=2.0>
|
|
/**
|
|
* @method addEvents
|
|
* Adds the specified events to the list of events which this Observable may fire.
|
|
* @param {Object/String...} eventNames 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.
|
|
* @deprecated 2.0 It's no longer needed to add events before firing.
|
|
*/
|
|
Ext.deprecateClassMethod(this, 'addEvents', function(){}, "addEvents() is deprecated. It's no longer needed to add events before firing");
|
|
|
|
/**
|
|
* @method addManagedListener
|
|
* Adds listeners to any Observable object (or Element) which are automatically removed when this Component
|
|
* is destroyed.
|
|
* @param {Ext.mixin.Observable/HTMLElement} object The item to which to add a listener/listeners.
|
|
* @param {Object/String} eventName The event name, or an object containing event name properties.
|
|
* @param {Function} [fn] If the `eventName` parameter was an event name, this is the handler function.
|
|
* @param {Object} [scope] If the `eventName` parameter was an event name, this is the scope in which
|
|
* the handler function is executed.
|
|
* @param {Object} [options] If the `eventName` parameter was an event name, this is the
|
|
* {@link #addListener} options.
|
|
* @deprecated 2.0 All listeners are now automatically managed where necessary. Simply use {@link #addListener}.
|
|
*/
|
|
Ext.deprecateClassMethod(this, 'addManagedListener', function(object, eventName, fn, scope, options) {
|
|
return object.addListener(eventName, fn, scope, options);
|
|
}, "addManagedListener() / mon() is deprecated, simply use addListener() / on(). All listeners are now automatically managed where necessary.");
|
|
|
|
/**
|
|
* @method removeManagedListener
|
|
* Adds listeners to any Observable object (or Element) which are automatically removed when this Component
|
|
* is destroyed.
|
|
* @param {Ext.mixin.Observable/HTMLElement} object The item to which to add a listener/listeners.
|
|
* @param {Object/String} eventName The event name, or an object containing event name properties.
|
|
* @param {Function} [fn] If the `eventName` parameter was an event name, this is the handler function.
|
|
* @param {Object} [scope] If the `eventName` parameter was an event name, this is the scope in which
|
|
* the handler function is executed.
|
|
* @deprecated 2.0 All listeners are now automatically managed where necessary. Simply use {@link #removeListener}.
|
|
*/
|
|
Ext.deprecateClassMethod(this, 'removeManagedListener', function(object, eventName, fn, scope) {
|
|
return object.removeListener(eventName, fn, scope);
|
|
}, "removeManagedListener() / mun() is deprecated, simply use removeListener() / un(). All listeners are now automatically managed where necessary.");
|
|
|
|
this.createAlias({
|
|
/**
|
|
* @method
|
|
* Alias for {@link #addManagedListener}.
|
|
* @inheritdoc Ext.mixin.Observable#addManagedListener
|
|
* @deprecated 2.0.0 This is now done automatically
|
|
*/
|
|
mon: 'addManagedListener',
|
|
/**
|
|
* @method
|
|
* Alias for {@link #removeManagedListener}.
|
|
* @inheritdoc Ext.mixin.Observable#removeManagedListener
|
|
* @deprecated 2.0.0 This is now done automatically
|
|
*/
|
|
mun: 'removeManagedListener'
|
|
});
|
|
//</deprecated>
|
|
});
|