# 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 at6//7//      http://www.apache.org/licenses/LICENSE-2.08//9// Unless required by applicable law or agreed to in writing, software10// 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 and13// limitations under the License.1415/**16 * @fileoverview A light weight event system modeled after Node's EventEmitter.17 */1819goog.provide('webdriver.EventEmitter');20212223/**24 * Object that can emit events for others to listen for. This is used instead25 * of Closure's event system because it is much more light weight. The API is26 * based on Node's EventEmitters.27 * @constructor28 */29webdriver.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};373839/**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 */44webdriver.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};626364/**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 for69 *     the given event type.70 */71webdriver.EventEmitter.prototype.listeners = function(type) {72  var listeners = this.events_[type];73  if (!listeners) {74    listeners = this.events_[type] = [];75  }76  return listeners;77};787980/**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 after86 *    the first event is fired.87 * @return {!webdriver.EventEmitter} A self reference.88 * @private89 */90webdriver.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  }99100  listeners.push({101    fn: listenerFn,102    scope: opt_scope,103    oneshot: !!opt_oneshot104  });105  return this;106};107108109/**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 */116webdriver.EventEmitter.prototype.addListener = function(type, listenerFn,117    opt_scope) {118  return this.addListener_(type, listenerFn, opt_scope);119};120121122/**123 * Registers a one-time listener which will be called only the first time an124 * 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 */130webdriver.EventEmitter.prototype.once = function(type, listenerFn, opt_scope) {131  return this.addListener_(type, listenerFn, opt_scope, true);132};133134135/**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 */142webdriver.EventEmitter.prototype.on =143    webdriver.EventEmitter.prototype.addListener;144145146/**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 */152webdriver.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};165166167/**168 * Removes all listeners for a specific type of event. If no event is169 * 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 */173webdriver.EventEmitter.prototype.removeAllListeners = function(opt_type) {174  goog.isDef(opt_type) ? delete this.events_[opt_type] : this.events_ = {};175  return this;176};