Path: blob/trunk/third_party/closure/goog/events/eventhandler.js
4063 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview Class to create objects which want to handle multiple events8* and have their listeners easily cleaned up via a dispose method.9*10* Example:11* <pre>12* function Something() {13* Something.base(this);14*15* ... set up object ...16*17* // Add event listeners18* this.listen(this.starEl, goog.events.EventType.CLICK, this.handleStar);19* this.listen(this.headerEl, goog.events.EventType.CLICK, this.expand);20* this.listen(this.collapseEl, goog.events.EventType.CLICK, this.collapse);21* this.listen(this.infoEl, goog.events.EventType.MOUSEOVER, this.showHover);22* this.listen(this.infoEl, goog.events.EventType.MOUSEOUT, this.hideHover);23* }24* goog.inherits(Something, goog.events.EventHandler);25*26* Something.prototype.disposeInternal = function() {27* Something.base(this, 'disposeInternal');28* goog.dom.removeNode(this.container);29* };30*31*32* // Then elsewhere:33*34* var activeSomething = null;35* function openSomething() {36* activeSomething = new Something();37* }38*39* function closeSomething() {40* if (activeSomething) {41* activeSomething.dispose(); // Remove event listeners42* activeSomething = null;43* }44* }45* </pre>46*/4748goog.provide('goog.events.EventHandler');4950goog.require('goog.Disposable');51goog.require('goog.events');52goog.require('goog.object');53goog.requireType('goog.events.Event');54goog.requireType('goog.events.EventId');55goog.requireType('goog.events.EventTarget');56goog.requireType('goog.events.EventWrapper');57585960/**61* Super class for objects that want to easily manage a number of event62* listeners. It allows a short cut to listen and also provides a quick way63* to remove all events listeners belonging to this object.64* @param {SCOPE=} opt_scope Object in whose scope to call the listeners.65* @constructor66* @extends {goog.Disposable}67* @template SCOPE68*/69goog.events.EventHandler = function(opt_scope) {70'use strict';71goog.Disposable.call(this);72// TODO(mknichel): Rename this to this.scope_ and fix the classes in google373// that access this private variable. :(74this.handler_ = opt_scope;7576/**77* Keys for events that are being listened to.78* @type {!Object<!goog.events.Key>}79* @private80*/81this.keys_ = {};82};83goog.inherits(goog.events.EventHandler, goog.Disposable);848586/**87* Utility array used to unify the cases of listening for an array of types88* and listening for a single event, without using recursion or allocating89* an array each time.90* @type {!Array<string>}91* @const92* @private93*/94goog.events.EventHandler.typeArray_ = [];959697/**98* Listen to an event on a Listenable. If the function is omitted then the99* EventHandler's handleEvent method will be used.100* @param {goog.events.ListenableType} src Event source.101* @param {string|Array<string>|102* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}103* type Event type to listen for or array of event types.104* @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}105* opt_fn Optional callback function to be used as the listener or an object106* with handleEvent function.107* @param {(boolean|!AddEventListenerOptions)=} opt_options108* @return {THIS} This object, allowing for chaining of calls.109* @this {THIS}110* @template EVENTOBJ, THIS111*/112goog.events.EventHandler.prototype.listen = function(113src, type, opt_fn, opt_options) {114'use strict';115var self = /** @type {!goog.events.EventHandler} */ (this);116return self.listen_(src, type, opt_fn, opt_options);117};118119120/**121* Listen to an event on a Listenable. If the function is omitted then the122* EventHandler's handleEvent method will be used.123* @param {goog.events.ListenableType} src Event source.124* @param {string|Array<string>|125* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}126* type Event type to listen for or array of event types.127* @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|128* null|undefined} fn Optional callback function to be used as the129* listener or an object with handleEvent function.130* @param {boolean|!AddEventListenerOptions|undefined} options131* @param {T} scope Object in whose scope to call the listener.132* @return {THIS} This object, allowing for chaining of calls.133* @this {THIS}134* @template T, EVENTOBJ, THIS135*/136goog.events.EventHandler.prototype.listenWithScope = function(137src, type, fn, options, scope) {138'use strict';139var self = /** @type {!goog.events.EventHandler} */ (this);140// TODO(mknichel): Deprecate this function.141return self.listen_(src, type, fn, options, scope);142};143144145/**146* Listen to an event on a Listenable. If the function is omitted then the147* EventHandler's handleEvent method will be used.148* @param {goog.events.ListenableType} src Event source.149* @param {string|Array<string>|150* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}151* type Event type to listen for or array of event types.152* @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn153* Optional callback function to be used as the listener or an object with154* handleEvent function.155* @param {(boolean|!AddEventListenerOptions)=} opt_options156* @param {Object=} opt_scope Object in whose scope to call the listener.157* @return {THIS} This object, allowing for chaining of calls.158* @this {THIS}159* @template EVENTOBJ, THIS160* @private161*/162goog.events.EventHandler.prototype.listen_ = function(163src, type, opt_fn, opt_options, opt_scope) {164'use strict';165var self = /** @type {!goog.events.EventHandler} */ (this);166if (!Array.isArray(type)) {167if (type) {168goog.events.EventHandler.typeArray_[0] = type.toString();169}170type = goog.events.EventHandler.typeArray_;171}172for (var i = 0; i < type.length; i++) {173var listenerObj = goog.events.listen(174src, type[i], opt_fn || self.handleEvent, opt_options || false,175opt_scope || self.handler_ || self);176177if (!listenerObj) {178// When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT179// (goog.events.CaptureSimulationMode) in IE8-, it will return null180// value.181return self;182}183184/** @suppress {strictMissingProperties} Added to tighten compiler checks */185var key = listenerObj.key;186self.keys_[key] = listenerObj;187}188189return self;190};191192193/**194* Listen to an event on a Listenable. If the function is omitted, then the195* EventHandler's handleEvent method will be used. After the event has fired the196* event listener is removed from the target. If an array of event types is197* provided, each event type will be listened to once.198* @param {goog.events.ListenableType} src Event source.199* @param {string|Array<string>|200* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}201* type Event type to listen for or array of event types.202* @param {function(this:SCOPE, EVENTOBJ):?|{handleEvent:function(?):?}|null=}203* opt_fn204* Optional callback function to be used as the listener or an object with205* handleEvent function.206* @param {(boolean|!AddEventListenerOptions)=} opt_options207* @return {THIS} This object, allowing for chaining of calls.208* @this {THIS}209* @template EVENTOBJ, THIS210*/211goog.events.EventHandler.prototype.listenOnce = function(212src, type, opt_fn, opt_options) {213'use strict';214var self = /** @type {!goog.events.EventHandler} */ (this);215return self.listenOnce_(src, type, opt_fn, opt_options);216};217218219/**220* Listen to an event on a Listenable. If the function is omitted, then the221* EventHandler's handleEvent method will be used. After the event has fired the222* event listener is removed from the target. If an array of event types is223* provided, each event type will be listened to once.224* @param {goog.events.ListenableType} src Event source.225* @param {string|Array<string>|226* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}227* type Event type to listen for or array of event types.228* @param {function(this:T, EVENTOBJ):?|{handleEvent:function(this:T, ?):?}|229* null|undefined} fn Optional callback function to be used as the230* listener or an object with handleEvent function.231* @param {boolean|undefined} capture Optional whether to use capture phase.232* @param {T} scope Object in whose scope to call the listener.233* @return {THIS} This object, allowing for chaining of calls.234* @this {THIS}235* @template T, EVENTOBJ, THIS236*/237goog.events.EventHandler.prototype.listenOnceWithScope = function(238src, type, fn, capture, scope) {239'use strict';240var self = /** @type {!goog.events.EventHandler} */ (this);241// TODO(mknichel): Deprecate this function.242return self.listenOnce_(src, type, fn, capture, scope);243};244245246/**247* Listen to an event on a Listenable. If the function is omitted, then the248* EventHandler's handleEvent method will be used. After the event has fired249* the event listener is removed from the target. If an array of event types is250* provided, each event type will be listened to once.251* @param {goog.events.ListenableType} src Event source.252* @param {string|Array<string>|253* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}254* type Event type to listen for or array of event types.255* @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null=} opt_fn256* Optional callback function to be used as the listener or an object with257* handleEvent function.258* @param {(boolean|!AddEventListenerOptions)=} opt_options259* @param {Object=} opt_scope Object in whose scope to call the listener.260* @return {THIS} This object, allowing for chaining of calls.261* @this {THIS}262* @template EVENTOBJ, THIS263* @private264*/265goog.events.EventHandler.prototype.listenOnce_ = function(266src, type, opt_fn, opt_options, opt_scope) {267'use strict';268var self = /** @type {!goog.events.EventHandler} */ (this);269if (Array.isArray(type)) {270for (var i = 0; i < type.length; i++) {271self.listenOnce_(src, type[i], opt_fn, opt_options, opt_scope);272}273} else {274var listenerObj = goog.events.listenOnce(275src, type, opt_fn || self.handleEvent, opt_options,276opt_scope || self.handler_ || self);277if (!listenerObj) {278// When goog.events.listen run on OFF_AND_FAIL or OFF_AND_SILENT279// (goog.events.CaptureSimulationMode) in IE8-, it will return null280// value.281return self;282}283284/** @suppress {strictMissingProperties} Added to tighten compiler checks */285var key = listenerObj.key;286self.keys_[key] = listenerObj;287}288289return self;290};291292293/**294* Adds an event listener with a specific event wrapper on a DOM Node or an295* object that has implemented {@link goog.events.EventTarget}. A listener can296* only be added once to an object.297*298* @param {EventTarget|goog.events.EventTarget} src The node to listen to299* events on.300* @param {goog.events.EventWrapper} wrapper Event wrapper to use.301* @param {function(this:SCOPE, ?):?|{handleEvent:function(?):?}|null} listener302* Callback method, or an object with a handleEvent function.303* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to304* false).305* @return {THIS} This object, allowing for chaining of calls.306* @this {THIS}307* @template THIS308*/309goog.events.EventHandler.prototype.listenWithWrapper = function(310src, wrapper, listener, opt_capt) {311'use strict';312var self = /** @type {!goog.events.EventHandler} */ (this);313// TODO(mknichel): Remove the opt_scope from this function and then314// templatize it.315return self.listenWithWrapper_(src, wrapper, listener, opt_capt);316};317318319/**320* Adds an event listener with a specific event wrapper on a DOM Node or an321* object that has implemented {@link goog.events.EventTarget}. A listener can322* only be added once to an object.323*324* @param {EventTarget|goog.events.EventTarget} src The node to listen to325* events on.326* @param {goog.events.EventWrapper} wrapper Event wrapper to use.327* @param {function(this:T, ?):?|{handleEvent:function(this:T, ?):?}|null}328* listener Optional callback function to be used as the329* listener or an object with handleEvent function.330* @param {boolean|undefined} capture Optional whether to use capture phase.331* @param {T} scope Object in whose scope to call the listener.332* @return {THIS} This object, allowing for chaining of calls.333* @this {THIS}334* @template T, THIS335*/336goog.events.EventHandler.prototype.listenWithWrapperAndScope = function(337src, wrapper, listener, capture, scope) {338'use strict';339var self = /** @type {!goog.events.EventHandler} */ (this);340// TODO(mknichel): Deprecate this function.341return self.listenWithWrapper_(src, wrapper, listener, capture, scope);342};343344345/**346* Adds an event listener with a specific event wrapper on a DOM Node or an347* object that has implemented {@link goog.events.EventTarget}. A listener can348* only be added once to an object.349*350* @param {EventTarget|goog.events.EventTarget} src The node to listen to351* events on.352* @param {goog.events.EventWrapper} wrapper Event wrapper to use.353* @param {function(?):?|{handleEvent:function(?):?}|null} listener Callback354* method, or an object with a handleEvent function.355* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to356* false).357* @param {Object=} opt_scope Element in whose scope to call the listener.358* @return {THIS} This object, allowing for chaining of calls.359* @this {THIS}360* @template THIS361* @private362*/363goog.events.EventHandler.prototype.listenWithWrapper_ = function(364src, wrapper, listener, opt_capt, opt_scope) {365'use strict';366var self = /** @type {!goog.events.EventHandler} */ (this);367wrapper.listen(368src, listener, opt_capt, opt_scope || self.handler_ || self, self);369return self;370};371372373/**374* @return {number} Number of listeners registered by this handler.375*/376goog.events.EventHandler.prototype.getListenerCount = function() {377'use strict';378var count = 0;379for (var key in this.keys_) {380if (Object.prototype.hasOwnProperty.call(this.keys_, key)) {381count++;382}383}384return count;385};386387388/**389* Unlistens on an event.390* @param {goog.events.ListenableType} src Event source.391* @param {string|Array<string>|392* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}393* type Event type or array of event types to unlisten to.394* @param {function(this:?, EVENTOBJ):?|{handleEvent:function(?):?}|null=}395* opt_fn Optional callback function to be used as the listener or an object396* with handleEvent function.397* @param {(boolean|!EventListenerOptions)=} opt_options398* @param {Object=} opt_scope Object in whose scope to call the listener.399* @return {THIS} This object, allowing for chaining of calls.400* @this {THIS}401* @template EVENTOBJ, THIS402*/403goog.events.EventHandler.prototype.unlisten = function(404src, type, opt_fn, opt_options, opt_scope) {405'use strict';406var self = /** @type {!goog.events.EventHandler} */ (this);407if (Array.isArray(type)) {408for (var i = 0; i < type.length; i++) {409self.unlisten(src, type[i], opt_fn, opt_options, opt_scope);410}411} else {412var capture =413goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;414var listener = goog.events.getListener(415src, type, opt_fn || self.handleEvent, capture,416opt_scope || self.handler_ || self);417418if (listener) {419goog.events.unlistenByKey(listener);420delete self.keys_[listener.key];421}422}423424return self;425};426427428/**429* Removes an event listener which was added with listenWithWrapper().430*431* @param {EventTarget|goog.events.EventTarget} src The target to stop432* listening to events on.433* @param {goog.events.EventWrapper} wrapper Event wrapper to use.434* @param {function(?):?|{handleEvent:function(?):?}|null} listener The435* listener function to remove.436* @param {boolean=} opt_capt In DOM-compliant browsers, this determines437* whether the listener is fired during the capture or bubble phase of the438* event.439* @param {Object=} opt_scope Element in whose scope to call the listener.440* @return {THIS} This object, allowing for chaining of calls.441* @this {THIS}442* @template THIS443*/444goog.events.EventHandler.prototype.unlistenWithWrapper = function(445src, wrapper, listener, opt_capt, opt_scope) {446'use strict';447var self = /** @type {!goog.events.EventHandler} */ (this);448wrapper.unlisten(449src, listener, opt_capt, opt_scope || self.handler_ || self, self);450return self;451};452453454/**455* Unlistens to all events.456*/457goog.events.EventHandler.prototype.removeAll = function() {458'use strict';459goog.object.forEach(this.keys_, function(listenerObj, key) {460'use strict';461if (this.keys_.hasOwnProperty(key)) {462goog.events.unlistenByKey(listenerObj);463}464}, this);465466this.keys_ = {};467};468469470/**471* Disposes of this EventHandler and removes all listeners that it registered.472* @override473* @protected474*/475goog.events.EventHandler.prototype.disposeInternal = function() {476'use strict';477goog.events.EventHandler.superClass_.disposeInternal.call(this);478this.removeAll();479};480481482/**483* Default event handler484* @param {goog.events.Event} e Event object.485*/486goog.events.EventHandler.prototype.handleEvent = function(e) {487'use strict';488throw new Error('EventHandler.handleEvent not implemented');489};490491492