Path: blob/trunk/third_party/closure/goog/events/events.js
4217 views
/**1* @license2* Copyright The Closure Library Authors.3* SPDX-License-Identifier: Apache-2.04*/56/**7* @fileoverview An event manager for both native browser event8* targets and custom JavaScript event targets9* (`goog.events.Listenable`). This provides an abstraction10* over browsers' event systems.11*12* It also provides a simulation of W3C event model's capture phase in13* Internet Explorer (IE 8 and below). Caveat: the simulation does not14* interact well with listeners registered directly on the elements15* (bypassing goog.events) or even with listeners registered via16* goog.events in a separate JS binary. In these cases, we provide17* no ordering guarantees.18*19* The listeners will receive a "patched" event object. Such event object20* contains normalized values for certain event properties that differs in21* different browsers.22*23* Example usage:24* <pre>25* goog.events.listen(myNode, 'click', function(e) { alert('woo') });26* goog.events.listen(myNode, 'mouseover', mouseHandler, true);27* goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);28* goog.events.removeAll(myNode);29* </pre>30*31* in IE and event object patching]32*33* @see ../demos/events.html34* @see ../demos/event-propagation.html35* @see ../demos/stopevent.html36*/3738// IMPLEMENTATION NOTES:39// goog.events stores an auxiliary data structure on each EventTarget40// source being listened on. This allows us to take advantage of GC,41// having the data structure GC'd when the EventTarget is GC'd. This42// GC behavior is equivalent to using W3C DOM Events directly.4344goog.provide('goog.events');45goog.provide('goog.events.CaptureSimulationMode');46goog.provide('goog.events.Key');47goog.provide('goog.events.ListenableType');4849goog.require('goog.asserts');50goog.require('goog.debug.entryPointRegistry');51goog.require('goog.events.BrowserEvent');52goog.require('goog.events.BrowserFeature');53goog.require('goog.events.Listenable');54goog.require('goog.events.ListenerMap');55goog.requireType('goog.debug.ErrorHandler');56goog.requireType('goog.events.EventId');57goog.requireType('goog.events.EventLike');58goog.requireType('goog.events.EventWrapper');59goog.requireType('goog.events.ListenableKey');60goog.requireType('goog.events.Listener');616263/**64* @typedef {number|goog.events.ListenableKey}65*/66goog.events.Key;676869/**70* @typedef {EventTarget|goog.events.Listenable}71*/72goog.events.ListenableType;737475/**76* Property name on a native event target for the listener map77* associated with the event target.78* @private @const {string}79*/80goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);818283/**84* String used to prepend to IE event types.85* @const86* @private87*/88goog.events.onString_ = 'on';899091/**92* Map of computed "on<eventname>" strings for IE event types. Caching93* this removes an extra object allocation in goog.events.listen which94* improves IE6 performance.95* @const96* @dict97* @private98*/99goog.events.onStringMap_ = {};100101102/**103* @enum {number} Different capture simulation mode for IE8-.104*/105goog.events.CaptureSimulationMode = {106/**107* Does not perform capture simulation. Will asserts in IE8- when you108* add capture listeners.109*/110OFF_AND_FAIL: 0,111112/**113* Does not perform capture simulation, silently ignore capture114* listeners.115*/116OFF_AND_SILENT: 1,117118/**119* Performs capture simulation.120*/121ON: 2122};123124125/**126* @define {number} The capture simulation mode for IE8-. By default,127* this is ON.128*/129goog.events.CAPTURE_SIMULATION_MODE =130goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);131132133/**134* Estimated count of total native listeners.135* @private {number}136*/137goog.events.listenerCountEstimate_ = 0;138139140/**141* Adds an event listener for a specific event on a native event142* target (such as a DOM element) or an object that has implemented143* {@link goog.events.Listenable}. A listener can only be added once144* to an object and if it is added again the key for the listener is145* returned. Note that if the existing listener is a one-off listener146* (registered via listenOnce), it will no longer be a one-off147* listener after a call to listen().148*149* @param {EventTarget|goog.events.Listenable} src The node to listen150* to events on.151* @param {string|Array<string>|152* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}153* type Event type or array of event types.154* @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}155* listener Callback method, or an object with a handleEvent function.156* WARNING: passing an Object is now softly deprecated.157* @param {(boolean|!AddEventListenerOptions)=} opt_options158* @param {T=} opt_handler Element in whose scope to call the listener.159* @return {goog.events.Key} Unique key for the listener.160* @template T,EVENTOBJ161* @suppress {strictMissingProperties} Added to tighten compiler checks162*/163goog.events.listen = function(src, type, listener, opt_options, opt_handler) {164'use strict';165if (opt_options && opt_options.once) {166return goog.events.listenOnce(167src, type, listener, opt_options, opt_handler);168}169if (Array.isArray(type)) {170for (var i = 0; i < type.length; i++) {171goog.events.listen(src, type[i], listener, opt_options, opt_handler);172}173return null;174}175176listener = goog.events.wrapListener(listener);177if (goog.events.Listenable.isImplementedBy(src)) {178var capture =179goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;180return src.listen(181/** @type {string|!goog.events.EventId} */ (type), listener, capture,182opt_handler);183} else {184return goog.events.listen_(185/** @type {!EventTarget} */ (src), type, listener,186/* callOnce */ false, opt_options, opt_handler);187}188};189190191/**192* Adds an event listener for a specific event on a native event193* target. A listener can only be added once to an object and if it194* is added again the key for the listener is returned.195*196* Note that a one-off listener will not change an existing listener,197* if any. On the other hand a normal listener will change existing198* one-off listener to become a normal listener.199*200* @param {EventTarget} src The node to listen to events on.201* @param {string|?goog.events.EventId<EVENTOBJ>} type Event type.202* @param {!Function} listener Callback function.203* @param {boolean} callOnce Whether the listener is a one-off204* listener or otherwise.205* @param {(boolean|!AddEventListenerOptions)=} opt_options206* @param {Object=} opt_handler Element in whose scope to call the listener.207* @return {goog.events.ListenableKey} Unique key for the listener.208* @template EVENTOBJ209* @private210* @suppress {strictMissingProperties} Added to tighten compiler checks211*/212goog.events.listen_ = function(213src, type, listener, callOnce, opt_options, opt_handler) {214'use strict';215if (!type) {216throw new Error('Invalid event type');217}218219var capture =220goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;221222var listenerMap = goog.events.getListenerMap_(src);223if (!listenerMap) {224src[goog.events.LISTENER_MAP_PROP_] = listenerMap =225new goog.events.ListenerMap(src);226}227228var listenerObj = /** @type {goog.events.Listener} */ (229listenerMap.add(type, listener, callOnce, capture, opt_handler));230231// If the listenerObj already has a proxy, it has been set up232// previously. We simply return.233if (listenerObj.proxy) {234return listenerObj;235}236237var proxy = goog.events.getProxy();238listenerObj.proxy = proxy;239240/** @suppress {strictMissingProperties} Added to tighten compiler checks */241proxy.src = src;242/** @suppress {strictMissingProperties} Added to tighten compiler checks */243proxy.listener = listenerObj;244245// Attach the proxy through the browser's API246if (src.addEventListener) {247// Don't pass an object as `capture` if the browser doesn't support that.248if (!goog.events.BrowserFeature.PASSIVE_EVENTS) {249opt_options = capture;250}251// Don't break tests that expect a boolean.252if (opt_options === undefined) opt_options = false;253src.addEventListener(type.toString(), proxy, opt_options);254} else if (src.attachEvent) {255// The else if above used to be an unconditional else. It would call256// attachEvent come gws or high water. This would sometimes throw an257// exception on IE11, spoiling the day of some callers. The previous258// incarnation of this code, from 2007, indicates that it replaced an259// earlier still version that caused excess allocations on IE6.260src.attachEvent(goog.events.getOnString_(type.toString()), proxy);261} else if (src.addListener && src.removeListener) {262// In IE, MediaQueryList uses addListener() insteadd of addEventListener. In263// Safari, there is no global for the MediaQueryList constructor, so we just264// check whether the object "looks like" MediaQueryList.265goog.asserts.assert(266type === 'change', 'MediaQueryList only has a change event');267src.addListener(proxy);268} else {269throw new Error('addEventListener and attachEvent are unavailable.');270}271272goog.events.listenerCountEstimate_++;273return listenerObj;274};275276277/**278* Helper function for returning a proxy function.279* @return {!Function} A new or reused function object.280*/281goog.events.getProxy = function() {282'use strict';283const proxyCallbackFunction = goog.events.handleBrowserEvent_;284/** @suppress {strictMissingProperties} Added to tighten compiler checks */285const f = function(eventObject) {286return proxyCallbackFunction.call(f.src, f.listener, eventObject);287};288return f;289};290291292/**293* Adds an event listener for a specific event on a native event294* target (such as a DOM element) or an object that has implemented295* {@link goog.events.Listenable}. After the event has fired the event296* listener is removed from the target.297*298* If an existing listener already exists, listenOnce will do299* nothing. In particular, if the listener was previously registered300* via listen(), listenOnce() will not turn the listener into a301* one-off listener. Similarly, if there is already an existing302* one-off listener, listenOnce does not modify the listeners (it is303* still a once listener).304*305* @param {EventTarget|goog.events.Listenable} src The node to listen306* to events on.307* @param {string|Array<string>|308* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}309* type Event type or array of event types.310* @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}311* listener Callback method.312* @param {(boolean|!AddEventListenerOptions)=} opt_options313* @param {T=} opt_handler Element in whose scope to call the listener.314* @return {goog.events.Key} Unique key for the listener.315* @template T,EVENTOBJ316* @suppress {strictMissingProperties} Added to tighten compiler checks317*/318goog.events.listenOnce = function(319src, type, listener, opt_options, opt_handler) {320'use strict';321if (Array.isArray(type)) {322for (var i = 0; i < type.length; i++) {323goog.events.listenOnce(src, type[i], listener, opt_options, opt_handler);324}325return null;326}327328listener = goog.events.wrapListener(listener);329if (goog.events.Listenable.isImplementedBy(src)) {330var capture =331goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;332return src.listenOnce(333/** @type {string|!goog.events.EventId} */ (type), listener, capture,334opt_handler);335} else {336return goog.events.listen_(337/** @type {!EventTarget} */ (src), type, listener,338/* callOnce */ true, opt_options, opt_handler);339}340};341342343/**344* Adds an event listener with a specific event wrapper on a DOM Node or an345* object that has implemented {@link goog.events.Listenable}. A listener can346* only be added once to an object.347*348* @param {EventTarget|goog.events.Listenable} src The target to349* listen to events on.350* @param {goog.events.EventWrapper} wrapper Event wrapper to use.351* @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener352* Callback method, or an object with a handleEvent function.353* @param {boolean=} opt_capt Whether to fire in capture phase (defaults to354* false).355* @param {T=} opt_handler Element in whose scope to call the listener.356* @template T357*/358goog.events.listenWithWrapper = function(359src, wrapper, listener, opt_capt, opt_handler) {360'use strict';361wrapper.listen(src, listener, opt_capt, opt_handler);362};363364365/**366* Removes an event listener which was added with listen().367*368* @param {EventTarget|goog.events.Listenable} src The target to stop369* listening to events on.370* @param {string|Array<string>|371* !goog.events.EventId<EVENTOBJ>|!Array<!goog.events.EventId<EVENTOBJ>>}372* type Event type or array of event types to unlisten to.373* @param {function(?):?|{handleEvent:function(?):?}|null} listener The374* listener function to remove.375* @param {(boolean|!EventListenerOptions)=} opt_options376* whether the listener is fired during the capture or bubble phase of the377* event.378* @param {Object=} opt_handler Element in whose scope to call the listener.379* @return {?boolean} indicating whether the listener was there to remove.380* @template EVENTOBJ381* @suppress {strictMissingProperties} Added to tighten compiler checks382*/383goog.events.unlisten = function(src, type, listener, opt_options, opt_handler) {384'use strict';385if (Array.isArray(type)) {386for (var i = 0; i < type.length; i++) {387goog.events.unlisten(src, type[i], listener, opt_options, opt_handler);388}389return null;390}391var capture =392goog.isObject(opt_options) ? !!opt_options.capture : !!opt_options;393394listener = goog.events.wrapListener(listener);395if (goog.events.Listenable.isImplementedBy(src)) {396return src.unlisten(397/** @type {string|!goog.events.EventId} */ (type), listener, capture,398opt_handler);399}400401if (!src) {402// TODO(chrishenry): We should tighten the API to only accept403// non-null objects, or add an assertion here.404return false;405}406407var listenerMap = goog.events.getListenerMap_(408/** @type {!EventTarget} */ (src));409if (listenerMap) {410var listenerObj = listenerMap.getListener(411/** @type {string|!goog.events.EventId} */ (type), listener, capture,412opt_handler);413if (listenerObj) {414return goog.events.unlistenByKey(listenerObj);415}416}417418return false;419};420421422/**423* Removes an event listener which was added with listen() by the key424* returned by listen().425*426* @param {goog.events.Key} key The key returned by listen() for this427* event listener.428* @return {boolean} indicating whether the listener was there to remove.429* @suppress {strictMissingProperties} Added to tighten compiler checks430*/431goog.events.unlistenByKey = function(key) {432'use strict';433// TODO(chrishenry): Remove this check when tests that rely on this434// are fixed.435if (typeof key === 'number') {436return false;437}438439var listener = key;440if (!listener || listener.removed) {441return false;442}443444var src = listener.src;445if (goog.events.Listenable.isImplementedBy(src)) {446return /** @type {!goog.events.Listenable} */ (src).unlistenByKey(listener);447}448449var type = listener.type;450/** @suppress {strictMissingProperties} Added to tighten compiler checks */451var proxy = listener.proxy;452if (src.removeEventListener) {453src.removeEventListener(type, proxy, listener.capture);454} else if (src.detachEvent) {455src.detachEvent(goog.events.getOnString_(type), proxy);456} else if (src.addListener && src.removeListener) {457src.removeListener(proxy);458}459goog.events.listenerCountEstimate_--;460461var listenerMap = goog.events.getListenerMap_(462/** @type {!EventTarget} */ (src));463// TODO(chrishenry): Try to remove this conditional and execute the464// first branch always. This should be safe.465if (listenerMap) {466listenerMap.removeByKey(listener);467if (listenerMap.getTypeCount() == 0) {468// Null the src, just because this is simple to do (and useful469// for IE <= 7).470listenerMap.src = null;471// We don't use delete here because IE does not allow delete472// on a window object.473src[goog.events.LISTENER_MAP_PROP_] = null;474}475} else {476/** @type {!goog.events.Listener} */ (listener).markAsRemoved();477}478479return true;480};481482483/**484* Removes an event listener which was added with listenWithWrapper().485*486* @param {EventTarget|goog.events.Listenable} src The target to stop487* listening to events on.488* @param {goog.events.EventWrapper} wrapper Event wrapper to use.489* @param {function(?):?|{handleEvent:function(?):?}|null} listener The490* listener function to remove.491* @param {boolean=} opt_capt In DOM-compliant browsers, this determines492* whether the listener is fired during the capture or bubble phase of the493* event.494* @param {Object=} opt_handler Element in whose scope to call the listener.495*/496goog.events.unlistenWithWrapper = function(497src, wrapper, listener, opt_capt, opt_handler) {498'use strict';499wrapper.unlisten(src, listener, opt_capt, opt_handler);500};501502503/**504* Removes all listeners from an object. You can also optionally505* remove listeners of a particular type.506*507* @param {Object|undefined} obj Object to remove listeners from. Must be an508* EventTarget or a goog.events.Listenable.509* @param {string|!goog.events.EventId=} opt_type Type of event to remove.510* Default is all types.511* @return {number} Number of listeners removed.512*/513goog.events.removeAll = function(obj, opt_type) {514'use strict';515// TODO(chrishenry): Change the type of obj to516// (!EventTarget|!goog.events.Listenable).517518if (!obj) {519return 0;520}521522if (goog.events.Listenable.isImplementedBy(obj)) {523return /** @type {?} */ (obj).removeAllListeners(opt_type);524}525526var listenerMap = goog.events.getListenerMap_(527/** @type {!EventTarget} */ (obj));528if (!listenerMap) {529return 0;530}531532var count = 0;533var typeStr = opt_type && opt_type.toString();534for (var type in listenerMap.listeners) {535if (!typeStr || type == typeStr) {536// Clone so that we don't need to worry about unlistenByKey537// changing the content of the ListenerMap.538var listeners = listenerMap.listeners[type].concat();539for (var i = 0; i < listeners.length; ++i) {540if (goog.events.unlistenByKey(listeners[i])) {541++count;542}543}544}545}546return count;547};548549550/**551* Gets the listeners for a given object, type and capture phase.552*553* @param {Object} obj Object to get listeners for.554* @param {string|!goog.events.EventId} type Event type.555* @param {boolean} capture Capture phase?.556* @return {!Array<!goog.events.Listener>} Array of listener objects.557*/558goog.events.getListeners = function(obj, type, capture) {559'use strict';560if (goog.events.Listenable.isImplementedBy(obj)) {561return /** @type {!goog.events.Listenable} */ (obj).getListeners(562type, capture);563} else {564if (!obj) {565// TODO(chrishenry): We should tighten the API to accept566// !EventTarget|goog.events.Listenable, and add an assertion here.567return [];568}569570var listenerMap = goog.events.getListenerMap_(571/** @type {!EventTarget} */ (obj));572return listenerMap ? listenerMap.getListeners(type, capture) : [];573}574};575576577/**578* Gets the goog.events.Listener for the event or null if no such listener is579* in use.580*581* @param {EventTarget|goog.events.Listenable} src The target from582* which to get listeners.583* @param {?string|!goog.events.EventId<EVENTOBJ>} type The type of the event.584* @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The585* listener function to get.586* @param {boolean=} opt_capt In DOM-compliant browsers, this determines587* whether the listener is fired during the588* capture or bubble phase of the event.589* @param {Object=} opt_handler Element in whose scope to call the listener.590* @return {goog.events.ListenableKey} the found listener or null if not found.591* @template EVENTOBJ592* @suppress {strictMissingProperties} Added to tighten compiler checks593*/594goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {595'use strict';596// TODO(chrishenry): Change type from ?string to string, or add assertion.597type = /** @type {string} */ (type);598listener = goog.events.wrapListener(listener);599var capture = !!opt_capt;600if (goog.events.Listenable.isImplementedBy(src)) {601return src.getListener(type, listener, capture, opt_handler);602}603604if (!src) {605// TODO(chrishenry): We should tighten the API to only accept606// non-null objects, or add an assertion here.607return null;608}609610var listenerMap = goog.events.getListenerMap_(611/** @type {!EventTarget} */ (src));612if (listenerMap) {613return listenerMap.getListener(type, listener, capture, opt_handler);614}615return null;616};617618619/**620* Returns whether an event target has any active listeners matching the621* specified signature. If either the type or capture parameters are622* unspecified, the function will match on the remaining criteria.623*624* @param {EventTarget|goog.events.Listenable} obj Target to get625* listeners for.626* @param {string|!goog.events.EventId=} opt_type Event type.627* @param {boolean=} opt_capture Whether to check for capture or bubble-phase628* listeners.629* @return {boolean} Whether an event target has one or more listeners matching630* the requested type and/or capture phase.631* @suppress {strictMissingProperties} Added to tighten compiler checks632*/633goog.events.hasListener = function(obj, opt_type, opt_capture) {634'use strict';635if (goog.events.Listenable.isImplementedBy(obj)) {636return obj.hasListener(opt_type, opt_capture);637}638639var listenerMap = goog.events.getListenerMap_(640/** @type {!EventTarget} */ (obj));641return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);642};643644645/**646* Provides a nice string showing the normalized event objects public members647* @param {Object} e Event Object.648* @return {string} String of the public members of the normalized event object.649*/650goog.events.expose = function(e) {651'use strict';652var str = [];653for (var key in e) {654if (e[key] && e[key].id) {655str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');656} else {657str.push(key + ' = ' + e[key]);658}659}660return str.join('\n');661};662663664/**665* Returns a string with on prepended to the specified type. This is used for IE666* which expects "on" to be prepended. This function caches the string in order667* to avoid extra allocations in steady state.668* @param {string} type Event type.669* @return {string} The type string with 'on' prepended.670* @private671*/672goog.events.getOnString_ = function(type) {673'use strict';674if (type in goog.events.onStringMap_) {675return goog.events.onStringMap_[type];676}677return goog.events.onStringMap_[type] = goog.events.onString_ + type;678};679680681/**682* Fires an object's listeners of a particular type and phase683*684* @param {Object} obj Object whose listeners to call.685* @param {string|!goog.events.EventId} type Event type.686* @param {boolean} capture Which event phase.687* @param {Object} eventObject Event object to be passed to listener.688* @return {boolean} True if all listeners returned true else false.689*/690goog.events.fireListeners = function(obj, type, capture, eventObject) {691'use strict';692if (goog.events.Listenable.isImplementedBy(obj)) {693return /** @type {!goog.events.Listenable} */ (obj).fireListeners(694type, capture, eventObject);695}696697return goog.events.fireListeners_(obj, type, capture, eventObject);698};699700701/**702* Fires an object's listeners of a particular type and phase.703* @param {Object} obj Object whose listeners to call.704* @param {string|!goog.events.EventId} type Event type.705* @param {boolean} capture Which event phase.706* @param {Object} eventObject Event object to be passed to listener.707* @return {boolean} True if all listeners returned true else false.708* @private709*/710goog.events.fireListeners_ = function(obj, type, capture, eventObject) {711'use strict';712/** @type {boolean} */713var retval = true;714715var listenerMap = goog.events.getListenerMap_(716/** @type {EventTarget} */ (obj));717if (listenerMap) {718// TODO(chrishenry): Original code avoids array creation when there719// is no listener, so we do the same. If this optimization turns720// out to be not required, we can replace this with721// listenerMap.getListeners(type, capture) instead, which is simpler.722var listenerArray = listenerMap.listeners[type.toString()];723if (listenerArray) {724listenerArray = listenerArray.concat();725for (var i = 0; i < listenerArray.length; i++) {726var listener = listenerArray[i];727// We might not have a listener if the listener was removed.728if (listener && listener.capture == capture && !listener.removed) {729var result = goog.events.fireListener(listener, eventObject);730retval = retval && (result !== false);731}732}733}734}735return retval;736};737738739/**740* Fires a listener with a set of arguments741*742* @param {goog.events.Listener} listener The listener object to call.743* @param {Object} eventObject The event object to pass to the listener.744* @return {*} Result of listener.745* @suppress {strictMissingProperties} Added to tighten compiler checks746*/747goog.events.fireListener = function(listener, eventObject) {748'use strict';749var listenerFn = listener.listener;750var listenerHandler = listener.handler || listener.src;751752if (listener.callOnce) {753goog.events.unlistenByKey(listener);754}755return listenerFn.call(listenerHandler, eventObject);756};757758759/**760* Gets the total number of listeners currently in the system.761* @return {number} Number of listeners.762* @deprecated This returns estimated count, now that Closure no longer763* stores a central listener registry. We still return an estimation764* to keep existing listener-related tests passing. In the near future,765* this function will be removed.766*/767goog.events.getTotalListenerCount = function() {768'use strict';769return goog.events.listenerCountEstimate_;770};771772773/**774* Dispatches an event (or event like object) and calls all listeners775* listening for events of this type. The type of the event is decided by the776* type property on the event object.777*778* If any of the listeners returns false OR calls preventDefault then this779* function will return false. If one of the capture listeners calls780* stopPropagation, then the bubble listeners won't fire.781*782* @param {goog.events.Listenable} src The event target.783* @param {goog.events.EventLike} e Event object.784* @return {boolean} If anyone called preventDefault on the event object (or785* if any of the handlers returns false) this will also return false.786* If there are no handlers, or if all handlers return true, this returns787* true.788*/789goog.events.dispatchEvent = function(src, e) {790'use strict';791goog.asserts.assert(792goog.events.Listenable.isImplementedBy(src),793'Can not use goog.events.dispatchEvent with ' +794'non-goog.events.Listenable instance.');795return src.dispatchEvent(e);796};797798799/**800* Installs exception protection for the browser event entry point using the801* given error handler.802*803* @param {goog.debug.ErrorHandler} errorHandler Error handler with which to804* protect the entry point.805*/806goog.events.protectBrowserEventEntryPoint = function(errorHandler) {807'use strict';808goog.events.handleBrowserEvent_ =809errorHandler.protectEntryPoint(goog.events.handleBrowserEvent_);810};811812813/**814* Handles an event and dispatches it to the correct listeners. This815* function is a proxy for the real listener the user specified.816*817* @param {goog.events.Listener} listener The listener object.818* @param {Event=} opt_evt Optional event object that gets passed in via the819* native event handlers.820* @return {*} Result of the event handler.821* @this {EventTarget} The object or Element that fired the event.822* @private823*/824goog.events.handleBrowserEvent_ = function(listener, opt_evt) {825'use strict';826if (listener.removed) {827return true;828}829830// Otherwise, simply fire the listener.831return goog.events.fireListener(832listener, new goog.events.BrowserEvent(opt_evt, this));833};834835836/**837* This is used to mark the IE event object so we do not do the Closure pass838* twice for a bubbling event.839* @param {Event} e The IE browser event.840* @private841*/842goog.events.markIeEvent_ = function(e) {843'use strict';844// Only the keyCode and the returnValue can be changed. We use keyCode for845// non keyboard events.846// event.returnValue is a bit more tricky. It is undefined by default. A847// boolean false prevents the default action. In a window.onbeforeunload and848// the returnValue is non undefined it will be alerted. However, we will only849// modify the returnValue for keyboard events. We can get a problem if non850// closure events sets the keyCode or the returnValue851852var useReturnValue = false;853854if (e.keyCode == 0) {855// We cannot change the keyCode in case that srcElement is input[type=file].856// We could test that that is the case but that would allocate 3 objects.857// If we use try/catch we will only allocate extra objects in the case of a858// failure.859860try {861e.keyCode = -1;862return;863} catch (ex) {864useReturnValue = true;865}866}867868if (useReturnValue ||869/** @type {boolean|undefined} */ (e.returnValue) == undefined) {870e.returnValue = true;871}872};873874875/**876* This is used to check if an IE event has already been handled by the Closure877* system so we do not do the Closure pass twice for a bubbling event.878* @param {Event} e The IE browser event.879* @return {boolean} True if the event object has been marked.880* @private881*/882goog.events.isMarkedIeEvent_ = function(e) {883'use strict';884return e.keyCode < 0 || e.returnValue != undefined;885};886887888/**889* Counter to create unique event ids.890* @private {number}891*/892goog.events.uniqueIdCounter_ = 0;893894895/**896* Creates a unique event id.897*898* @param {string} identifier The identifier.899* @return {string} A unique identifier.900* @idGenerator {unique}901*/902goog.events.getUniqueId = function(identifier) {903'use strict';904return identifier + '_' + goog.events.uniqueIdCounter_++;905};906907908/**909* @param {EventTarget} src The source object.910* @return {goog.events.ListenerMap} A listener map for the given911* source object, or null if none exists.912* @private913*/914goog.events.getListenerMap_ = function(src) {915'use strict';916var listenerMap = src[goog.events.LISTENER_MAP_PROP_];917// IE serializes the property as well (e.g. when serializing outer918// HTML). So we must check that the value is of the correct type.919return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;920};921922923/**924* Expando property for listener function wrapper for Object with925* handleEvent.926* @private @const {string}927*/928goog.events.LISTENER_WRAPPER_PROP_ =929'__closure_events_fn_' + ((Math.random() * 1e9) >>> 0);930931932/**933* @param {Object|Function} listener The listener function or an934* object that contains handleEvent method.935* @return {!Function} Either the original function or a function that936* calls obj.handleEvent. If the same listener is passed to this937* function more than once, the same function is guaranteed to be938* returned.939* @suppress {strictMissingProperties} Added to tighten compiler checks940*/941goog.events.wrapListener = function(listener) {942'use strict';943goog.asserts.assert(listener, 'Listener can not be null.');944945if (typeof listener === 'function') {946return listener;947}948949goog.asserts.assert(950listener.handleEvent, 'An object listener must have handleEvent method.');951if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {952listener[goog.events.LISTENER_WRAPPER_PROP_] = function(e) {953'use strict';954return /** @type {?} */ (listener).handleEvent(e);955};956}957return listener[goog.events.LISTENER_WRAPPER_PROP_];958};959960961// Register the browser event handler as an entry point, so that962// it can be monitored for exception handling, etc.963goog.debug.entryPointRegistry.register(964/**965* @param {function(!Function): !Function} transformer The transforming966* function.967*/968function(transformer) {969'use strict';970goog.events.handleBrowserEvent_ =971transformer(goog.events.handleBrowserEvent_);972});973974975