Mercurial > eldonilo > lightstring
comparison lib/EventEmitter.js @ 105:fb50311997b5
Add EventEmitter.js lib
author | Sonny Piers <sonny.piers@gmail.com> |
---|---|
date | Wed, 13 Jun 2012 12:48:06 +0200 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
104:b04667a0d2d4 | 105:fb50311997b5 |
---|---|
1 /** | |
2 * EventEmitter v3.1.5 | |
3 * https://github.com/Wolfy87/EventEmitter | |
4 * | |
5 * Oliver Caldwell (http://oli.me.uk) | |
6 * Creative Commons Attribution 3.0 Unported License (http://creativecommons.org/licenses/by/3.0/) | |
7 */ | |
8 | |
9 ;(function(exports) { | |
10 // JSHint config | |
11 /*jshint smarttabs:true,devel:true*/ | |
12 /*global define:true*/ | |
13 | |
14 // Place the script into strict mode | |
15 'use strict'; | |
16 | |
17 /** | |
18 * EventEmitter class | |
19 * Creates an object with event registering and firing methods | |
20 */ | |
21 function EventEmitter() { | |
22 // Initialise required storage variables | |
23 this._events = {}; | |
24 this._maxListeners = 10; | |
25 } | |
26 | |
27 /** | |
28 * Event class | |
29 * Contains Event methods and property storage | |
30 * | |
31 * @param {String} type Event type name | |
32 * @param {Function} listener Function to be called when the event is fired | |
33 * @param {Object} scope Object that this should be set to when the listener is called | |
34 * @param {Boolean} once If true then the listener will be removed after the first call | |
35 * @param {Object} instance The parent EventEmitter instance | |
36 */ | |
37 function Event(type, listener, scope, once, instance) { | |
38 // Store arguments | |
39 this.type = type; | |
40 this.listener = listener; | |
41 this.scope = scope; | |
42 this.once = once; | |
43 this.instance = instance; | |
44 } | |
45 | |
46 /** | |
47 * Executes the listener | |
48 * | |
49 * @param {Array} args List of arguments to pass to the listener | |
50 * @return {Boolean} If false then it was a once event | |
51 */ | |
52 Event.prototype.fire = function(args) { | |
53 this.listener.apply(this.scope || this.instance, args); | |
54 | |
55 // Remove the listener if this is a once only listener | |
56 if(this.once) { | |
57 this.instance.removeListener(this.type, this.listener, this.scope); | |
58 return false; | |
59 } | |
60 }; | |
61 | |
62 /** | |
63 * Passes every listener for a specified event to a function one at a time | |
64 * | |
65 * @param {String} type Event type name | |
66 * @param {Function} callback Function to pass each listener to | |
67 * @return {Object} The current EventEmitter instance to allow chaining | |
68 */ | |
69 EventEmitter.prototype.eachListener = function(type, callback) { | |
70 // Initialise variables | |
71 var i = null, | |
72 possibleListeners = null, | |
73 result = null; | |
74 | |
75 // Only loop if the type exists | |
76 if(this._events.hasOwnProperty(type)) { | |
77 possibleListeners = this._events[type]; | |
78 | |
79 for(i = 0; i < possibleListeners.length; i += 1) { | |
80 result = callback.call(this, possibleListeners[i], i); | |
81 | |
82 if(result === false) { | |
83 i -= 1; | |
84 } | |
85 else if(result === true) { | |
86 break; | |
87 } | |
88 } | |
89 } | |
90 | |
91 // Return the instance to allow chaining | |
92 return this; | |
93 }; | |
94 | |
95 /** | |
96 * Adds an event listener for the specified event | |
97 * | |
98 * @param {String} type Event type name | |
99 * @param {Function} listener Function to be called when the event is fired | |
100 * @param {Object} scope Object that this should be set to when the listener is called | |
101 * @param {Boolean} once If true then the listener will be removed after the first call | |
102 * @return {Object} The current EventEmitter instance to allow chaining | |
103 */ | |
104 EventEmitter.prototype.addListener = function(type, listener, scope, once) { | |
105 // Create the listener array if it does not exist yet | |
106 if(!this._events.hasOwnProperty(type)) { | |
107 this._events[type] = []; | |
108 } | |
109 | |
110 // Push the new event to the array | |
111 this._events[type].push(new Event(type, listener, scope, once, this)); | |
112 | |
113 // Emit the new listener event | |
114 this.emit('newListener', type, listener, scope, once); | |
115 | |
116 // Check if we have exceeded the maxListener count | |
117 // Ignore this check if the count is 0 | |
118 // Also don't check if we have already fired a warning | |
119 if(this._maxListeners && !this._events[type].warned && this._events[type].length > this._maxListeners) { | |
120 // The max listener count has been exceeded! | |
121 // Warn via the console if it exists | |
122 if(typeof console !== 'undefined') { | |
123 console.warn('Possible EventEmitter memory leak detected. ' + this._events[type].length + ' listeners added. Use emitter.setMaxListeners() to increase limit.'); | |
124 } | |
125 | |
126 // Set the flag so it doesn't fire again | |
127 this._events[type].warned = true; | |
128 } | |
129 | |
130 // Return the instance to allow chaining | |
131 return this; | |
132 }; | |
133 | |
134 /** | |
135 * Alias of the addListener method | |
136 * | |
137 * @param {String} type Event type name | |
138 * @param {Function} listener Function to be called when the event is fired | |
139 * @param {Object} scope Object that this should be set to when the listener is called | |
140 * @param {Boolean} once If true then the listener will be removed after the first call | |
141 */ | |
142 EventEmitter.prototype.on = EventEmitter.prototype.addListener; | |
143 | |
144 /** | |
145 * Alias of the addListener method but will remove the event after the first use | |
146 * | |
147 * @param {String} type Event type name | |
148 * @param {Function} listener Function to be called when the event is fired | |
149 * @param {Object} scope Object that this should be set to when the listener is called | |
150 * @return {Object} The current EventEmitter instance to allow chaining | |
151 */ | |
152 EventEmitter.prototype.once = function(type, listener, scope) { | |
153 return this.addListener(type, listener, scope, true); | |
154 }; | |
155 | |
156 /** | |
157 * Removes the a listener for the specified event | |
158 * | |
159 * @param {String} type Event type name the listener must have for the event to be removed | |
160 * @param {Function} listener Listener the event must have to be removed | |
161 * @param {Object} scope The scope the event must have to be removed | |
162 * @return {Object} The current EventEmitter instance to allow chaining | |
163 */ | |
164 EventEmitter.prototype.removeListener = function(type, listener, scope) { | |
165 this.eachListener(type, function(currentListener, index) { | |
166 // If this is the listener remove it from the array | |
167 // We also compare the scope if it was passed | |
168 if(currentListener.listener === listener && (!scope || currentListener.scope === scope)) { | |
169 this._events[type].splice(index, 1); | |
170 } | |
171 }); | |
172 | |
173 // Remove the property if there are no more listeners | |
174 if(this._events[type] && this._events[type].length === 0) { | |
175 delete this._events[type]; | |
176 } | |
177 | |
178 // Return the instance to allow chaining | |
179 return this; | |
180 }; | |
181 | |
182 /** | |
183 * Alias of the removeListener method | |
184 * | |
185 * @param {String} type Event type name the listener must have for the event to be removed | |
186 * @param {Function} listener Listener the event must have to be removed | |
187 * @param {Object} scope The scope the event must have to be removed | |
188 * @return {Object} The current EventEmitter instance to allow chaining | |
189 */ | |
190 EventEmitter.prototype.off = EventEmitter.prototype.removeListener; | |
191 | |
192 /** | |
193 * Removes all listeners for a specified event | |
194 * If no event type is passed it will remove every listener | |
195 * | |
196 * @param {String} type Event type name to remove all listeners from | |
197 * @return {Object} The current EventEmitter instance to allow chaining | |
198 */ | |
199 EventEmitter.prototype.removeAllListeners = function(type) { | |
200 // Check for a type, if there is none remove all listeners | |
201 // If there is a type however, just remove the listeners for that type | |
202 if(type && this._events.hasOwnProperty(type)) { | |
203 delete this._events[type]; | |
204 } | |
205 else if(!type) { | |
206 this._events = {}; | |
207 } | |
208 | |
209 // Return the instance to allow chaining | |
210 return this; | |
211 }; | |
212 | |
213 /** | |
214 * Retrieves the array of listeners for a specified event | |
215 * | |
216 * @param {String} type Event type name to return all listeners from | |
217 * @return {Array} Will return either an array of listeners or an empty array if there are none | |
218 */ | |
219 EventEmitter.prototype.listeners = function(type) { | |
220 // Return the array of listeners or an empty array if it does not exist | |
221 if(this._events.hasOwnProperty(type)) { | |
222 // It does exist, loop over building the array | |
223 var listeners = []; | |
224 | |
225 this.eachListener(type, function(evt) { | |
226 listeners.push(evt.listener); | |
227 }); | |
228 | |
229 return listeners; | |
230 } | |
231 | |
232 return []; | |
233 }; | |
234 | |
235 /** | |
236 * Emits an event executing all appropriate listeners | |
237 * All values passed after the type will be passed as arguments to the listeners | |
238 * | |
239 * @param {String} type Event type name to run all listeners from | |
240 * @return {Object} The current EventEmitter instance to allow chaining | |
241 */ | |
242 EventEmitter.prototype.emit = function(type) { | |
243 // Calculate the arguments | |
244 var args = [], | |
245 i = null; | |
246 | |
247 for(i = 1; i < arguments.length; i += 1) { | |
248 args.push(arguments[i]); | |
249 } | |
250 | |
251 this.eachListener(type, function(currentListener) { | |
252 return currentListener.fire(args); | |
253 }); | |
254 | |
255 // Return the instance to allow chaining | |
256 return this; | |
257 }; | |
258 | |
259 /** | |
260 * Sets the max listener count for the EventEmitter | |
261 * When the count of listeners for an event exceeds this limit a warning will be printed | |
262 * Set to 0 for no limit | |
263 * | |
264 * @param {Number} maxListeners The new max listener limit | |
265 * @return {Object} The current EventEmitter instance to allow chaining | |
266 */ | |
267 EventEmitter.prototype.setMaxListeners = function(maxListeners) { | |
268 this._maxListeners = maxListeners; | |
269 | |
270 // Return the instance to allow chaining | |
271 return this; | |
272 }; | |
273 | |
274 /** | |
275 * Builds a clone of the prototype object for you to extend with | |
276 * | |
277 * @return {Object} A clone of the EventEmitter prototype object | |
278 */ | |
279 EventEmitter.extend = function() { | |
280 // First thing we need to do is create our new prototype | |
281 // Then we loop over the current one copying each method out | |
282 // When done, simply return the clone | |
283 var clone = {}, | |
284 current = this.prototype, | |
285 key = null; | |
286 | |
287 for(key in current) { | |
288 // Make sure this is actually a property of the object before copying it | |
289 // We don't want any default object methods leaking though | |
290 if(current.hasOwnProperty(key)) { | |
291 clone[key] = current[key]; | |
292 } | |
293 } | |
294 | |
295 // All done, return the clone | |
296 return clone; | |
297 }; | |
298 | |
299 // Export the class | |
300 // If AMD is available then use it | |
301 if(typeof define === 'function' && define.amd) { | |
302 define(function() { | |
303 return EventEmitter; | |
304 }); | |
305 } | |
306 | |
307 // No matter what it will be added to the global object | |
308 exports.EventEmitter = EventEmitter; | |
309 }(this)); |