Upgrade to ExtJS 4.0.0 - Released 04/26/2011
[extjs.git] / src / core / src / util / Event.js
1 Ext.require('Ext.util.DelayedTask', function() {
2
3     Ext.util.Event = Ext.extend(Object, (function() {
4         function createBuffered(handler, listener, o, scope) {
5             listener.task = new Ext.util.DelayedTask();
6             return function() {
7                 listener.task.delay(o.buffer, handler, scope, Ext.Array.toArray(arguments));
8             };
9         }
10
11         function createDelayed(handler, listener, o, scope) {
12             return function() {
13                 var task = new Ext.util.DelayedTask();
14                 if (!listener.tasks) {
15                     listener.tasks = [];
16                 }
17                 listener.tasks.push(task);
18                 task.delay(o.delay || 10, handler, scope, Ext.Array.toArray(arguments));
19             };
20         }
21
22         function createSingle(handler, listener, o, scope) {
23             return function() {
24                 listener.ev.removeListener(listener.fn, scope);
25                 return handler.apply(scope, arguments);
26             };
27         }
28
29         return {
30             isEvent: true,
31
32             constructor: function(observable, name) {
33                 this.name = name;
34                 this.observable = observable;
35                 this.listeners = [];
36             },
37
38             addListener: function(fn, scope, options) {
39                 var me = this,
40                     listener;
41                     scope = scope || me.observable;
42
43                 //<debug error>
44                 if (!fn) {
45                     Ext.Error.raise({
46                         sourceClass: Ext.getClassName(this.observable),
47                         sourceMethod: "addListener",
48                         msg: "The specified callback function is undefined"
49                     });
50                 }
51                 //</debug>
52
53                 if (!me.isListening(fn, scope)) {
54                     listener = me.createListener(fn, scope, options);
55                     if (me.firing) {
56                         // if we are currently firing this event, don't disturb the listener loop
57                         me.listeners = me.listeners.slice(0);
58                     }
59                     me.listeners.push(listener);
60                 }
61             },
62
63             createListener: function(fn, scope, o) {
64                 o = o || {};
65                 scope = scope || this.observable;
66
67                 var listener = {
68                         fn: fn,
69                         scope: scope,
70                         o: o,
71                         ev: this
72                     },
73                     handler = fn;
74
75                 // The order is important. The 'single' wrapper must be wrapped by the 'buffer' and 'delayed' wrapper
76                 // because the event removal that the single listener does destroys the listener's DelayedTask(s)
77                 if (o.single) {
78                     handler = createSingle(handler, listener, o, scope);
79                 }
80                 if (o.delay) {
81                     handler = createDelayed(handler, listener, o, scope);
82                 }
83                 if (o.buffer) {
84                     handler = createBuffered(handler, listener, o, scope);
85                 }
86
87                 listener.fireFn = handler;
88                 return listener;
89             },
90
91             findListener: function(fn, scope) {
92                 var listeners = this.listeners,
93                 i = listeners.length,
94                 listener,
95                 s;
96
97                 while (i--) {
98                     listener = listeners[i];
99                     if (listener) {
100                         s = listener.scope;
101                         if (listener.fn == fn && (s == scope || s == this.observable)) {
102                             return i;
103                         }
104                     }
105                 }
106
107                 return - 1;
108             },
109
110             isListening: function(fn, scope) {
111                 return this.findListener(fn, scope) !== -1;
112             },
113
114             removeListener: function(fn, scope) {
115                 var me = this,
116                     index,
117                     listener,
118                     k;
119                 index = me.findListener(fn, scope);
120                 if (index != -1) {
121                     listener = me.listeners[index];
122
123                     if (me.firing) {
124                         me.listeners = me.listeners.slice(0);
125                     }
126
127                     // cancel and remove a buffered handler that hasn't fired yet
128                     if (listener.task) {
129                         listener.task.cancel();
130                         delete listener.task;
131                     }
132
133                     // cancel and remove all delayed handlers that haven't fired yet
134                     k = listener.tasks && listener.tasks.length;
135                     if (k) {
136                         while (k--) {
137                             listener.tasks[k].cancel();
138                         }
139                         delete listener.tasks;
140                     }
141
142                     // remove this listener from the listeners array
143                     me.listeners.splice(index, 1);
144                     return true;
145                 }
146
147                 return false;
148             },
149
150             // Iterate to stop any buffered/delayed events
151             clearListeners: function() {
152                 var listeners = this.listeners,
153                     i = listeners.length;
154
155                 while (i--) {
156                     this.removeListener(listeners[i].fn, listeners[i].scope);
157                 }
158             },
159
160             fire: function() {
161                 var me = this,
162                     listeners = me.listeners,
163                     count = listeners.length,
164                     i,
165                     args,
166                     listener;
167
168                 if (count > 0) {
169                     me.firing = true;
170                     for (i = 0; i < count; i++) {
171                         listener = listeners[i];
172                         args = arguments.length ? Array.prototype.slice.call(arguments, 0) : [];
173                         if (listener.o) {
174                             args.push(listener.o);
175                         }
176                         if (listener && listener.fireFn.apply(listener.scope || me.observable, args) === false) {
177                             return (me.firing = false);
178                         }
179                     }
180                 }
181                 me.firing = false;
182                 return true;
183             }
184         };
185     })());
186 });