source: trunk/web/addons/job_monarch/lib/extjs-30/src/util/core/Observable.js @ 625

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

lib/extjs-30:

  • new ExtJS 3.0
File size: 16.2 KB
Line 
1/*!
2 * Ext JS Library 3.0.0
3 * Copyright(c) 2006-2009 Ext JS, LLC
4 * licensing@extjs.com
5 * http://www.extjs.com/license
6 */
7(function(){
8
9var EXTUTIL = Ext.util,
10    TOARRAY = Ext.toArray,
11    EACH = Ext.each,
12    ISOBJECT = Ext.isObject,
13    TRUE = true,
14    FALSE = false;
15/**
16 * @class Ext.util.Observable
17 * Base class that provides a common interface for publishing events. Subclasses are expected to
18 * to have a property "events" with all the events defined, and, optionally, a property "listeners"
19 * with configured listeners defined.<br>
20 * For example:
21 * <pre><code>
22Employee = Ext.extend(Ext.util.Observable, {
23    constructor: function(config){
24        this.name = config.name;
25        this.addEvents({
26            "fired" : true,
27            "quit" : true
28        });
29
30        // Copy configured listeners into *this* object so that the base class&#39;s
31        // constructor will add them.
32        this.listeners = config.listeners;
33
34        // Call our superclass constructor to complete construction process.
35        Employee.superclass.constructor.call(config)
36    }
37});
38</code></pre>
39 * This could then be used like this:<pre><code>
40var newEmployee = new Employee({
41    name: employeeName,
42    listeners: {
43        quit: function() {
44            // By default, "this" will be the object that fired the event.
45            alert(this.name + " has quit!");
46        }
47    }
48});
49</code></pre>
50 */
51EXTUTIL.Observable = function(){
52    /**
53     * @cfg {Object} listeners (optional) <p>A config object containing one or more event handlers to be added to this
54     * object during initialization.  This should be a valid listeners config object as specified in the
55     * {@link #addListener} example for attaching multiple handlers at once.</p>
56     * <br><p><b><u>DOM events from ExtJs {@link Ext.Component Components}</u></b></p>
57     * <br><p>While <i>some</i> ExtJs Component classes export selected DOM events (e.g. "click", "mouseover" etc), this
58     * is usually only done when extra value can be added. For example the {@link Ext.DataView DataView}'s
59     * <b><code>{@link Ext.DataView#click click}</code></b> event passing the node clicked on. To access DOM
60     * events directly from a Component's HTMLElement, listeners must be added to the <i>{@link Ext.Component#getEl Element}</i> after the Component
61     * has been rendered. A plugin can simplify this step:<pre><code>
62// Plugin is configured with a listeners config object.
63// The Component is appended to the argument list of all handler functions.
64Ext.DomObserver = Ext.extend(Object, {
65    constructor: function(config) {
66        this.listeners = config.listeners ? config.listeners : config;
67    },
68
69    // Component passes itself into plugin&#39;s init method
70    init: function(c) {
71        var p, l = this.listeners;
72        for (p in l) {
73            if (Ext.isFunction(l[p])) {
74                l[p] = this.createHandler(l[p], c);
75            } else {
76                l[p].fn = this.createHandler(l[p].fn, c);
77            }
78        }
79
80        // Add the listeners to the Element immediately following the render call
81        c.render = c.render.{@link Function#createSequence createSequence}(function() {
82            var e = c.getEl();
83            if (e) {
84                e.on(l);
85            }
86        });
87    },
88
89    createHandler: function(fn, c) {
90        return function(e) {
91            fn.call(this, e, c);
92        };
93    }
94});
95
96var combo = new Ext.form.ComboBox({
97
98    // Collapse combo when its element is clicked on
99    plugins: [ new Ext.DomObserver({
100        click: function(evt, comp) {
101            comp.collapse();
102        }
103    })],
104    store: myStore,
105    typeAhead: true,
106    mode: 'local',
107    triggerAction: 'all'
108});
109     * </code></pre></p>
110     */
111    var me = this, e = me.events;
112    if(me.listeners){
113        me.on(me.listeners);
114        delete me.listeners;
115    }
116    me.events = e || {};
117};
118
119EXTUTIL.Observable.prototype = function(){
120    var filterOptRe = /^(?:scope|delay|buffer|single)$/, toLower = function(s){
121        return s.toLowerCase();
122    };
123
124    return {
125        /**
126         * <p>Fires the specified event with the passed parameters (minus the event name).</p>
127         * <p>An event may be set to bubble up an Observable parent hierarchy (See {@link Ext.Component#getBubbleTarget})
128         * by calling {@link #enableBubble}.</p>
129         * @param {String} eventName The name of the event to fire.
130         * @param {Object...} args Variable number of parameters are passed to handlers.
131         * @return {Boolean} returns false if any of the handlers return false otherwise it returns true.
132         */
133
134        fireEvent : function(){
135            var a = TOARRAY(arguments),
136                ename = toLower(a[0]),
137                me = this,
138                ret = TRUE,
139                ce = me.events[ename],
140                q,
141                c;
142            if (me.eventsSuspended === TRUE) {
143                if (q = me.suspendedEventsQueue) {
144                    q.push(a);
145                }
146            }
147            else if(ISOBJECT(ce) && ce.bubble){
148                if(ce.fire.apply(ce, a.slice(1)) === FALSE) {
149                    return FALSE;
150                }
151                c = me.getBubbleTarget && me.getBubbleTarget();
152                if(c && c.enableBubble) {
153                    c.enableBubble(ename);
154                    return c.fireEvent.apply(c, a);
155                }
156            }
157            else {
158                if (ISOBJECT(ce)) {
159                    a.shift();
160                    ret = ce.fire.apply(ce, a);
161                }
162            }
163            return ret;
164        },
165
166        /**
167         * Appends an event handler to this object.
168         * @param {String}   eventName The name of the event to listen for.
169         * @param {Function} handler The method the event invokes.
170         * @param {Object}   scope (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
171         * <b>If omitted, defaults to the object which fired the event.</b>
172         * @param {Object}   options (optional) An object containing handler configuration.
173         * properties. This may contain any of the following properties:<ul>
174         * <li><b>scope</b> : Object<div class="sub-desc">The scope (<code><b>this</b></code> reference) in which the handler function is executed.
175         * <b>If omitted, defaults to the object which fired the event.</b></div></li>
176         * <li><b>delay</b> : Number<div class="sub-desc">The number of milliseconds to delay the invocation of the handler after the event fires.</div></li>
177         * <li><b>single</b> : Boolean<div class="sub-desc">True to add a handler to handle just the next firing of the event, and then remove itself.</div></li>
178         * <li><b>buffer</b> : Number<div class="sub-desc">Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
179         * by the specified number of milliseconds. If the event fires again within that time, the original
180         * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</div></li>
181         * <li><b>target</b> : Observable<div class="sub-desc">Only call the handler if the event was fired on the target Observable, <i>not</i>
182         * if the event was bubbled up from a child Observable.</div></li>
183         * </ul><br>
184         * <p>
185         * <b>Combining Options</b><br>
186         * Using the options argument, it is possible to combine different types of listeners:<br>
187         * <br>
188         * A delayed, one-time listener.
189         * <pre><code>
190myDataView.on('click', this.onClick, this, {
191    single: true,
192    delay: 100
193});</code></pre>
194         * <p>
195         * <b>Attaching multiple handlers in 1 call</b><br>
196         * The method also allows for a single argument to be passed which is a config object containing properties
197         * which specify multiple handlers.
198         * <p>
199         * <pre><code>
200myGridPanel.on({
201    'click' : {
202        fn: this.onClick,
203        scope: this,
204        delay: 100
205    },
206    'mouseover' : {
207        fn: this.onMouseOver,
208        scope: this
209    },
210    'mouseout' : {
211        fn: this.onMouseOut,
212        scope: this
213    }
214});</code></pre>
215     * <p>
216     * Or a shorthand syntax:<br>
217     * <pre><code>
218myGridPanel.on({
219    'click' : this.onClick,
220    'mouseover' : this.onMouseOver,
221    'mouseout' : this.onMouseOut,
222     scope: this
223});</code></pre>
224         */
225        addListener : function(eventName, fn, scope, o){
226            var me = this,
227                e,
228                oe,
229                isF,
230            ce;
231            if (ISOBJECT(eventName)) {
232                o = eventName;
233                for (e in o){
234                    oe = o[e];
235                    if (!filterOptRe.test(e)) {
236                        me.addListener(e, oe.fn || oe, oe.scope || o.scope, oe.fn ? oe : o);
237                    }
238                }
239            } else {
240                eventName = toLower(eventName);
241                ce = me.events[eventName] || TRUE;
242                if (typeof ce == "boolean") {
243                    me.events[eventName] = ce = new EXTUTIL.Event(me, eventName);
244                }
245                ce.addListener(fn, scope, ISOBJECT(o) ? o : {});
246            }
247        },
248
249        /**
250         * Removes an event handler.
251         * @param {String}   eventName The type of event the handler was associated with.
252         * @param {Function} handler   The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
253         * @param {Object}   scope     (optional) The scope originally specified for the handler.
254         */
255        removeListener : function(eventName, fn, scope){
256            var ce = this.events[toLower(eventName)];
257            if (ISOBJECT(ce)) {
258                ce.removeListener(fn, scope);
259            }
260        },
261
262        /**
263         * Removes all listeners for this object
264         */
265        purgeListeners : function(){
266            var events = this.events,
267                evt,
268                key;
269            for(key in events){
270                evt = events[key];
271                if(ISOBJECT(evt)){
272                    evt.clearListeners();
273                }
274            }
275        },
276
277        /**
278         * Used to define events on this Observable
279         * @param {Object} object The object with the events defined
280         */
281        addEvents : function(o){
282            var me = this;
283            me.events = me.events || {};
284            if (typeof o == 'string') {
285                EACH(arguments, function(a) {
286                    me.events[a] = me.events[a] || TRUE;
287                });
288            } else {
289                Ext.applyIf(me.events, o);
290            }
291        },
292
293        /**
294         * Checks to see if this object has any listeners for a specified event
295         * @param {String} eventName The name of the event to check for
296         * @return {Boolean} True if the event is being listened for, else false
297         */
298        hasListener : function(eventName){
299            var e = this.events[eventName];
300            return ISOBJECT(e) && e.listeners.length > 0;
301        },
302
303        /**
304         * Suspend the firing of all events. (see {@link #resumeEvents})
305         * @param {Boolean} queueSuspended Pass as true to queue up suspended events to be fired
306         * after the {@link #resumeEvents} call instead of discarding all suspended events;
307         */
308        suspendEvents : function(queueSuspended){
309            this.eventsSuspended = TRUE;
310            if (queueSuspended){
311                this.suspendedEventsQueue = [];
312            }
313        },
314
315        /**
316         * Resume firing events. (see {@link #suspendEvents})
317         * If events were suspended using the <tt><b>queueSuspended</b></tt> parameter, then all
318         * events fired during event suspension will be sent to any listeners now.
319         */
320        resumeEvents : function(){
321            var me = this;
322            me.eventsSuspended = !delete me.suspendedEventQueue;
323            EACH(me.suspendedEventsQueue, function(e) {
324                me.fireEvent.apply(me, e);
325            });
326        }
327    }
328}();
329
330var OBSERVABLE = EXTUTIL.Observable.prototype;
331/**
332 * Appends an event handler to this object (shorthand for {@link #addListener}.)
333 * @param {String}   eventName     The type of event to listen for
334 * @param {Function} handler       The method the event invokes
335 * @param {Object}   scope         (optional) The scope (<code><b>this</b></code> reference) in which the handler function is executed.
336 * <b>If omitted, defaults to the object which fired the event.</b>
337 * @param {Object}   options       (optional) An object containing handler configuration.
338 * @method
339 */
340OBSERVABLE.on = OBSERVABLE.addListener;
341/**
342 * Removes an event handler (shorthand for {@link #removeListener}.)
343 * @param {String}   eventName     The type of event the handler was associated with.
344 * @param {Function} handler       The handler to remove. <b>This must be a reference to the function passed into the {@link #addListener} call.</b>
345 * @param {Object}   scope         (optional) The scope originally specified for the handler.
346 * @method
347 */
348OBSERVABLE.un = OBSERVABLE.removeListener;
349
350/**
351 * Removes <b>all</b> added captures from the Observable.
352 * @param {Observable} o The Observable to release
353 * @static
354 */
355EXTUTIL.Observable.releaseCapture = function(o){
356    o.fireEvent = OBSERVABLE.fireEvent;
357};
358
359function createTargeted(h, o, scope){
360    return function(){
361        if(o.target == arguments[0]){
362            h.apply(scope, TOARRAY(arguments));
363        }
364    };
365};
366
367function createBuffered(h, o, scope){
368    var task = new EXTUTIL.DelayedTask();
369    return function(){
370        task.delay(o.buffer, h, scope, TOARRAY(arguments));
371    };
372}
373
374function createSingle(h, e, fn, scope){
375    return function(){
376        e.removeListener(fn, scope);
377        return h.apply(scope, arguments);
378    };
379}
380
381function createDelayed(h, o, scope){
382    return function(){
383        var args = TOARRAY(arguments);
384        (function(){
385            h.apply(scope, args);
386        }).defer(o.delay || 10);
387    };
388};
389
390EXTUTIL.Event = function(obj, name){
391    this.name = name;
392    this.obj = obj;
393    this.listeners = [];
394};
395
396EXTUTIL.Event.prototype = {
397    addListener : function(fn, scope, options){
398        var me = this,
399            l;
400        scope = scope || me.obj;
401        if(!me.isListening(fn, scope)){
402            l = me.createListener(fn, scope, options);
403            if(me.firing){ // if we are currently firing this event, don't disturb the listener loop
404                me.listeners = me.listeners.slice(0);
405            }
406            me.listeners.push(l);
407        }
408    },
409
410    createListener: function(fn, scope, o){
411        o = o || {}, scope = scope || this.obj;
412        var l = {
413            fn: fn,
414            scope: scope,
415            options: o
416        }, h = fn;
417        if(o.target){
418            h = createTargeted(h, o, scope);
419        }
420        if(o.delay){
421            h = createDelayed(h, o, scope);
422        }
423        if(o.single){
424            h = createSingle(h, this, fn, scope);
425        }
426        if(o.buffer){
427            h = createBuffered(h, o, scope);
428        }
429        l.fireFn = h;
430        return l;
431    },
432
433    findListener : function(fn, scope){
434        var s, ret = -1;
435        EACH(this.listeners, function(l, i) {
436            s = l.scope;
437            if(l.fn == fn && (s == scope || s == this.obj)){
438                ret = i;
439                return FALSE;
440            }
441        },
442        this);
443        return ret;
444    },
445
446    isListening : function(fn, scope){
447        return this.findListener(fn, scope) != -1;
448    },
449
450    removeListener : function(fn, scope){
451        var index,
452            me = this,
453            ret = FALSE;
454        if((index = me.findListener(fn, scope)) != -1){
455            if (me.firing) {
456                me.listeners = me.listeners.slice(0);
457            }
458            me.listeners.splice(index, 1);
459            ret = TRUE;
460        }
461        return ret;
462    },
463
464    clearListeners : function(){
465        this.listeners = [];
466    },
467
468    fire : function(){
469        var me = this,
470            args = TOARRAY(arguments),
471            ret = TRUE;
472
473        EACH(me.listeners, function(l) {
474            me.firing = TRUE;
475            if (l.fireFn.apply(l.scope || me.obj || window, args) === FALSE) {
476                return ret = me.firing = FALSE;
477            }
478        });
479        me.firing = FALSE;
480        return ret;
481    }
482};
483})();
Note: See TracBrowser for help on using the repository browser.