/* * Ext JS Library 2.0 Copyright(c) 2006-2007, Ext JS, LLC. licensing@extjs.com * * http://extjs.com/license */ /** * @class Ext.util.Observable Abstract base class that provides a common * interface for publishing events. Subclasses are expected to to have a * property "events" with all the events defined.
* For example: * *

 * Employee = function(name) {
 * 	this.name = name;
 * 	this.addEvents({
 * 				"fired" : true,
 * 				"quit" : true
 * 			});
 * }
 * Ext.extend(Employee, Ext.util.Observable);
 * 
*/ Ext.util.Observable = function() { /** * @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. */ if (this.listeners) { this.on(this.listeners); delete this.listeners; } }; Ext.util.Observable.prototype = { /** * Fires the specified event with the passed parameters (minus the event * name). * * @param {String} * eventName * @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() { if (this.eventsSuspended !== true) { var ce = this.events[arguments[0].toLowerCase()]; if (typeof ce == "object") { return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1)); } } return true; }, // private filterOptRe : /^(?:scope|delay|buffer|single)$/, /** * Appends an event handler to this component * * @param {String} * eventName The type of event to listen for * @param {Function} * handler The method the event invokes * @param {Object} * scope (optional) The scope in which to execute the handler * function. The handler function's "this" context. * @param {Object} * options (optional) An object containing handler configuration * properties. This may contain any of the following properties: * *
*

* Combining Options
* Using the options argument, it is possible to combine * different types of listeners:
*
* A normalized, delayed, one-time listener that auto stops the * event and passes a custom argument (forumId) * *


	 * el.on('click', this.onClick, this, {
	 * 			single : true,
	 * 			delay : 100,
	 * 			forumId : 4
	 * 		});
	 * 
* *

* 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. *

* *


	 * foo.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:
* *


	 * foo.on({
	 * 			'click' : this.onClick,
	 * 			'mouseover' : this.onMouseOver,
	 * 			'mouseout' : this.onMouseOut,
	 * 			scope : this
	 * 		});
	 * 
*/ addListener : function(eventName, fn, scope, o) { if (typeof eventName == "object") { o = eventName; for (var e in o) { if (this.filterOptRe.test(e)) { continue; } if (typeof o[e] == "function") { // shared options this.addListener(e, o[e], o.scope, o); } else { // individual options this.addListener(e, o[e].fn, o[e].scope, o[e]); } } return; } o = (!o || typeof o == "boolean") ? {} : o; eventName = eventName.toLowerCase(); var ce = this.events[eventName] || true; if (typeof ce == "boolean") { ce = new Ext.util.Event(this, eventName); this.events[eventName] = ce; } ce.addListener(fn, scope, o); }, /** * Removes a listener * * @param {String} * eventName The type of event to listen for * @param {Function} * handler The handler to remove * @param {Object} * scope (optional) The scope (this object) for the handler */ removeListener : function(eventName, fn, scope) { var ce = this.events[eventName.toLowerCase()]; if (typeof ce == "object") { ce.removeListener(fn, scope); } }, /** * Removes all listeners for this object */ purgeListeners : function() { for (var evt in this.events) { if (typeof this.events[evt] == "object") { this.events[evt].clearListeners(); } } }, relayEvents : function(o, events) { var createHandler = function(ename) { return function() { return this.fireEvent.apply(this, Ext.combine(ename, Array.prototype.slice.call(arguments, 0))); }; }; for (var i = 0, len = events.length; i < len; i++) { var ename = events[i]; if (!this.events[ename]) { this.events[ename] = true; }; o.on(ename, createHandler(ename), this); } }, /** * Used to define events on this Observable * * @param {Object} * object The object with the events defined */ addEvents : function(o) { if (!this.events) { this.events = {}; } if (typeof o == 'string') { for (var i = 0, a = arguments, v; v = a[i]; i++) { if (!this.events[a[i]]) { o[a[i]] = true; } } } else { Ext.applyIf(this.events, o); } }, /** * 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]; return typeof e == "object" && e.listeners.length > 0; }, /** * Suspend the firing of all events. (see {@link #resumeEvents}) */ suspendEvents : function() { this.eventsSuspended = true; }, /** * Resume firing events. (see {@link #suspendEvents}) */ resumeEvents : function() { this.eventsSuspended = false; }, // these are considered experimental // allows for easier interceptor and sequences, including cancelling and // overwriting the return value of the call // private getMethodEvent : function(method) { if (!this.methodEvents) { this.methodEvents = {}; } var e = this.methodEvents[method]; if (!e) { e = {}; this.methodEvents[method] = e; e.originalFn = this[method]; e.methodName = method; e.before = []; e.after = []; var returnValue, v, cancel; var obj = this; 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; } if (v.cancel === true) { cancel = true; } } else if (v === false) { cancel = true; } else { returnValue = v; } } } this[method] = function() { returnValue = v = undefined; cancel = false; var args = Array.prototype.slice.call(arguments, 0); for (var i = 0, len = e.before.length; i < len; i++) { makeCall(e.before[i].fn, e.before[i].scope, args); if (cancel) { return returnValue; } } if ((v = e.originalFn.apply(obj, args)) !== undefined) { returnValue = v; } for (var i = 0, len = e.after.length; i < len; i++) { makeCall(e.after[i].fn, e.after[i].scope, args); if (cancel) { return returnValue; } } return returnValue; }; } return e; }, // adds an "interceptor" called before the original method beforeMethod : function(method, fn, scope) { var e = this.getMethodEvent(method); e.before.push({ fn : fn, scope : scope }); }, // adds a "sequence" called after the original method afterMethod : function(method, fn, scope) { var e = this.getMethodEvent(method); e.after.push({ fn : fn, scope : scope }); }, removeMethodListener : function(method, fn, scope) { var e = this.getMethodEvent(method); for (var i = 0, len = e.before.length; i < len; i++) { if (e.before[i].fn == fn && e.before[i].scope == scope) { e.before.splice(i, 1); return; } } for (var i = 0, len = e.after.length; i < len; i++) { if (e.after[i].fn == fn && e.after[i].scope == scope) { e.after.splice(i, 1); return; } } } }; /** * Appends an event handler to this element (shorthand for 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 in which to execute the handler * function. The handler function's "this" context. * @param {Object} * options (optional) * @method */ Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener; /** * Removes a listener (shorthand for removeListener) * * @param {String} * eventName The type of event to listen for * @param {Function} * handler The handler to remove * @param {Object} * scope (optional) The scope (this object) for the handler * @method */ Ext.util.Observable.prototype.un = Ext.util.Observable.prototype.removeListener; /** * 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 {Observable} * o The Observable to capture * @param {Function} * fn The function to call * @param {Object} * scope (optional) The scope (this object) for the fn * @static */ Ext.util.Observable.capture = function(o, fn, scope) { o.fireEvent = o.fireEvent.createInterceptor(fn, scope); }; /** * Removes all added captures from the Observable. * * @param {Observable} * o The Observable to release * @static */ Ext.util.Observable.releaseCapture = function(o) { o.fireEvent = Ext.util.Observable.prototype.fireEvent; }; (function() { var createBuffered = function(h, o, scope) { var task = new Ext.util.DelayedTask(); return function() { task.delay(o.buffer, h, scope, Array.prototype.slice.call( arguments, 0)); }; }; var createSingle = function(h, e, fn, scope) { return function() { e.removeListener(fn, scope); return h.apply(scope, arguments); }; }; var createDelayed = function(h, o, scope) { return function() { var args = Array.prototype.slice.call(arguments, 0); setTimeout(function() { h.apply(scope, args); }, o.delay || 10); }; }; Ext.util.Event = function(obj, name) { this.name = name; this.obj = obj; this.listeners = []; }; Ext.util.Event.prototype = { addListener : function(fn, scope, options) { scope = scope || this.obj; if (!this.isListening(fn, scope)) { var l = this.createListener(fn, scope, options); if (!this.firing) { this.listeners.push(l); } else { // if we are currently firing this event, don't // disturb the listener loop this.listeners = this.listeners.slice(0); this.listeners.push(l); } } }, createListener : function(fn, scope, o) { o = o || {}; scope = scope || this.obj; var l = { fn : fn, scope : scope, options : o }; var h = fn; if (o.delay) { h = createDelayed(h, o, scope); } if (o.single) { h = createSingle(h, this, fn, scope); } if (o.buffer) { h = createBuffered(h, o, scope); } l.fireFn = h; return l; }, findListener : function(fn, scope) { scope = scope || this.obj; var ls = this.listeners; for (var i = 0, len = ls.length; i < len; i++) { var l = ls[i]; if (l.fn == fn && l.scope == scope) { return i; } } return -1; }, isListening : function(fn, scope) { return this.findListener(fn, scope) != -1; }, removeListener : function(fn, scope) { var index; if ((index = this.findListener(fn, scope)) != -1) { if (!this.firing) { this.listeners.splice(index, 1); } else { this.listeners = this.listeners.slice(0); this.listeners.splice(index, 1); } return true; } return false; }, clearListeners : function() { this.listeners = []; }, fire : function() { var ls = this.listeners, scope, len = ls.length; if (len > 0) { this.firing = true; var args = Array.prototype.slice.call(arguments, 0); for (var i = 0; i < len; i++) { var l = ls[i]; if (l.fireFn .apply(l.scope || this.obj || window, arguments) === false) { this.firing = false; return false; } } this.firing = false; } return true; } }; })();