lib/webdriver/events.js

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