1 | // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // http://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | /** |
16 | * @fileoverview A light weight event system modeled after Node's EventEmitter. |
17 | */ |
18 | |
19 | goog.provide('webdriver.EventEmitter'); |
20 | |
21 | |
22 | |
23 | /** |
24 | * Object that can emit events for others to listen for. This is used instead |
25 | * of Closure's event system because it is much more light weight. The API is |
26 | * based on Node's EventEmitters. |
27 | * @constructor |
28 | */ |
29 | webdriver.EventEmitter = function() { |
30 | /** |
31 | * Map of events to registered listeners. |
32 | * @private {!Object.<!Array.<{fn: !Function, oneshot: boolean, |
33 | * scope: (Object|undefined)}>>} |
34 | */ |
35 | this.events_ = {}; |
36 | }; |
37 | |
38 | |
39 | /** |
40 | * Fires an event and calls all listeners. |
41 | * @param {string} type The type of event to emit. |
42 | * @param {...*} var_args Any arguments to pass to each listener. |
43 | */ |
44 | webdriver.EventEmitter.prototype.emit = function(type, var_args) { |
45 | var args = Array.prototype.slice.call(arguments, 1); |
46 | var listeners = this.events_[type]; |
47 | if (!listeners) { |
48 | return; |
49 | } |
50 | for (var i = 0; i < listeners.length;) { |
51 | var listener = listeners[i]; |
52 | listener.fn.apply(listener.scope, args); |
53 | if (listeners[i] === listener) { |
54 | if (listeners[i].oneshot) { |
55 | listeners.splice(i, 1); |
56 | } else { |
57 | i += 1; |
58 | } |
59 | } |
60 | } |
61 | }; |
62 | |
63 | |
64 | /** |
65 | * Returns a mutable list of listeners for a specific type of event. |
66 | * @param {string} type The type of event to retrieve the listeners for. |
67 | * @return {!Array.<{fn: !Function, oneshot: boolean, |
68 | * scope: (Object|undefined)}>} The registered listeners for |
69 | * the given event type. |
70 | */ |
71 | webdriver.EventEmitter.prototype.listeners = function(type) { |
72 | var listeners = this.events_[type]; |
73 | if (!listeners) { |
74 | listeners = this.events_[type] = []; |
75 | } |
76 | return listeners; |
77 | }; |
78 | |
79 | |
80 | /** |
81 | * Registers a listener. |
82 | * @param {string} type The type of event to listen for. |
83 | * @param {!Function} listenerFn The function to invoke when the event is fired. |
84 | * @param {Object=} opt_scope The object in whose scope to invoke the listener. |
85 | * @param {boolean=} opt_oneshot Whether the listener should be removed after |
86 | * the first event is fired. |
87 | * @return {!webdriver.EventEmitter} A self reference. |
88 | * @private |
89 | */ |
90 | webdriver.EventEmitter.prototype.addListener_ = function(type, listenerFn, |
91 | opt_scope, opt_oneshot) { |
92 | var listeners = this.listeners(type); |
93 | var n = listeners.length; |
94 | for (var i = 0; i < n; ++i) { |
95 | if (listeners[i].fn == listenerFn) { |
96 | return this; |
97 | } |
98 | } |
99 | |
100 | listeners.push({ |
101 | fn: listenerFn, |
102 | scope: opt_scope, |
103 | oneshot: !!opt_oneshot |
104 | }); |
105 | return this; |
106 | }; |
107 | |
108 | |
109 | /** |
110 | * Registers a listener. |
111 | * @param {string} type The type of event to listen for. |
112 | * @param {!Function} listenerFn The function to invoke when the event is fired. |
113 | * @param {Object=} opt_scope The object in whose scope to invoke the listener. |
114 | * @return {!webdriver.EventEmitter} A self reference. |
115 | */ |
116 | webdriver.EventEmitter.prototype.addListener = function(type, listenerFn, |
117 | opt_scope) { |
118 | return this.addListener_(type, listenerFn, opt_scope); |
119 | }; |
120 | |
121 | |
122 | /** |
123 | * Registers a one-time listener which will be called only the first time an |
124 | * event is emitted, after which it will be removed. |
125 | * @param {string} type The type of event to listen for. |
126 | * @param {!Function} listenerFn The function to invoke when the event is fired. |
127 | * @param {Object=} opt_scope The object in whose scope to invoke the listener. |
128 | * @return {!webdriver.EventEmitter} A self reference. |
129 | */ |
130 | webdriver.EventEmitter.prototype.once = function(type, listenerFn, opt_scope) { |
131 | return this.addListener_(type, listenerFn, opt_scope, true); |
132 | }; |
133 | |
134 | |
135 | /** |
136 | * An alias for {@code #addListener()}. |
137 | * @param {string} type The type of event to listen for. |
138 | * @param {!Function} listenerFn The function to invoke when the event is fired. |
139 | * @param {Object=} opt_scope The object in whose scope to invoke the listener. |
140 | * @return {!webdriver.EventEmitter} A self reference. |
141 | */ |
142 | webdriver.EventEmitter.prototype.on = |
143 | webdriver.EventEmitter.prototype.addListener; |
144 | |
145 | |
146 | /** |
147 | * Removes a previously registered event listener. |
148 | * @param {string} type The type of event to unregister. |
149 | * @param {!Function} listenerFn The handler function to remove. |
150 | * @return {!webdriver.EventEmitter} A self reference. |
151 | */ |
152 | webdriver.EventEmitter.prototype.removeListener = function(type, listenerFn) { |
153 | var listeners = this.events_[type]; |
154 | if (listeners) { |
155 | var n = listeners.length; |
156 | for (var i = 0; i < n; ++i) { |
157 | if (listeners[i].fn == listenerFn) { |
158 | listeners.splice(i, 1); |
159 | return this; |
160 | } |
161 | } |
162 | } |
163 | return this; |
164 | }; |
165 | |
166 | |
167 | /** |
168 | * Removes all listeners for a specific type of event. If no event is |
169 | * specified, all listeners across all types will be removed. |
170 | * @param {string=} opt_type The type of event to remove listeners from. |
171 | * @return {!webdriver.EventEmitter} A self reference. |
172 | */ |
173 | webdriver.EventEmitter.prototype.removeAllListeners = function(opt_type) { |
174 | goog.isDef(opt_type) ? delete this.events_[opt_type] : this.events_ = {}; |
175 | return this; |
176 | }; |