2 * Ext JS Library 2.2.1
\r
3 * Copyright(c) 2006-2009, Ext JS, LLC.
\r
4 * licensing@extjs.com
\r
6 * http://extjs.com/license
\r
10 * @class Ext.util.Observable
\r
11 * Abstract base class that provides a common interface for publishing events. Subclasses are expected to
\r
12 * to have a property "events" with all the events defined.<br>
\r
15 Employee = function(name){
\r
22 Ext.extend(Employee, Ext.util.Observable);
\r
25 Ext.util.Observable = function(){
\r
27 * @cfg {Object} listeners (optional) A config object containing one or more event handlers to be added to this
\r
28 * object during initialization. This should be a valid listeners config object as specified in the
\r
29 * {@link #addListener} example for attaching multiple handlers at once.
\r
32 this.on(this.listeners);
\r
33 delete this.listeners;
\r
36 Ext.util.Observable.prototype = {
\r
38 * Fires the specified event with the passed parameters (minus the event name).
\r
39 * @param {String} eventName
\r
40 * @param {Object...} args Variable number of parameters are passed to handlers
\r
41 * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
\r
43 fireEvent : function(){
\r
44 if(this.eventsSuspended !== true){
\r
45 var ce = this.events[arguments[0].toLowerCase()];
\r
46 if(typeof ce == "object"){
\r
47 return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
\r
54 filterOptRe : /^(?:scope|delay|buffer|single)$/,
\r
57 * Appends an event handler to this component
\r
58 * @param {String} eventName The type of event to listen for
\r
59 * @param {Function} handler The method the event invokes
\r
60 * @param {Object} scope (optional) The scope in which to execute the handler
\r
61 * function. The handler function's "this" context.
\r
62 * @param {Object} options (optional) An object containing handler configuration
\r
63 * properties. This may contain any of the following properties:<ul>
\r
64 * <li><b>scope</b> : Object<p class="sub-desc">The scope in which to execute the handler function. The handler function's "this" context.</p></li>
\r
65 * <li><b>delay</b> : Number<p class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</p></li>
\r
66 * <li><b>single</b> : Boolean<p class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</p></li>
\r
67 * <li><b>buffer</b> : Number<p class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
\r
68 * by the specified number of milliseconds. If the event fires again within that time, the original
\r
69 * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</p></li>
\r
72 * <b>Combining Options</b><br>
\r
73 * Using the options argument, it is possible to combine different types of listeners:<br>
\r
75 * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
\r
77 el.on('click', this.onClick, this, {
\r
83 * <b>Attaching multiple handlers in 1 call</b><br>
\r
84 * The method also allows for a single argument to be passed which is a config object containing properties
\r
85 * which specify multiple handlers.
\r
95 fn: this.onMouseOver,
\r
99 fn: this.onMouseOut,
\r
104 * Or a shorthand syntax:<br>
\r
107 'click' : this.onClick,
\r
108 'mouseover' : this.onMouseOver,
\r
109 'mouseout' : this.onMouseOut,
\r
113 addListener : function(eventName, fn, scope, o){
\r
114 if(typeof eventName == "object"){
\r
117 if(this.filterOptRe.test(e)){
\r
120 if(typeof o[e] == "function"){
\r
122 this.addListener(e, o[e], o.scope, o);
\r
124 // individual options
\r
125 this.addListener(e, o[e].fn, o[e].scope, o[e]);
\r
130 o = (!o || typeof o == "boolean") ? {} : o;
\r
131 eventName = eventName.toLowerCase();
\r
132 var ce = this.events[eventName] || true;
\r
133 if(typeof ce == "boolean"){
\r
134 ce = new Ext.util.Event(this, eventName);
\r
135 this.events[eventName] = ce;
\r
137 ce.addListener(fn, scope, o);
\r
141 * Removes a listener
\r
142 * @param {String} eventName The type of event to listen for
\r
143 * @param {Function} handler The handler to remove
\r
144 * @param {Object} scope (optional) The scope (this object) for the handler
\r
146 removeListener : function(eventName, fn, scope){
\r
147 var ce = this.events[eventName.toLowerCase()];
\r
148 if(typeof ce == "object"){
\r
149 ce.removeListener(fn, scope);
\r
154 * Removes all listeners for this object
\r
156 purgeListeners : function(){
\r
157 for(var evt in this.events){
\r
158 if(typeof this.events[evt] == "object"){
\r
159 this.events[evt].clearListeners();
\r
165 * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
\r
166 * @param {Object} o The Observable whose events this object is to relay.
\r
167 * @param {Array} events Array of event names to relay.
\r
169 relayEvents : function(o, events){
\r
170 var createHandler = function(ename){
\r
172 return this.fireEvent.apply(this, Ext.combine(ename, Array.prototype.slice.call(arguments, 0)));
\r
175 for(var i = 0, len = events.length; i < len; i++){
\r
176 var ename = events[i];
\r
177 if(!this.events[ename]){ this.events[ename] = true; };
\r
178 o.on(ename, createHandler(ename), this);
\r
183 * Used to define events on this Observable
\r
184 * @param {Object} object The object with the events defined
\r
186 addEvents : function(o){
\r
190 if(typeof o == 'string'){
\r
191 for(var i = 0, a = arguments, v; v = a[i]; i++){
\r
192 if(!this.events[a[i]]){
\r
193 this.events[a[i]] = true;
\r
197 Ext.applyIf(this.events, o);
\r
202 * Checks to see if this object has any listeners for a specified event
\r
203 * @param {String} eventName The name of the event to check for
\r
204 * @return {Boolean} True if the event is being listened for, else false
\r
206 hasListener : function(eventName){
\r
207 var e = this.events[eventName];
\r
208 return typeof e == "object" && e.listeners.length > 0;
\r
212 * Suspend the firing of all events. (see {@link #resumeEvents})
\r
214 suspendEvents : function(){
\r
215 this.eventsSuspended = true;
\r
219 * Resume firing events. (see {@link #suspendEvents})
\r
221 resumeEvents : function(){
\r
222 this.eventsSuspended = false;
\r
225 // these are considered experimental
\r
226 // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
\r
228 getMethodEvent : function(method){
\r
229 if(!this.methodEvents){
\r
230 this.methodEvents = {};
\r
232 var e = this.methodEvents[method];
\r
235 this.methodEvents[method] = e;
\r
237 e.originalFn = this[method];
\r
238 e.methodName = method;
\r
243 var returnValue, v, cancel;
\r
246 var makeCall = function(fn, scope, args){
\r
247 if((v = fn.apply(scope || obj, args)) !== undefined){
\r
248 if(typeof v === 'object'){
\r
249 if(v.returnValue !== undefined){
\r
250 returnValue = v.returnValue;
\r
254 if(v.cancel === true){
\r
257 }else if(v === false){
\r
265 this[method] = function(){
\r
266 returnValue = v = undefined; cancel = false;
\r
267 var args = Array.prototype.slice.call(arguments, 0);
\r
268 for(var i = 0, len = e.before.length; i < len; i++){
\r
269 makeCall(e.before[i].fn, e.before[i].scope, args);
\r
271 return returnValue;
\r
275 if((v = e.originalFn.apply(obj, args)) !== undefined){
\r
279 for(var i = 0, len = e.after.length; i < len; i++){
\r
280 makeCall(e.after[i].fn, e.after[i].scope, args);
\r
282 return returnValue;
\r
285 return returnValue;
\r
291 // adds an "interceptor" called before the original method
\r
292 beforeMethod : function(method, fn, scope){
\r
293 var e = this.getMethodEvent(method);
\r
294 e.before.push({fn: fn, scope: scope});
\r
297 // adds a "sequence" called after the original method
\r
298 afterMethod : function(method, fn, scope){
\r
299 var e = this.getMethodEvent(method);
\r
300 e.after.push({fn: fn, scope: scope});
\r
303 removeMethodListener : function(method, fn, scope){
\r
304 var e = this.getMethodEvent(method);
\r
305 for(var i = 0, len = e.before.length; i < len; i++){
\r
306 if(e.before[i].fn == fn && e.before[i].scope == scope){
\r
307 e.before.splice(i, 1);
\r
311 for(var i = 0, len = e.after.length; i < len; i++){
\r
312 if(e.after[i].fn == fn && e.after[i].scope == scope){
\r
313 e.after.splice(i, 1);
\r
320 * Appends an event handler to this element (shorthand for addListener)
\r
321 * @param {String} eventName The type of event to listen for
\r
322 * @param {Function} handler The method the event invokes
\r
323 * @param {Object} scope (optional) The scope in which to execute the handler
\r
324 * function. The handler function's "this" context.
\r
325 * @param {Object} options (optional)
\r
328 Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
\r
330 * Removes a listener (shorthand for removeListener)
\r
331 * @param {String} eventName The type of event to listen for
\r
332 * @param {Function} handler The handler to remove
\r
333 * @param {Object} scope (optional) The scope (this object) for the handler
\r
336 Ext.util.Observable.prototype.un = Ext.util.Observable.prototype.removeListener;
\r
339 * Starts capture on the specified Observable. All events will be passed
\r
340 * to the supplied function with the event name + standard signature of the event
\r
341 * <b>before</b> the event is fired. If the supplied function returns false,
\r
342 * the event will not fire.
\r
343 * @param {Observable} o The Observable to capture
\r
344 * @param {Function} fn The function to call
\r
345 * @param {Object} scope (optional) The scope (this object) for the fn
\r
348 Ext.util.Observable.capture = function(o, fn, scope){
\r
349 o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
\r
353 * Removes <b>all</b> added captures from the Observable.
\r
354 * @param {Observable} o The Observable to release
\r
357 Ext.util.Observable.releaseCapture = function(o){
\r
358 o.fireEvent = Ext.util.Observable.prototype.fireEvent;
\r
363 var createBuffered = function(h, o, scope){
\r
364 var task = new Ext.util.DelayedTask();
\r
366 task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
\r
370 var createSingle = function(h, e, fn, scope){
\r
372 e.removeListener(fn, scope);
\r
373 return h.apply(scope, arguments);
\r
377 var createDelayed = function(h, o, scope){
\r
379 var args = Array.prototype.slice.call(arguments, 0);
\r
380 setTimeout(function(){
\r
381 h.apply(scope, args);
\r
386 Ext.util.Event = function(obj, name){
\r
389 this.listeners = [];
\r
392 Ext.util.Event.prototype = {
\r
393 addListener : function(fn, scope, options){
\r
394 scope = scope || this.obj;
\r
395 if(!this.isListening(fn, scope)){
\r
396 var l = this.createListener(fn, scope, options);
\r
398 this.listeners.push(l);
\r
399 }else{ // if we are currently firing this event, don't disturb the listener loop
\r
400 this.listeners = this.listeners.slice(0);
\r
401 this.listeners.push(l);
\r
406 createListener : function(fn, scope, o){
\r
408 scope = scope || this.obj;
\r
409 var l = {fn: fn, scope: scope, options: o};
\r
412 h = createDelayed(h, o, scope);
\r
415 h = createSingle(h, this, fn, scope);
\r
418 h = createBuffered(h, o, scope);
\r
424 findListener : function(fn, scope){
\r
425 scope = scope || this.obj;
\r
426 var ls = this.listeners;
\r
427 for(var i = 0, len = ls.length; i < len; i++){
\r
429 if(l.fn == fn && l.scope == scope){
\r
436 isListening : function(fn, scope){
\r
437 return this.findListener(fn, scope) != -1;
\r
440 removeListener : function(fn, scope){
\r
442 if((index = this.findListener(fn, scope)) != -1){
\r
444 this.listeners.splice(index, 1);
\r
446 this.listeners = this.listeners.slice(0);
\r
447 this.listeners.splice(index, 1);
\r
454 clearListeners : function(){
\r
455 this.listeners = [];
\r
459 var ls = this.listeners, scope, len = ls.length;
\r
461 this.firing = true;
\r
462 var args = Array.prototype.slice.call(arguments, 0);
\r
463 for(var i = 0; i < len; i++){
\r
465 if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
\r
466 this.firing = false;
\r
470 this.firing = false;
\r