source: trunk/web/addons/job_monarch/lib/extjs/source/util/Observable.js @ 619

Last change on this file since 619 was 619, checked in by ramonb, 15 years ago

lib/:

  • added new AJAX dependancies: ExtJS, pChart, Lightbox2
File size: 16.2 KB
Line 
1/*
2 * Ext JS Library 2.2.1
3 * Copyright(c) 2006-2009, Ext JS, LLC.
4 * licensing@extjs.com
5 *
6 * http://extjs.com/license
7 */
8
9/**
10 * @class Ext.util.Observable
11 * Abstract base class that provides a common interface for publishing events. Subclasses are expected to
12 * to have a property "events" with all the events defined.<br>
13 * For example:
14 * <pre><code>
15 Employee = function(name){
16    this.name = name;
17    this.addEvents({
18        "fired" : true,
19        "quit" : true
20    });
21 }
22 Ext.extend(Employee, Ext.util.Observable);
23</code></pre>
24 */
25Ext.util.Observable = function(){
26    /**
27     * @cfg {Object} listeners (optional) A config object containing one or more event handlers to be added to this
28     * object during initialization.  This should be a valid listeners config object as specified in the
29     * {@link #addListener} example for attaching multiple handlers at once.
30     */
31    if(this.listeners){
32        this.on(this.listeners);
33        delete this.listeners;
34    }
35};
36Ext.util.Observable.prototype = {
37    /**
38     * Fires the specified event with the passed parameters (minus the event name).
39     * @param {String} eventName
40     * @param {Object...} args Variable number of parameters are passed to handlers
41     * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
42     */
43    fireEvent : function(){
44        if(this.eventsSuspended !== true){
45            var ce = this.events[arguments[0].toLowerCase()];
46            if(typeof ce == "object"){
47                return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
48            }
49        }
50        return true;
51    },
52
53    // private
54    filterOptRe : /^(?:scope|delay|buffer|single)$/,
55
56    /**
57     * Appends an event handler to this component
58     * @param {String}   eventName The type of event to listen for
59     * @param {Function} handler The method the event invokes
60     * @param {Object}   scope (optional) The scope in which to execute the handler
61     * function. The handler function's "this" context.
62     * @param {Object}   options (optional) An object containing handler configuration
63     * properties. This may contain any of the following properties:<ul>
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>
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>
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>
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
68     * by the specified number of milliseconds. If the event fires again within that time, the original
69     * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</p></li>
70     * </ul><br>
71     * <p>
72     * <b>Combining Options</b><br>
73     * Using the options argument, it is possible to combine different types of listeners:<br>
74     * <br>
75     * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
76     * <pre><code>
77el.on('click', this.onClick, this, {
78    single: true,
79    delay: 100,
80    forumId: 4
81});</code></pre>
82     * <p>
83     * <b>Attaching multiple handlers in 1 call</b><br>
84      * The method also allows for a single argument to be passed which is a config object containing properties
85     * which specify multiple handlers.
86     * <p>
87     * <pre><code>
88foo.on({
89    'click' : {
90        fn: this.onClick,
91        scope: this,
92        delay: 100
93    },
94    'mouseover' : {
95        fn: this.onMouseOver,
96        scope: this
97    },
98    'mouseout' : {
99        fn: this.onMouseOut,
100        scope: this
101    }
102});</code></pre>
103     * <p>
104     * Or a shorthand syntax:<br>
105     * <pre><code>
106foo.on({
107    'click' : this.onClick,
108    'mouseover' : this.onMouseOver,
109    'mouseout' : this.onMouseOut,
110     scope: this
111});</code></pre>
112     */
113    addListener : function(eventName, fn, scope, o){
114        if(typeof eventName == "object"){
115            o = eventName;
116            for(var e in o){
117                if(this.filterOptRe.test(e)){
118                    continue;
119                }
120                if(typeof o[e] == "function"){
121                    // shared options
122                    this.addListener(e, o[e], o.scope,  o);
123                }else{
124                    // individual options
125                    this.addListener(e, o[e].fn, o[e].scope, o[e]);
126                }
127            }
128            return;
129        }
130        o = (!o || typeof o == "boolean") ? {} : o;
131        eventName = eventName.toLowerCase();
132        var ce = this.events[eventName] || true;
133        if(typeof ce == "boolean"){
134            ce = new Ext.util.Event(this, eventName);
135            this.events[eventName] = ce;
136        }
137        ce.addListener(fn, scope, o);
138    },
139
140    /**
141     * Removes a listener
142     * @param {String}   eventName     The type of event to listen for
143     * @param {Function} handler        The handler to remove
144     * @param {Object}   scope  (optional) The scope (this object) for the handler
145     */
146    removeListener : function(eventName, fn, scope){
147        var ce = this.events[eventName.toLowerCase()];
148        if(typeof ce == "object"){
149            ce.removeListener(fn, scope);
150        }
151    },
152
153    /**
154     * Removes all listeners for this object
155     */
156    purgeListeners : function(){
157        for(var evt in this.events){
158            if(typeof this.events[evt] == "object"){
159                 this.events[evt].clearListeners();
160            }
161        }
162    },
163
164    /**
165     * Relays selected events from the specified Observable as if the events were fired by <tt><b>this</b></tt>.
166     * @param {Object} o The Observable whose events this object is to relay.
167     * @param {Array} events Array of event names to relay.
168     */
169    relayEvents : function(o, events){
170        var createHandler = function(ename){
171            return function(){
172                return this.fireEvent.apply(this, Ext.combine(ename, Array.prototype.slice.call(arguments, 0)));
173            };
174        };
175        for(var i = 0, len = events.length; i < len; i++){
176            var ename = events[i];
177            if(!this.events[ename]){ this.events[ename] = true; };
178            o.on(ename, createHandler(ename), this);
179        }
180    },
181
182    /**
183     * Used to define events on this Observable
184     * @param {Object} object The object with the events defined
185     */
186    addEvents : function(o){
187        if(!this.events){
188            this.events = {};
189        }
190        if(typeof o == 'string'){
191            for(var i = 0, a = arguments, v; v = a[i]; i++){
192                if(!this.events[a[i]]){
193                    this.events[a[i]] = true;
194                }
195            }
196        }else{
197            Ext.applyIf(this.events, o);
198        }
199    },
200
201    /**
202     * Checks to see if this object has any listeners for a specified event
203     * @param {String} eventName The name of the event to check for
204     * @return {Boolean} True if the event is being listened for, else false
205     */
206    hasListener : function(eventName){
207        var e = this.events[eventName];
208        return typeof e == "object" && e.listeners.length > 0;
209    },
210
211    /**
212     * Suspend the firing of all events. (see {@link #resumeEvents})
213     */
214    suspendEvents : function(){
215        this.eventsSuspended = true;
216    },
217
218    /**
219     * Resume firing events. (see {@link #suspendEvents})
220     */
221    resumeEvents : function(){
222        this.eventsSuspended = false;
223    },
224
225    // these are considered experimental
226    // allows for easier interceptor and sequences, including cancelling and overwriting the return value of the call
227    // private
228    getMethodEvent : function(method){
229        if(!this.methodEvents){
230            this.methodEvents = {};
231        }
232        var e = this.methodEvents[method];
233        if(!e){
234            e = {};
235            this.methodEvents[method] = e;
236
237            e.originalFn = this[method];
238            e.methodName = method;
239            e.before = [];
240            e.after = [];
241
242
243            var returnValue, v, cancel;
244            var obj = this;
245
246            var makeCall = function(fn, scope, args){
247                if((v = fn.apply(scope || obj, args)) !== undefined){
248                    if(typeof v === 'object'){
249                        if(v.returnValue !== undefined){
250                            returnValue = v.returnValue;
251                        }else{
252                            returnValue = v;
253                        }
254                        if(v.cancel === true){
255                            cancel = true;
256                        }
257                    }else if(v === false){
258                        cancel = true;
259                    }else {
260                        returnValue = v;
261                    }
262                }
263            }
264
265            this[method] = function(){
266                returnValue = v = undefined; cancel = false;
267                var args = Array.prototype.slice.call(arguments, 0);
268                for(var i = 0, len = e.before.length; i < len; i++){
269                    makeCall(e.before[i].fn, e.before[i].scope, args);
270                    if(cancel){
271                        return returnValue;
272                    }
273                }
274
275                if((v = e.originalFn.apply(obj, args)) !== undefined){
276                    returnValue = v;
277                }
278
279                for(var i = 0, len = e.after.length; i < len; i++){
280                    makeCall(e.after[i].fn, e.after[i].scope, args);
281                    if(cancel){
282                        return returnValue;
283                    }
284                }
285                return returnValue;
286            };
287        }
288        return e;
289    },
290
291    // adds an "interceptor" called before the original method
292    beforeMethod : function(method, fn, scope){
293        var e = this.getMethodEvent(method);
294        e.before.push({fn: fn, scope: scope});
295    },
296
297    // adds a "sequence" called after the original method
298    afterMethod : function(method, fn, scope){
299        var e = this.getMethodEvent(method);
300        e.after.push({fn: fn, scope: scope});
301    },
302
303    removeMethodListener : function(method, fn, scope){
304        var e = this.getMethodEvent(method);
305        for(var i = 0, len = e.before.length; i < len; i++){
306            if(e.before[i].fn == fn && e.before[i].scope == scope){
307                e.before.splice(i, 1);
308                return;
309            }
310        }
311        for(var i = 0, len = e.after.length; i < len; i++){
312            if(e.after[i].fn == fn && e.after[i].scope == scope){
313                e.after.splice(i, 1);
314                return;
315            }
316        }
317    }
318};
319/**
320 * Appends an event handler to this element (shorthand for addListener)
321 * @param {String}   eventName     The type of event to listen for
322 * @param {Function} handler        The method the event invokes
323 * @param {Object}   scope (optional) The scope in which to execute the handler
324 * function. The handler function's "this" context.
325 * @param {Object}   options  (optional)
326 * @method
327 */
328Ext.util.Observable.prototype.on = Ext.util.Observable.prototype.addListener;
329/**
330 * Removes a listener (shorthand for removeListener)
331 * @param {String}   eventName     The type of event to listen for
332 * @param {Function} handler        The handler to remove
333 * @param {Object}   scope  (optional) The scope (this object) for the handler
334 * @method
335 */
336Ext.util.Observable.prototype.un = Ext.util.Observable.prototype.removeListener;
337
338/**
339 * Starts capture on the specified Observable. All events will be passed
340 * to the supplied function with the event name + standard signature of the event
341 * <b>before</b> the event is fired. If the supplied function returns false,
342 * the event will not fire.
343 * @param {Observable} o The Observable to capture
344 * @param {Function} fn The function to call
345 * @param {Object} scope (optional) The scope (this object) for the fn
346 * @static
347 */
348Ext.util.Observable.capture = function(o, fn, scope){
349    o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
350};
351
352/**
353 * Removes <b>all</b> added captures from the Observable.
354 * @param {Observable} o The Observable to release
355 * @static
356 */
357Ext.util.Observable.releaseCapture = function(o){
358    o.fireEvent = Ext.util.Observable.prototype.fireEvent;
359};
360
361(function(){
362
363    var createBuffered = function(h, o, scope){
364        var task = new Ext.util.DelayedTask();
365        return function(){
366            task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
367        };
368    };
369
370    var createSingle = function(h, e, fn, scope){
371        return function(){
372            e.removeListener(fn, scope);
373            return h.apply(scope, arguments);
374        };
375    };
376
377    var createDelayed = function(h, o, scope){
378        return function(){
379            var args = Array.prototype.slice.call(arguments, 0);
380            setTimeout(function(){
381                h.apply(scope, args);
382            }, o.delay || 10);
383        };
384    };
385
386    Ext.util.Event = function(obj, name){
387        this.name = name;
388        this.obj = obj;
389        this.listeners = [];
390    };
391
392    Ext.util.Event.prototype = {
393        addListener : function(fn, scope, options){
394            scope = scope || this.obj;
395            if(!this.isListening(fn, scope)){
396                var l = this.createListener(fn, scope, options);
397                if(!this.firing){
398                    this.listeners.push(l);
399                }else{ // if we are currently firing this event, don't disturb the listener loop
400                    this.listeners = this.listeners.slice(0);
401                    this.listeners.push(l);
402                }
403            }
404        },
405
406        createListener : function(fn, scope, o){
407            o = o || {};
408            scope = scope || this.obj;
409            var l = {fn: fn, scope: scope, options: o};
410            var h = fn;
411            if(o.delay){
412                h = createDelayed(h, o, scope);
413            }
414            if(o.single){
415                h = createSingle(h, this, fn, scope);
416            }
417            if(o.buffer){
418                h = createBuffered(h, o, scope);
419            }
420            l.fireFn = h;
421            return l;
422        },
423
424        findListener : function(fn, scope){
425            scope = scope || this.obj;
426            var ls = this.listeners;
427            for(var i = 0, len = ls.length; i < len; i++){
428                var l = ls[i];
429                if(l.fn == fn && l.scope == scope){
430                    return i;
431                }
432            }
433            return -1;
434        },
435
436        isListening : function(fn, scope){
437            return this.findListener(fn, scope) != -1;
438        },
439
440        removeListener : function(fn, scope){
441            var index;
442            if((index = this.findListener(fn, scope)) != -1){
443                if(!this.firing){
444                    this.listeners.splice(index, 1);
445                }else{
446                    this.listeners = this.listeners.slice(0);
447                    this.listeners.splice(index, 1);
448                }
449                return true;
450            }
451            return false;
452        },
453
454        clearListeners : function(){
455            this.listeners = [];
456        },
457
458        fire : function(){
459            var ls = this.listeners, scope, len = ls.length;
460            if(len > 0){
461                this.firing = true;
462                var args = Array.prototype.slice.call(arguments, 0);
463                for(var i = 0; i < len; i++){
464                    var l = ls[i];
465                    if(l.fireFn.apply(l.scope||this.obj||window, arguments) === false){
466                        this.firing = false;
467                        return false;
468                    }
469                }
470                this.firing = false;
471            }
472            return true;
473        }
474    };
475})();
Note: See TracBrowser for help on using the repository browser.