Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / core / src / util / Event.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 Ext.require('Ext.util.DelayedTask', function() {
16
17     Ext.util.Event = Ext.extend(Object, (function() {
18         function createBuffered(handler, listener, o, scope) {
19             listener.task = new Ext.util.DelayedTask();
20             return function() {
21                 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
22             };
23         }
24
25         function createDelayed(handler, listener, o, scope) {
26             return function() {
27                 var task = new Ext.util.DelayedTask();
28                 if (!listener.tasks) {
29                     listener.tasks = [];
30                 }
31                 listener.tasks.push(task);
32                 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
33             };
34         }
35
36         function createSingle(handler, listener, o, scope) {
37             return function() {
38                 listener.ev.removeListener(listener.fn, scope);
39                 return handler.apply(scope, arguments);
40             };
41         }
42
43         return {
44             isEvent: true,
45
46             constructor: function(observable, name) {
47                 this.name = name;
48                 this.observable = observable;
49                 this.listeners = [];
50             },
51
52             addListener: function(fn, scope, options) {
53                 var me = this,
54                     listener;
55                     scope = scope || me.observable;
56
57                 //<debug error>
58                 if (!fn) {
59                     Ext.Error.raise({
60                         sourceClass: Ext.getClassName(this.observable),
61                         sourceMethod: "addListener",
62                         msg: "The specified callback function is undefined"
63                     });
64                 }
65                 //</debug>
66
67                 if (!me.isListening(fn, scope)) {
68                     listener = me.createListener(fn, scope, options);
69                     if (me.firing) {
70                         // if we are currently firing this event, don't disturb the listener loop
71                         me.listeners = me.listeners.slice(0);
72                     }
73                     me.listeners.push(listener);
74                 }
75             },
76
77             createListener: function(fn, scope, o) {
78                 o = o || {};
79                 scope = scope || this.observable;
80
81                 var listener = {
82                         fn: fn,
83                         scope: scope,
84                         o: o,
85                         ev: this
86                     },
87                     handler = fn;
88
89                 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
90                 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
91                 if (o.single) {
92                     handler = createSingle(handler, listener, o, scope);
93                 }
94                 if (o.delay) {
95                     handler = createDelayed(handler, listener, o, scope);
96                 }
97                 if (o.buffer) {
98                     handler = createBuffered(handler, listener, o, scope);
99                 }
100
101                 listener.fireFn = handler;
102                 return listener;
103             },
104
105             findListener: function(fn, scope) {
106                 var listeners = this.listeners,
107                 i = listeners.length,
108                 listener,
109                 s;
110
111                 while (i--) {
112                     listener = listeners[i];
113                     if (listener) {
114                         s = listener.scope;
115                         if (listener.fn == fn && (s == scope || s == this.observable)) {
116                             return i;
117                         }
118                     }
119                 }
120
121                 return - 1;
122             },
123
124             isListening: function(fn, scope) {
125                 return this.findListener(fn, scope) !== -1;
126             },
127
128             removeListener: function(fn, scope) {
129                 var me = this,
130                     index,
131                     listener,
132                     k;
133                 index = me.findListener(fn, scope);
134                 if (index != -1) {
135                     listener = me.listeners[index];
136
137                     if (me.firing) {
138                         me.listeners = me.listeners.slice(0);
139                     }
140
141                     // cancel and remove a buffered handler that hasn't fired yet
142                     if (listener.task) {
143                         listener.task.cancel();
144                         delete listener.task;
145                     }
146
147                     // cancel and remove all delayed handlers that haven't fired yet
148                     k = listener.tasks && listener.tasks.length;
149                     if (k) {
150                         while (k--) {
151                             listener.tasks[k].cancel();
152                         }
153                         delete listener.tasks;
154                     }
155
156                     // remove this listener from the listeners array
157                     Ext.Array.erase(me.listeners, index, 1);
158                     return true;
159                 }
160
161                 return false;
162             },
163
164             // Iterate to stop any buffered/delayed events
165             clearListeners: function() {
166                 var listeners = this.listeners,
167                     i = listeners.length;
168
169                 while (i--) {
170                     this.removeListener(listeners[i].fn, listeners[i].scope);
171                 }
172             },
173
174             fire: function() {
175                 var me = this,
176                     listeners = me.listeners,
177                     count = listeners.length,
178                     i,
179                     args,
180                     listener;
181
182                 if (count > 0) {
183                     me.firing = true;
184                     for (i = 0; i < count; i++) {
185                         listener = listeners[i];
186                         args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
187                         if (listener.o) {
188                             args.push(listener.o);
189                         }
190                         if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
191                             return (me.firing = false);
192                         }
193                     }
194                 }
195                 me.firing = false;
196                 return true;
197             }
198         };
199     })());
200 });
201