| Index: third_party/google_input_tools/third_party/closure_library/closure/goog/events/events.js
|
| diff --git a/third_party/google_input_tools/third_party/closure_library/closure/goog/events/events.js b/third_party/google_input_tools/third_party/closure_library/closure/goog/events/events.js
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..e39d3bd78b772e89ded2c99f71526b5154129950
|
| --- /dev/null
|
| +++ b/third_party/google_input_tools/third_party/closure_library/closure/goog/events/events.js
|
| @@ -0,0 +1,995 @@
|
| +// 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 An event manager for both native browser event
|
| + * targets and custom JavaScript event targets
|
| + * ({@code goog.events.Listenable}). This provides an abstraction
|
| + * over browsers' event systems.
|
| + *
|
| + * It also provides a simulation of W3C event model's capture phase in
|
| + * Internet Explorer (IE 8 and below). Caveat: the simulation does not
|
| + * interact well with listeners registered directly on the elements
|
| + * (bypassing goog.events) or even with listeners registered via
|
| + * goog.events in a separate JS binary. In these cases, we provide
|
| + * no ordering guarantees.
|
| + *
|
| + * The listeners will receive a "patched" event object. Such event object
|
| + * contains normalized values for certain event properties that differs in
|
| + * different browsers.
|
| + *
|
| + * Example usage:
|
| + * <pre>
|
| + * goog.events.listen(myNode, 'click', function(e) { alert('woo') });
|
| + * goog.events.listen(myNode, 'mouseover', mouseHandler, true);
|
| + * goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
|
| + * goog.events.removeAll(myNode);
|
| + * </pre>
|
| + *
|
| + * in IE and event object patching]
|
| + *
|
| + * @see ../demos/events.html
|
| + * @see ../demos/event-propagation.html
|
| + * @see ../demos/stopevent.html
|
| + */
|
| +
|
| +// IMPLEMENTATION NOTES:
|
| +// goog.events stores an auxiliary data structure on each EventTarget
|
| +// source being listened on. This allows us to take advantage of GC,
|
| +// having the data structure GC'd when the EventTarget is GC'd. This
|
| +// GC behavior is equivalent to using W3C DOM Events directly.
|
| +
|
| +goog.provide('goog.events');
|
| +goog.provide('goog.events.CaptureSimulationMode');
|
| +goog.provide('goog.events.Key');
|
| +goog.provide('goog.events.ListenableType');
|
| +
|
| +goog.require('goog.asserts');
|
| +goog.require('goog.debug.entryPointRegistry');
|
| +goog.require('goog.events.BrowserEvent');
|
| +goog.require('goog.events.BrowserFeature');
|
| +goog.require('goog.events.Listenable');
|
| +goog.require('goog.events.ListenerMap');
|
| +
|
| +goog.forwardDeclare('goog.debug.ErrorHandler');
|
| +goog.forwardDeclare('goog.events.EventWrapper');
|
| +
|
| +
|
| +/**
|
| + * @typedef {number|goog.events.ListenableKey}
|
| + */
|
| +goog.events.Key;
|
| +
|
| +
|
| +/**
|
| + * @typedef {EventTarget|goog.events.Listenable}
|
| + */
|
| +goog.events.ListenableType;
|
| +
|
| +
|
| +/**
|
| + * Property name on a native event target for the listener map
|
| + * associated with the event target.
|
| + * @const
|
| + * @private
|
| + */
|
| +goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
|
| +
|
| +
|
| +/**
|
| + * String used to prepend to IE event types.
|
| + * @const
|
| + * @private
|
| + */
|
| +goog.events.onString_ = 'on';
|
| +
|
| +
|
| +/**
|
| + * Map of computed "on<eventname>" strings for IE event types. Caching
|
| + * this removes an extra object allocation in goog.events.listen which
|
| + * improves IE6 performance.
|
| + * @const
|
| + * @dict
|
| + * @private
|
| + */
|
| +goog.events.onStringMap_ = {};
|
| +
|
| +
|
| +/**
|
| + * @enum {number} Different capture simulation mode for IE8-.
|
| + */
|
| +goog.events.CaptureSimulationMode = {
|
| + /**
|
| + * Does not perform capture simulation. Will asserts in IE8- when you
|
| + * add capture listeners.
|
| + */
|
| + OFF_AND_FAIL: 0,
|
| +
|
| + /**
|
| + * Does not perform capture simulation, silently ignore capture
|
| + * listeners.
|
| + */
|
| + OFF_AND_SILENT: 1,
|
| +
|
| + /**
|
| + * Performs capture simulation.
|
| + */
|
| + ON: 2
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @define {number} The capture simulation mode for IE8-. By default,
|
| + * this is ON.
|
| + */
|
| +goog.define('goog.events.CAPTURE_SIMULATION_MODE', 2);
|
| +
|
| +
|
| +/**
|
| + * Estimated count of total native listeners.
|
| + * @private {number}
|
| + */
|
| +goog.events.listenerCountEstimate_ = 0;
|
| +
|
| +
|
| +/**
|
| + * Adds an event listener for a specific event on a native event
|
| + * target (such as a DOM element) or an object that has implemented
|
| + * {@link goog.events.Listenable}. A listener can only be added once
|
| + * to an object and if it is added again the key for the listener is
|
| + * returned. Note that if the existing listener is a one-off listener
|
| + * (registered via listenOnce), it will no longer be a one-off
|
| + * listener after a call to listen().
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The node to listen
|
| + * to events on.
|
| + * @param {string|Array.<string>|
|
| + * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
|
| + * type Event type or array of event types.
|
| + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
|
| + * listener Callback method, or an object with a handleEvent function.
|
| + * WARNING: passing an Object is now softly deprecated.
|
| + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
| + * false).
|
| + * @param {T=} opt_handler Element in whose scope to call the listener.
|
| + * @return {goog.events.Key} Unique key for the listener.
|
| + * @template T,EVENTOBJ
|
| + */
|
| +goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
|
| + if (goog.isArray(type)) {
|
| + for (var i = 0; i < type.length; i++) {
|
| + goog.events.listen(src, type[i], listener, opt_capt, opt_handler);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + listener = goog.events.wrapListener(listener);
|
| + if (goog.events.Listenable.isImplementedBy(src)) {
|
| + return src.listen(
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, opt_capt, opt_handler);
|
| + } else {
|
| + return goog.events.listen_(
|
| + /** @type {EventTarget} */ (src),
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, /* callOnce */ false, opt_capt, opt_handler);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Adds an event listener for a specific event on a native event
|
| + * target. A listener can only be added once to an object and if it
|
| + * is added again the key for the listener is returned.
|
| + *
|
| + * Note that a one-off listener will not change an existing listener,
|
| + * if any. On the other hand a normal listener will change existing
|
| + * one-off listener to become a normal listener.
|
| + *
|
| + * @param {EventTarget} src The node to listen to events on.
|
| + * @param {string|!goog.events.EventId} type Event type.
|
| + * @param {!Function} listener Callback function.
|
| + * @param {boolean} callOnce Whether the listener is a one-off
|
| + * listener or otherwise.
|
| + * @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.ListenableKey} Unique key for the listener.
|
| + * @private
|
| + */
|
| +goog.events.listen_ = function(
|
| + src, type, listener, callOnce, opt_capt, opt_handler) {
|
| + if (!type) {
|
| + throw Error('Invalid event type');
|
| + }
|
| +
|
| + var capture = !!opt_capt;
|
| + if (capture && !goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
|
| + if (goog.events.CAPTURE_SIMULATION_MODE ==
|
| + goog.events.CaptureSimulationMode.OFF_AND_FAIL) {
|
| + goog.asserts.fail('Can not register capture listener in IE8-.');
|
| + return null;
|
| + } else if (goog.events.CAPTURE_SIMULATION_MODE ==
|
| + goog.events.CaptureSimulationMode.OFF_AND_SILENT) {
|
| + return null;
|
| + }
|
| + }
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(src);
|
| + if (!listenerMap) {
|
| + src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
|
| + new goog.events.ListenerMap(src);
|
| + }
|
| +
|
| + var listenerObj = listenerMap.add(
|
| + type, listener, callOnce, opt_capt, opt_handler);
|
| +
|
| + // If the listenerObj already has a proxy, it has been set up
|
| + // previously. We simply return.
|
| + if (listenerObj.proxy) {
|
| + return listenerObj;
|
| + }
|
| +
|
| + var proxy = goog.events.getProxy();
|
| + listenerObj.proxy = proxy;
|
| +
|
| + proxy.src = src;
|
| + proxy.listener = listenerObj;
|
| +
|
| + // Attach the proxy through the browser's API
|
| + if (src.addEventListener) {
|
| + src.addEventListener(type.toString(), proxy, capture);
|
| + } else {
|
| + // The else above used to be else if (src.attachEvent) and then there was
|
| + // another else statement that threw an exception warning the developer
|
| + // they made a mistake. This resulted in an extra object allocation in IE6
|
| + // due to a wrapper object that had to be implemented around the element
|
| + // and so was removed.
|
| + src.attachEvent(goog.events.getOnString_(type.toString()), proxy);
|
| + }
|
| +
|
| + goog.events.listenerCountEstimate_++;
|
| + return listenerObj;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Helper function for returning a proxy function.
|
| + * @return {!Function} A new or reused function object.
|
| + */
|
| +goog.events.getProxy = function() {
|
| + var proxyCallbackFunction = goog.events.handleBrowserEvent_;
|
| + // Use a local var f to prevent one allocation.
|
| + var f = goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT ?
|
| + function(eventObject) {
|
| + return proxyCallbackFunction.call(f.src, f.listener, eventObject);
|
| + } :
|
| + function(eventObject) {
|
| + var v = proxyCallbackFunction.call(f.src, f.listener, eventObject);
|
| + // NOTE(user): In IE, we hack in a capture phase. However, if
|
| + // there is inline event handler which tries to prevent default (for
|
| + // example <a href="..." onclick="return false">...</a>) in a
|
| + // descendant element, the prevent default will be overridden
|
| + // by this listener if this listener were to return true. Hence, we
|
| + // return undefined.
|
| + if (!v) return v;
|
| + };
|
| + return f;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Adds an event listener for a specific event on a native event
|
| + * target (such as a DOM element) or an object that has implemented
|
| + * {@link goog.events.Listenable}. After the event has fired the event
|
| + * listener is removed from the target.
|
| + *
|
| + * If an existing listener already exists, listenOnce will do
|
| + * nothing. In particular, if the listener was previously registered
|
| + * via listen(), listenOnce() will not turn the listener into a
|
| + * one-off listener. Similarly, if there is already an existing
|
| + * one-off listener, listenOnce does not modify the listeners (it is
|
| + * still a once listener).
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The node to listen
|
| + * to events on.
|
| + * @param {string|Array.<string>|
|
| + * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
|
| + * type Event type or array of event types.
|
| + * @param {function(this:T, EVENTOBJ):?|{handleEvent:function(?):?}|null}
|
| + * listener Callback method.
|
| + * @param {boolean=} opt_capt Fire in capture phase?.
|
| + * @param {T=} opt_handler Element in whose scope to call the listener.
|
| + * @return {goog.events.Key} Unique key for the listener.
|
| + * @template T,EVENTOBJ
|
| + */
|
| +goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
|
| + if (goog.isArray(type)) {
|
| + for (var i = 0; i < type.length; i++) {
|
| + goog.events.listenOnce(src, type[i], listener, opt_capt, opt_handler);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + listener = goog.events.wrapListener(listener);
|
| + if (goog.events.Listenable.isImplementedBy(src)) {
|
| + return src.listenOnce(
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, opt_capt, opt_handler);
|
| + } else {
|
| + return goog.events.listen_(
|
| + /** @type {EventTarget} */ (src),
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, /* callOnce */ true, opt_capt, opt_handler);
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Adds an event listener with a specific event wrapper on a DOM Node or an
|
| + * object that has implemented {@link goog.events.Listenable}. A listener can
|
| + * only be added once to an object.
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The target to
|
| + * listen to events on.
|
| + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
|
| + * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
|
| + * Callback method, or an object with a handleEvent function.
|
| + * @param {boolean=} opt_capt Whether to fire in capture phase (defaults to
|
| + * false).
|
| + * @param {T=} opt_handler Element in whose scope to call the listener.
|
| + * @template T
|
| + */
|
| +goog.events.listenWithWrapper = function(src, wrapper, listener, opt_capt,
|
| + opt_handler) {
|
| + wrapper.listen(src, listener, opt_capt, opt_handler);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Removes an event listener which was added with listen().
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The target to stop
|
| + * listening to events on.
|
| + * @param {string|Array.<string>|
|
| + * !goog.events.EventId.<EVENTOBJ>|!Array.<!goog.events.EventId.<EVENTOBJ>>}
|
| + * type Event type or array of event types to unlisten to.
|
| + * @param {function(?):?|{handleEvent:function(?):?}|null} 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 {?boolean} indicating whether the listener was there to remove.
|
| + * @template EVENTOBJ
|
| + */
|
| +goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
|
| + if (goog.isArray(type)) {
|
| + for (var i = 0; i < type.length; i++) {
|
| + goog.events.unlisten(src, type[i], listener, opt_capt, opt_handler);
|
| + }
|
| + return null;
|
| + }
|
| +
|
| + listener = goog.events.wrapListener(listener);
|
| + if (goog.events.Listenable.isImplementedBy(src)) {
|
| + return src.unlisten(
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, opt_capt, opt_handler);
|
| + }
|
| +
|
| + if (!src) {
|
| + // TODO(user): We should tighten the API to only accept
|
| + // non-null objects, or add an assertion here.
|
| + return false;
|
| + }
|
| +
|
| + var capture = !!opt_capt;
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (src));
|
| + if (listenerMap) {
|
| + var listenerObj = listenerMap.getListener(
|
| + /** @type {string|!goog.events.EventId} */ (type),
|
| + listener, capture, opt_handler);
|
| + if (listenerObj) {
|
| + return goog.events.unlistenByKey(listenerObj);
|
| + }
|
| + }
|
| +
|
| + return false;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Removes an event listener which was added with listen() by the key
|
| + * returned by listen().
|
| + *
|
| + * @param {goog.events.Key} key The key returned by listen() for this
|
| + * event listener.
|
| + * @return {boolean} indicating whether the listener was there to remove.
|
| + */
|
| +goog.events.unlistenByKey = function(key) {
|
| + // TODO(user): Remove this check when tests that rely on this
|
| + // are fixed.
|
| + if (goog.isNumber(key)) {
|
| + return false;
|
| + }
|
| +
|
| + var listener = /** @type {goog.events.ListenableKey} */ (key);
|
| + if (!listener || listener.removed) {
|
| + return false;
|
| + }
|
| +
|
| + var src = listener.src;
|
| + if (goog.events.Listenable.isImplementedBy(src)) {
|
| + return src.unlistenByKey(listener);
|
| + }
|
| +
|
| + var type = listener.type;
|
| + var proxy = listener.proxy;
|
| + if (src.removeEventListener) {
|
| + src.removeEventListener(type, proxy, listener.capture);
|
| + } else if (src.detachEvent) {
|
| + src.detachEvent(goog.events.getOnString_(type), proxy);
|
| + }
|
| + goog.events.listenerCountEstimate_--;
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (src));
|
| + // TODO(user): Try to remove this conditional and execute the
|
| + // first branch always. This should be safe.
|
| + if (listenerMap) {
|
| + listenerMap.removeByKey(listener);
|
| + if (listenerMap.getTypeCount() == 0) {
|
| + // Null the src, just because this is simple to do (and useful
|
| + // for IE <= 7).
|
| + listenerMap.src = null;
|
| + // We don't use delete here because IE does not allow delete
|
| + // on a window object.
|
| + src[goog.events.LISTENER_MAP_PROP_] = null;
|
| + }
|
| + } else {
|
| + listener.markAsRemoved();
|
| + }
|
| +
|
| + return true;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Removes an event listener which was added with listenWithWrapper().
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The target to stop
|
| + * listening to events on.
|
| + * @param {goog.events.EventWrapper} wrapper Event wrapper to use.
|
| + * @param {function(?):?|{handleEvent:function(?):?}|null} 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.
|
| + */
|
| +goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
|
| + opt_handler) {
|
| + wrapper.unlisten(src, listener, opt_capt, opt_handler);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Removes all listeners from an object. You can also optionally
|
| + * remove listeners of a particular type.
|
| + *
|
| + * @param {Object|undefined} obj Object to remove listeners from. Must be an
|
| + * EventTarget or a goog.events.Listenable.
|
| + * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
|
| + * Default is all types.
|
| + * @return {number} Number of listeners removed.
|
| + */
|
| +goog.events.removeAll = function(obj, opt_type) {
|
| + // TODO(user): Change the type of obj to
|
| + // (!EventTarget|!goog.events.Listenable).
|
| +
|
| + if (!obj) {
|
| + return 0;
|
| + }
|
| +
|
| + if (goog.events.Listenable.isImplementedBy(obj)) {
|
| + return obj.removeAllListeners(opt_type);
|
| + }
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (obj));
|
| + if (!listenerMap) {
|
| + return 0;
|
| + }
|
| +
|
| + var count = 0;
|
| + var typeStr = opt_type && opt_type.toString();
|
| + for (var type in listenerMap.listeners) {
|
| + if (!typeStr || type == typeStr) {
|
| + // Clone so that we don't need to worry about unlistenByKey
|
| + // changing the content of the ListenerMap.
|
| + var listeners = listenerMap.listeners[type].concat();
|
| + for (var i = 0; i < listeners.length; ++i) {
|
| + if (goog.events.unlistenByKey(listeners[i])) {
|
| + ++count;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return count;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Removes all native listeners registered via goog.events. Native
|
| + * listeners are listeners on native browser objects (such as DOM
|
| + * elements). In particular, goog.events.Listenable and
|
| + * goog.events.EventTarget listeners will NOT be removed.
|
| + * @return {number} Number of listeners removed.
|
| + * @deprecated This doesn't do anything, now that Closure no longer
|
| + * stores a central listener registry.
|
| + */
|
| +goog.events.removeAllNativeListeners = function() {
|
| + goog.events.listenerCountEstimate_ = 0;
|
| + return 0;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the listeners for a given object, type and capture phase.
|
| + *
|
| + * @param {Object} obj Object to get listeners for.
|
| + * @param {string|!goog.events.EventId} type Event type.
|
| + * @param {boolean} capture Capture phase?.
|
| + * @return {Array.<goog.events.Listener>} Array of listener objects.
|
| + */
|
| +goog.events.getListeners = function(obj, type, capture) {
|
| + if (goog.events.Listenable.isImplementedBy(obj)) {
|
| + return obj.getListeners(type, capture);
|
| + } else {
|
| + if (!obj) {
|
| + // TODO(user): We should tighten the API to accept
|
| + // !EventTarget|goog.events.Listenable, and add an assertion here.
|
| + return [];
|
| + }
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (obj));
|
| + return listenerMap ? listenerMap.getListeners(type, capture) : [];
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the goog.events.Listener for the event or null if no such listener is
|
| + * in use.
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} src The target from
|
| + * which to get listeners.
|
| + * @param {?string|!goog.events.EventId.<EVENTOBJ>} type The type of the event.
|
| + * @param {function(EVENTOBJ):?|{handleEvent:function(?):?}|null} listener The
|
| + * listener function to get.
|
| + * @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.ListenableKey} the found listener or null if not found.
|
| + * @template EVENTOBJ
|
| + */
|
| +goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
|
| + // TODO(user): Change type from ?string to string, or add assertion.
|
| + type = /** @type {string} */ (type);
|
| + listener = goog.events.wrapListener(listener);
|
| + var capture = !!opt_capt;
|
| + if (goog.events.Listenable.isImplementedBy(src)) {
|
| + return src.getListener(type, listener, capture, opt_handler);
|
| + }
|
| +
|
| + if (!src) {
|
| + // TODO(user): We should tighten the API to only accept
|
| + // non-null objects, or add an assertion here.
|
| + return null;
|
| + }
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (src));
|
| + if (listenerMap) {
|
| + return listenerMap.getListener(type, listener, capture, opt_handler);
|
| + }
|
| + return null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Returns whether an event target has any active listeners matching the
|
| + * specified signature. If either the type or capture parameters are
|
| + * unspecified, the function will match on the remaining criteria.
|
| + *
|
| + * @param {EventTarget|goog.events.Listenable} obj Target to get
|
| + * listeners for.
|
| + * @param {string|!goog.events.EventId=} opt_type Event type.
|
| + * @param {boolean=} opt_capture Whether to check for capture or bubble-phase
|
| + * listeners.
|
| + * @return {boolean} Whether an event target has one or more listeners matching
|
| + * the requested type and/or capture phase.
|
| + */
|
| +goog.events.hasListener = function(obj, opt_type, opt_capture) {
|
| + if (goog.events.Listenable.isImplementedBy(obj)) {
|
| + return obj.hasListener(opt_type, opt_capture);
|
| + }
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (obj));
|
| + return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Provides a nice string showing the normalized event objects public members
|
| + * @param {Object} e Event Object.
|
| + * @return {string} String of the public members of the normalized event object.
|
| + */
|
| +goog.events.expose = function(e) {
|
| + var str = [];
|
| + for (var key in e) {
|
| + if (e[key] && e[key].id) {
|
| + str.push(key + ' = ' + e[key] + ' (' + e[key].id + ')');
|
| + } else {
|
| + str.push(key + ' = ' + e[key]);
|
| + }
|
| + }
|
| + return str.join('\n');
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Returns a string with on prepended to the specified type. This is used for IE
|
| + * which expects "on" to be prepended. This function caches the string in order
|
| + * to avoid extra allocations in steady state.
|
| + * @param {string} type Event type.
|
| + * @return {string} The type string with 'on' prepended.
|
| + * @private
|
| + */
|
| +goog.events.getOnString_ = function(type) {
|
| + if (type in goog.events.onStringMap_) {
|
| + return goog.events.onStringMap_[type];
|
| + }
|
| + return goog.events.onStringMap_[type] = goog.events.onString_ + type;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Fires an object's listeners of a particular type and phase
|
| + *
|
| + * @param {Object} obj Object whose listeners to call.
|
| + * @param {string|!goog.events.EventId} type Event type.
|
| + * @param {boolean} capture Which event phase.
|
| + * @param {Object} eventObject Event object to be passed to listener.
|
| + * @return {boolean} True if all listeners returned true else false.
|
| + */
|
| +goog.events.fireListeners = function(obj, type, capture, eventObject) {
|
| + if (goog.events.Listenable.isImplementedBy(obj)) {
|
| + return obj.fireListeners(type, capture, eventObject);
|
| + }
|
| +
|
| + return goog.events.fireListeners_(obj, type, capture, eventObject);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Fires an object's listeners of a particular type and phase.
|
| + * @param {Object} obj Object whose listeners to call.
|
| + * @param {string|!goog.events.EventId} type Event type.
|
| + * @param {boolean} capture Which event phase.
|
| + * @param {Object} eventObject Event object to be passed to listener.
|
| + * @return {boolean} True if all listeners returned true else false.
|
| + * @private
|
| + */
|
| +goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
|
| + var retval = 1;
|
| +
|
| + var listenerMap = goog.events.getListenerMap_(
|
| + /** @type {EventTarget} */ (obj));
|
| + if (listenerMap) {
|
| + // TODO(user): Original code avoids array creation when there
|
| + // is no listener, so we do the same. If this optimization turns
|
| + // out to be not required, we can replace this with
|
| + // listenerMap.getListeners(type, capture) instead, which is simpler.
|
| + var listenerArray = listenerMap.listeners[type.toString()];
|
| + if (listenerArray) {
|
| + listenerArray = listenerArray.concat();
|
| + for (var i = 0; i < listenerArray.length; i++) {
|
| + var listener = listenerArray[i];
|
| + // We might not have a listener if the listener was removed.
|
| + if (listener && listener.capture == capture && !listener.removed) {
|
| + retval &=
|
| + goog.events.fireListener(listener, eventObject) !== false;
|
| + }
|
| + }
|
| + }
|
| + }
|
| + return Boolean(retval);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Fires a listener with a set of arguments
|
| + *
|
| + * @param {goog.events.Listener} listener The listener object to call.
|
| + * @param {Object} eventObject The event object to pass to the listener.
|
| + * @return {boolean} Result of listener.
|
| + */
|
| +goog.events.fireListener = function(listener, eventObject) {
|
| + var listenerFn = listener.listener;
|
| + var listenerHandler = listener.handler || listener.src;
|
| +
|
| + if (listener.callOnce) {
|
| + goog.events.unlistenByKey(listener);
|
| + }
|
| + return listenerFn.call(listenerHandler, eventObject);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the total number of listeners currently in the system.
|
| + * @return {number} Number of listeners.
|
| + * @deprecated This returns estimated count, now that Closure no longer
|
| + * stores a central listener registry. We still return an estimation
|
| + * to keep existing listener-related tests passing. In the near future,
|
| + * this function will be removed.
|
| + */
|
| +goog.events.getTotalListenerCount = function() {
|
| + return goog.events.listenerCountEstimate_;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Dispatches an event (or event like object) and calls all listeners
|
| + * listening for events of this type. The type of the event is decided by the
|
| + * type property on the event object.
|
| + *
|
| + * If any of the listeners returns false OR calls preventDefault then this
|
| + * function will return false. If one of the capture listeners calls
|
| + * stopPropagation, then the bubble listeners won't fire.
|
| + *
|
| + * @param {goog.events.Listenable} src The event target.
|
| + * @param {goog.events.EventLike} e Event object.
|
| + * @return {boolean} If anyone called preventDefault on the event object (or
|
| + * if any of the handlers returns false) this will also return false.
|
| + * If there are no handlers, or if all handlers return true, this returns
|
| + * true.
|
| + */
|
| +goog.events.dispatchEvent = function(src, e) {
|
| + goog.asserts.assert(
|
| + goog.events.Listenable.isImplementedBy(src),
|
| + 'Can not use goog.events.dispatchEvent with ' +
|
| + 'non-goog.events.Listenable instance.');
|
| + return src.dispatchEvent(e);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Installs exception protection for the browser event entry point using the
|
| + * given error handler.
|
| + *
|
| + * @param {goog.debug.ErrorHandler} errorHandler Error handler with which to
|
| + * protect the entry point.
|
| + */
|
| +goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
|
| + goog.events.handleBrowserEvent_ = errorHandler.protectEntryPoint(
|
| + goog.events.handleBrowserEvent_);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Handles an event and dispatches it to the correct listeners. This
|
| + * function is a proxy for the real listener the user specified.
|
| + *
|
| + * @param {goog.events.Listener} listener The listener object.
|
| + * @param {Event=} opt_evt Optional event object that gets passed in via the
|
| + * native event handlers.
|
| + * @return {boolean} Result of the event handler.
|
| + * @this {EventTarget} The object or Element that fired the event.
|
| + * @private
|
| + */
|
| +goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
|
| + if (listener.removed) {
|
| + return true;
|
| + }
|
| +
|
| + // Synthesize event propagation if the browser does not support W3C
|
| + // event model.
|
| + if (!goog.events.BrowserFeature.HAS_W3C_EVENT_SUPPORT) {
|
| + var ieEvent = opt_evt ||
|
| + /** @type {Event} */ (goog.getObjectByName('window.event'));
|
| + var evt = new goog.events.BrowserEvent(ieEvent, this);
|
| + var retval = true;
|
| +
|
| + if (goog.events.CAPTURE_SIMULATION_MODE ==
|
| + goog.events.CaptureSimulationMode.ON) {
|
| + // If we have not marked this event yet, we should perform capture
|
| + // simulation.
|
| + if (!goog.events.isMarkedIeEvent_(ieEvent)) {
|
| + goog.events.markIeEvent_(ieEvent);
|
| +
|
| + var ancestors = [];
|
| + for (var parent = evt.currentTarget; parent;
|
| + parent = parent.parentNode) {
|
| + ancestors.push(parent);
|
| + }
|
| +
|
| + // Fire capture listeners.
|
| + var type = listener.type;
|
| + for (var i = ancestors.length - 1; !evt.propagationStopped_ && i >= 0;
|
| + i--) {
|
| + evt.currentTarget = ancestors[i];
|
| + retval &= goog.events.fireListeners_(ancestors[i], type, true, evt);
|
| + }
|
| +
|
| + // Fire bubble listeners.
|
| + //
|
| + // We can technically rely on IE to perform bubble event
|
| + // propagation. However, it turns out that IE fires events in
|
| + // opposite order of attachEvent registration, which broke
|
| + // some code and tests that rely on the order. (While W3C DOM
|
| + // Level 2 Events TR leaves the event ordering unspecified,
|
| + // modern browsers and W3C DOM Level 3 Events Working Draft
|
| + // actually specify the order as the registration order.)
|
| + for (var i = 0; !evt.propagationStopped_ && i < ancestors.length; i++) {
|
| + evt.currentTarget = ancestors[i];
|
| + retval &= goog.events.fireListeners_(ancestors[i], type, false, evt);
|
| + }
|
| + }
|
| + } else {
|
| + retval = goog.events.fireListener(listener, evt);
|
| + }
|
| + return retval;
|
| + }
|
| +
|
| + // Otherwise, simply fire the listener.
|
| + return goog.events.fireListener(
|
| + listener, new goog.events.BrowserEvent(opt_evt, this));
|
| +};
|
| +
|
| +
|
| +/**
|
| + * This is used to mark the IE event object so we do not do the Closure pass
|
| + * twice for a bubbling event.
|
| + * @param {Event} e The IE browser event.
|
| + * @private
|
| + */
|
| +goog.events.markIeEvent_ = function(e) {
|
| + // Only the keyCode and the returnValue can be changed. We use keyCode for
|
| + // non keyboard events.
|
| + // event.returnValue is a bit more tricky. It is undefined by default. A
|
| + // boolean false prevents the default action. In a window.onbeforeunload and
|
| + // the returnValue is non undefined it will be alerted. However, we will only
|
| + // modify the returnValue for keyboard events. We can get a problem if non
|
| + // closure events sets the keyCode or the returnValue
|
| +
|
| + var useReturnValue = false;
|
| +
|
| + if (e.keyCode == 0) {
|
| + // We cannot change the keyCode in case that srcElement is input[type=file].
|
| + // We could test that that is the case but that would allocate 3 objects.
|
| + // If we use try/catch we will only allocate extra objects in the case of a
|
| + // failure.
|
| + /** @preserveTry */
|
| + try {
|
| + e.keyCode = -1;
|
| + return;
|
| + } catch (ex) {
|
| + useReturnValue = true;
|
| + }
|
| + }
|
| +
|
| + if (useReturnValue ||
|
| + /** @type {boolean|undefined} */ (e.returnValue) == undefined) {
|
| + e.returnValue = true;
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| + * This is used to check if an IE event has already been handled by the Closure
|
| + * system so we do not do the Closure pass twice for a bubbling event.
|
| + * @param {Event} e The IE browser event.
|
| + * @return {boolean} True if the event object has been marked.
|
| + * @private
|
| + */
|
| +goog.events.isMarkedIeEvent_ = function(e) {
|
| + return e.keyCode < 0 || e.returnValue != undefined;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Counter to create unique event ids.
|
| + * @private {number}
|
| + */
|
| +goog.events.uniqueIdCounter_ = 0;
|
| +
|
| +
|
| +/**
|
| + * Creates a unique event id.
|
| + *
|
| + * @param {string} identifier The identifier.
|
| + * @return {string} A unique identifier.
|
| + * @idGenerator
|
| + */
|
| +goog.events.getUniqueId = function(identifier) {
|
| + return identifier + '_' + goog.events.uniqueIdCounter_++;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * @param {EventTarget} src The source object.
|
| + * @return {goog.events.ListenerMap} A listener map for the given
|
| + * source object, or null if none exists.
|
| + * @private
|
| + */
|
| +goog.events.getListenerMap_ = function(src) {
|
| + var listenerMap = src[goog.events.LISTENER_MAP_PROP_];
|
| + // IE serializes the property as well (e.g. when serializing outer
|
| + // HTML). So we must check that the value is of the correct type.
|
| + return listenerMap instanceof goog.events.ListenerMap ? listenerMap : null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Expando property for listener function wrapper for Object with
|
| + * handleEvent.
|
| + * @const
|
| + * @private
|
| + */
|
| +goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
|
| + ((Math.random() * 1e9) >>> 0);
|
| +
|
| +
|
| +/**
|
| + * @param {Object|Function} listener The listener function or an
|
| + * object that contains handleEvent method.
|
| + * @return {!Function} Either the original function or a function that
|
| + * calls obj.handleEvent. If the same listener is passed to this
|
| + * function more than once, the same function is guaranteed to be
|
| + * returned.
|
| + */
|
| +goog.events.wrapListener = function(listener) {
|
| + goog.asserts.assert(listener, 'Listener can not be null.');
|
| +
|
| + if (goog.isFunction(listener)) {
|
| + return listener;
|
| + }
|
| +
|
| + goog.asserts.assert(
|
| + listener.handleEvent, 'An object listener must have handleEvent method.');
|
| + if (!listener[goog.events.LISTENER_WRAPPER_PROP_]) {
|
| + listener[goog.events.LISTENER_WRAPPER_PROP_] =
|
| + function(e) { return listener.handleEvent(e); };
|
| + }
|
| + return listener[goog.events.LISTENER_WRAPPER_PROP_];
|
| +};
|
| +
|
| +
|
| +// Register the browser event handler as an entry point, so that
|
| +// it can be monitored for exception handling, etc.
|
| +goog.debug.entryPointRegistry.register(
|
| + /**
|
| + * @param {function(!Function): !Function} transformer The transforming
|
| + * function.
|
| + */
|
| + function(transformer) {
|
| + goog.events.handleBrowserEvent_ = transformer(
|
| + goog.events.handleBrowserEvent_);
|
| + });
|
|
|