// Copyright 2005 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. /** * @fileoverview Class to create objects which want to handle multiple events * and have their listeners easily cleaned up via a dispose method. * * Example: *
 * function Something() {
 *   goog.base(this);
 *
 *   ... set up object ...
 *
 *   // Add event listeners
 *   this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);
 *   this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);
 *   this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);
 *   this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);
 *   this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);
 * }
 * goog.inherits(Something, goog.events.EventHandler);
 *
 * Something.prototype.disposeInternal = function() {
 *   goog.base(this, 'disposeInternal');
 *   goog.dom.removeNode(this.container);
 * };
 *
 *
 * // Then elsewhere:
 *
 * var activeSomething = null;
 * function openSomething() {
 *   activeSomething = new Something();
 * }
 *
 * function closeSomething() {
 *   if (activeSomething) {
 *     activeSomething.dispose();  // Remove event listeners
 *     activeSomething = null;
 *   }
 * }
 * 
* */ goog.provide('goog.events.EventHandler'); goog.require('goog.Disposable'); goog.require('goog.array'); goog.require('goog.events'); goog.require('goog.events.EventWrapper'); /** * Super class for objects that want to easily manage a number of event * listeners. It allows a short cut to listen and also provides a quick way * to remove all events listeners belonging to this object. * @param {Object=} opt_handler Object in whose scope to call the listeners. * @constructor * @extends {goog.Disposable} */ goog.events.EventHandler = function(opt_handler) { goog.Disposable.call(this); this.handler_ = opt_handler; /** * Keys for events that are being listened to. * @type {Array.} * @private */ this.keys_ = []; }; goog.inherits(goog.events.EventHandler, goog.Disposable); /** * Utility array used to unify the cases of listening for an array of types * and listening for a single event, without using recursion or allocating * an array each time. * @type {Array.} * @private */ goog.events.EventHandler.typeArray_ = []; /** * Listen to an event on a DOM node or EventTarget. If the function is omitted * then the EventHandler's handleEvent method will be used. * @param {goog.events.EventTarget|EventTarget} src Event source. * @param {string|Array.} type Event type to listen for or array of * event types. * @param {Function|Object=} opt_fn Optional callback function to be used as the * listener or an object with handleEvent function. * @param {boolean=} opt_capture Optional whether to use capture phase. * @param {Object=} opt_handler Object in whose scope to call the listener. * @return {goog.events.EventHandler} This object, allowing for chaining of * calls. */ goog.events.EventHandler.prototype.listen = function(src, type, opt_fn, opt_capture, opt_handler) { if (!goog.isArray(type)) { goog.events.EventHandler.typeArray_[0] = /** @type {string} */(type); type = goog.events.EventHandler.typeArray_; } for (var i = 0; i < type.length; i++) { // goog.events.listen generates unique keys so we don't have to check their // presence in the this.keys_ array. var key = (/** @type {number} */ goog.events.listen(src, type[i], opt_fn || this, opt_capture || false, opt_handler || this.handler_ || this)); this.keys_.push(key); } return this; }; /** * Listen to an event on a DOM node or EventTarget. If the function is omitted * then the EventHandler's handleEvent method will be used. After the event has * fired the event listener is removed from the target. If an array of event * types is provided, each event type will be listened to once. * @param {goog.events.EventTarget|EventTarget} src Event source. * @param {string|Array.} type Event type to listen for or array of * event types. * @param {Function|Object=} opt_fn Optional callback function to be used as the * listener or an object with handleEvent function. * @param {boolean=} opt_capture Optional whether to use capture phase. * @param {Object=} opt_handler Object in whose scope to call the listener. * @return {goog.events.EventHandler} This object, allowing for chaining of * calls. */ goog.events.EventHandler.prototype.listenOnce = function(src, type, opt_fn, opt_capture, opt_handler) { if (goog.isArray(type)) { for (var i = 0; i < type.length; i++) { this.listenOnce(src, type[i], opt_fn, opt_capture, opt_handler); } } else { var key = (/** @type {number} */ goog.events.listenOnce(src, type, opt_fn || this, opt_capture, opt_handler || this.handler_ || this)); this.keys_.push(key); } return this; }; /** * Adds an event listener with a specific event wrapper on a DOM Node or an * object that has implemented {@link goog.events.EventTarget}. A listener can * only be added once to an object. * * @param {EventTarget|goog.events.EventTarget} src The node to listen to * events on. * @param {goog.events.EventWrapper} wrapper Event wrapper to use. * @param {Function|Object} listener Callback method, or an object with a * handleEvent function. * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to * false). * @param {Object=} opt_handler Element in whose scope to call the listener. * @return {goog.events.EventHandler} This object, allowing for chaining of * calls. */ goog.events.EventHandler.prototype.listenWithWrapper = function(src, wrapper, listener, opt_capt, opt_handler) { wrapper.listen(src, listener, opt_capt, opt_handler || this.handler_ || this, this); return this; }; /** * @return {number} Number of listeners registered by this handler. */ goog.events.EventHandler.prototype.getListenerCount = function() { return this.keys_.length; }; /** * Unlistens on an event. * @param {goog.events.EventTarget|EventTarget} src Event source. * @param {string|Array.} type Event type to listen for. * @param {Function|Object=} opt_fn Optional callback function to be used as the * listener or an object with handleEvent function. * @param {boolean=} opt_capture Optional whether to use capture phase. * @param {Object=} opt_handler Object in whose scope to call the listener. * @return {goog.events.EventHandler} This object, allowing for chaining of * calls. */ goog.events.EventHandler.prototype.unlisten = function(src, type, opt_fn, opt_capture, opt_handler) { if (goog.isArray(type)) { for (var i = 0; i < type.length; i++) { this.unlisten(src, type[i], opt_fn, opt_capture, opt_handler); } } else { var listener = goog.events.getListener(src, type, opt_fn || this, opt_capture, opt_handler || this.handler_ || this); if (listener) { var key = listener.key; goog.events.unlistenByKey(key); goog.array.remove(this.keys_, key); } } return this; }; /** * Removes an event listener which was added with listenWithWrapper(). * * @param {EventTarget|goog.events.EventTarget} src The target to stop * listening to events on. * @param {goog.events.EventWrapper} wrapper Event wrapper to use. * @param {Function|Object} listener The listener function to remove. * @param {boolean=} opt_capt In DOM-compliant browsers, this determines * whether the listener is fired during the capture or bubble phase of the * event. * @param {Object=} opt_handler Element in whose scope to call the listener. * @return {goog.events.EventHandler} This object, allowing for chaining of * calls. */ goog.events.EventHandler.prototype.unlistenWithWrapper = function(src, wrapper, listener, opt_capt, opt_handler) { wrapper.unlisten(src, listener, opt_capt, opt_handler || this.handler_ || this, this); return this; }; /** * Unlistens to all events. */ goog.events.EventHandler.prototype.removeAll = function() { goog.array.forEach(this.keys_, goog.events.unlistenByKey); this.keys_.length = 0; }; /** * Disposes of this EventHandler and removes all listeners that it registered. * @override * @protected */ goog.events.EventHandler.prototype.disposeInternal = function() { goog.events.EventHandler.superClass_.disposeInternal.call(this); this.removeAll(); }; /** * Default event handler * @param {goog.events.Event} e Event object. */ goog.events.EventHandler.prototype.handleEvent = function(e) { throw Error('EventHandler.handleEvent not implemented'); };