# lib/webdriver/events.js

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 | };