diff --git a/haskell/ReplSession.hs b/haskell/ReplSession.hs
index a7aad38..2bae7bb 100644
--- a/haskell/ReplSession.hs
+++ b/haskell/ReplSession.hs
@@ -10,6 +10,7 @@ import System.Process
import System.Directory (getDirectoryContents)
import Data.List (isSuffixOf)
import Control.Monad (liftM)
+import Language.Haskell.Exts
data ReplSession = ReplSession {
replIn :: Handle,
@@ -20,11 +21,33 @@ data ReplSession = ReplSession {
evalInSession :: String -> ReplSession -> IO (Either String String)
evalInSession cmd session@(ReplSession input out err _) = do
- clearHandle out 0
+ sendCommand (":{\n" ++ prepareCode cmd ++ "\n") session
+ clearHandle out 10
clearHandle err 0
- sendCommand (cmd ++ "\n") session
+ sendCommand ":}\n" session
readEvalOutput session
+prepareCode :: String -> String
+prepareCode code = case parseCode code of
+ [] -> code
+ decls -> if isFunDecl $ head decls
+ then prependLet decls
+ else code
+
+isFunDecl :: Decl -> Bool
+isFunDecl (TypeSig {}) = True
+isFunDecl (FunBind {}) = True
+isFunDecl (PatBind {}) = True
+isFunDecl _ = False
+
+prependLet :: [Decl] -> String
+prependLet = prettyPrint . letStmt
+
+parseCode :: String -> [Decl]
+parseCode code = case parseFileContents code of
+ (ParseOk (Module _ _ _ _ _ _ decls)) -> decls
+ (ParseFailed {}) -> []
+
readEvalOutput :: ReplSession -> IO (Either String String)
readEvalOutput (ReplSession _ out err _) = do
output <- readUntil out ("--EvalFinished\n" `isSuffixOf`)
diff --git a/haskell_compiled.js b/haskell_compiled.js
index 8e7a832..38c4210 100644
--- a/haskell_compiled.js
+++ b/haskell_compiled.js
@@ -1,5 +1,5 @@
-if(!lt.util.load.provided_QMARK_('goog.debug.Error')) {
-// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.disposable.IDisposable')) {
+// Copyright 2011 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.
@@ -14,45 +14,39 @@ if(!lt.util.load.provided_QMARK_('goog.debug.Error')) {
// limitations under the License.
/**
- * @fileoverview Provides a base class for custom Error objects such that the
- * stack is correctly maintained.
- *
- * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
- * sufficient.
- *
+ * @fileoverview Definition of the disposable interface. A disposable object
+ * has a dispose method to to clean up references and resources.
+ * @author nnaze@google.com (Nathan Naze)
*/
-goog.provide('goog.debug.Error');
+
+goog.provide('goog.disposable.IDisposable');
/**
- * Base class for custom error objects.
- * @param {*=} opt_msg The message associated with the error.
- * @constructor
- * @extends {Error}
+ * Interface for a disposable object. If a instance requires cleanup
+ * (references COM objects, DOM notes, or other disposable objects), it should
+ * implement this interface (it may subclass goog.Disposable).
+ * @interface
*/
-goog.debug.Error = function(opt_msg) {
+goog.disposable.IDisposable = function() {};
- // Ensure there is a stack trace.
- if (Error.captureStackTrace) {
- Error.captureStackTrace(this, goog.debug.Error);
- } else {
- this.stack = new Error().stack || '';
- }
- if (opt_msg) {
- this.message = String(opt_msg);
- }
-};
-goog.inherits(goog.debug.Error, Error);
+/**
+ * Disposes of the object and its resources.
+ * @return {void} Nothing.
+ */
+goog.disposable.IDisposable.prototype.dispose;
-/** @override */
-goog.debug.Error.prototype.name = 'CustomError';
+/**
+ * @return {boolean} Whether the object has been disposed of.
+ */
+goog.disposable.IDisposable.prototype.isDisposed;
}
-if(!lt.util.load.provided_QMARK_('goog.asserts')) {
-// Copyright 2008 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.Disposable')) {
+// 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.
@@ -67,276 +61,291 @@ if(!lt.util.load.provided_QMARK_('goog.asserts')) {
// limitations under the License.
/**
- * @fileoverview Utilities to check the preconditions, postconditions and
- * invariants runtime.
- *
- * Methods in this package should be given special treatment by the compiler
- * for type-inference. For example, goog.asserts.assert(foo)
- * will restrict foo to a truthy value.
- *
- * The compiler has an option to disable asserts. So code like:
- *
- * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar());
- *
- * will be transformed into:
- *
- * var x = foo();
- *
- * The compiler will leave in foo() (because its return value is used),
- * but it will remove bar() because it assumes it does not have side-effects.
- *
+ * @fileoverview Implements the disposable interface. The dispose method is used
+ * to clean up references and resources.
+ * @author arv@google.com (Erik Arvidsson)
*/
-goog.provide('goog.asserts');
-goog.provide('goog.asserts.AssertionError');
-
-goog.require('goog.debug.Error');
-goog.require('goog.string');
+goog.provide('goog.Disposable');
+/** @suppress {extraProvide} */
+goog.provide('goog.dispose');
+/** @suppress {extraProvide} */
+goog.provide('goog.disposeAll');
-/**
- * @define {boolean} Whether to strip out asserts or to leave them in.
- */
-goog.asserts.ENABLE_ASSERTS = goog.DEBUG;
+goog.require('goog.disposable.IDisposable');
/**
- * Error object for failed assertions.
- * @param {string} messagePattern The pattern that was used to form message.
- * @param {!Array.<*>} messageArgs The items to substitute into the pattern.
+ * Class that provides the basic implementation for disposable objects. If your
+ * class holds one or more references to COM objects, DOM nodes, or other
+ * disposable objects, it should extend this class or implement the disposable
+ * interface (defined in goog.disposable.IDisposable).
* @constructor
- * @extends {goog.debug.Error}
+ * @implements {goog.disposable.IDisposable}
*/
-goog.asserts.AssertionError = function(messagePattern, messageArgs) {
- messageArgs.unshift(messagePattern);
- goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
- // Remove the messagePattern afterwards to avoid permenantly modifying the
- // passed in array.
- messageArgs.shift();
+goog.Disposable = function() {
+ if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
+ if (goog.Disposable.INCLUDE_STACK_ON_CREATION) {
+ this.creationStack = new Error().stack;
+ }
+ goog.Disposable.instances_[goog.getUid(this)] = this;
+ }
+};
+
+/**
+ * @enum {number} Different monitoring modes for Disposable.
+ */
+goog.Disposable.MonitoringMode = {
/**
- * The message pattern used to format the error message. Error handlers can
- * use this to uniquely identify the assertion.
- * @type {string}
+ * No monitoring.
*/
- this.messagePattern = messagePattern;
+ OFF: 0,
+ /**
+ * Creating and disposing the goog.Disposable instances is monitored. All
+ * disposable objects need to call the {@code goog.Disposable} base
+ * constructor. The PERMANENT mode must be switched on before creating any
+ * goog.Disposable instances.
+ */
+ PERMANENT: 1,
+ /**
+ * INTERACTIVE mode can be switched on and off on the fly without producing
+ * errors. It also doesn't warn if the disposable objects don't call the
+ * {@code goog.Disposable} base constructor.
+ */
+ INTERACTIVE: 2
};
-goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
-
-
-/** @override */
-goog.asserts.AssertionError.prototype.name = 'AssertionError';
/**
- * Throws an exception with the given message and "Assertion failed" prefixed
- * onto it.
- * @param {string} defaultMessage The message to use if givenMessage is empty.
- * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage.
- * @param {string|undefined} givenMessage Message supplied by the caller.
- * @param {Array.<*>} givenArgs The substitution arguments for givenMessage.
- * @throws {goog.asserts.AssertionError} When the value is not a number.
- * @private
+ * @define {number} The monitoring mode of the goog.Disposable
+ * instances. Default is OFF. Switching on the monitoring is only
+ * recommended for debugging because it has a significant impact on
+ * performance and memory usage. If switched off, the monitoring code
+ * compiles down to 0 bytes.
*/
-goog.asserts.doAssertFailure_ =
- function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
- var message = 'Assertion failed';
- if (givenMessage) {
- message += ': ' + givenMessage;
- var args = givenArgs;
- } else if (defaultMessage) {
- message += ': ' + defaultMessage;
- args = defaultArgs;
- }
- // The '' + works around an Opera 10 bug in the unit tests. Without it,
- // a stack trace is added to var message above. With this, a stack trace is
- // not added until this line (it causes the extra garbage to be added after
- // the assertion message instead of in the middle of it).
- throw new goog.asserts.AssertionError('' + message, args || []);
-};
+goog.define('goog.Disposable.MONITORING_MODE', 0);
/**
- * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
- * true.
- * @param {*} condition The condition to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {*} The value of the condition.
- * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
+ * @define {boolean} Whether to attach creation stack to each created disposable
+ * instance; This is only relevant for when MonitoringMode != OFF.
*/
-goog.asserts.assert = function(condition, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !condition) {
- goog.asserts.doAssertFailure_('', null, opt_message,
- Array.prototype.slice.call(arguments, 2));
- }
- return condition;
-};
+goog.define('goog.Disposable.INCLUDE_STACK_ON_CREATION', true);
/**
- * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
- * when we want to add a check in the unreachable area like switch-case
- * statement:
- *
- *
- * switch(type) {
- * case FOO: doSomething(); break;
- * case BAR: doSomethingElse(); break;
- * default: goog.assert.fail('Unrecognized type: ' + type);
- * // We have only 2 types - "default:" section is unreachable code.
- * }
- *
- *
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @throws {goog.asserts.AssertionError} Failure.
+ * Maps the unique ID of every undisposed {@code goog.Disposable} object to
+ * the object itself.
+ * @type {!Object.}
+ * @private
*/
-goog.asserts.fail = function(opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS) {
- throw new goog.asserts.AssertionError(
- 'Failure' + (opt_message ? ': ' + opt_message : ''),
- Array.prototype.slice.call(arguments, 1));
- }
-};
+goog.Disposable.instances_ = {};
/**
- * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {number} The value, guaranteed to be a number when asserts enabled.
- * @throws {goog.asserts.AssertionError} When the value is not a number.
+ * @return {!Array.} All {@code goog.Disposable} objects that
+ * haven't been disposed of.
*/
-goog.asserts.assertNumber = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
- goog.asserts.doAssertFailure_('Expected number but got %s: %s.',
- [goog.typeOf(value), value], opt_message,
- Array.prototype.slice.call(arguments, 2));
+goog.Disposable.getUndisposedObjects = function() {
+ var ret = [];
+ for (var id in goog.Disposable.instances_) {
+ if (goog.Disposable.instances_.hasOwnProperty(id)) {
+ ret.push(goog.Disposable.instances_[Number(id)]);
+ }
}
- return /** @type {number} */ (value);
+ return ret;
};
/**
- * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {string} The value, guaranteed to be a string when asserts enabled.
- * @throws {goog.asserts.AssertionError} When the value is not a string.
+ * Clears the registry of undisposed objects but doesn't dispose of them.
*/
-goog.asserts.assertString = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
- goog.asserts.doAssertFailure_('Expected string but got %s: %s.',
- [goog.typeOf(value), value], opt_message,
- Array.prototype.slice.call(arguments, 2));
- }
- return /** @type {string} */ (value);
+goog.Disposable.clearUndisposedObjects = function() {
+ goog.Disposable.instances_ = {};
};
/**
- * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {!Function} The value, guaranteed to be a function when asserts
- * enabled.
- * @throws {goog.asserts.AssertionError} When the value is not a function.
+ * Whether the object has been disposed of.
+ * @type {boolean}
+ * @private
*/
-goog.asserts.assertFunction = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
- goog.asserts.doAssertFailure_('Expected function but got %s: %s.',
- [goog.typeOf(value), value], opt_message,
- Array.prototype.slice.call(arguments, 2));
- }
- return /** @type {!Function} */ (value);
-};
+goog.Disposable.prototype.disposed_ = false;
/**
- * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {!Object} The value, guaranteed to be a non-null object.
- * @throws {goog.asserts.AssertionError} When the value is not an object.
+ * Callbacks to invoke when this object is disposed.
+ * @type {Array.}
+ * @private
*/
-goog.asserts.assertObject = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
- goog.asserts.doAssertFailure_('Expected object but got %s: %s.',
- [goog.typeOf(value), value],
- opt_message, Array.prototype.slice.call(arguments, 2));
- }
- return /** @type {!Object} */ (value);
+goog.Disposable.prototype.onDisposeCallbacks_;
+
+
+/**
+ * If monitoring the goog.Disposable instances is enabled, stores the creation
+ * stack trace of the Disposable instance.
+ * @type {string}
+ */
+goog.Disposable.prototype.creationStack;
+
+
+/**
+ * @return {boolean} Whether the object has been disposed of.
+ * @override
+ */
+goog.Disposable.prototype.isDisposed = function() {
+ return this.disposed_;
};
/**
- * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {!Array} The value, guaranteed to be a non-null array.
- * @throws {goog.asserts.AssertionError} When the value is not an array.
+ * @return {boolean} Whether the object has been disposed of.
+ * @deprecated Use {@link #isDisposed} instead.
*/
-goog.asserts.assertArray = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
- goog.asserts.doAssertFailure_('Expected array but got %s: %s.',
- [goog.typeOf(value), value], opt_message,
- Array.prototype.slice.call(arguments, 2));
+goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
+
+
+/**
+ * Disposes of the object. If the object hasn't already been disposed of, calls
+ * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should
+ * override {@link #disposeInternal} in order to delete references to COM
+ * objects, DOM nodes, and other disposable objects. Reentrant.
+ *
+ * @return {void} Nothing.
+ * @override
+ */
+goog.Disposable.prototype.dispose = function() {
+ if (!this.disposed_) {
+ // Set disposed_ to true first, in case during the chain of disposal this
+ // gets disposed recursively.
+ this.disposed_ = true;
+ this.disposeInternal();
+ if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
+ var uid = goog.getUid(this);
+ if (goog.Disposable.MONITORING_MODE ==
+ goog.Disposable.MonitoringMode.PERMANENT &&
+ !goog.Disposable.instances_.hasOwnProperty(uid)) {
+ throw Error(this + ' did not call the goog.Disposable base ' +
+ 'constructor or was disposed of after a clearUndisposedObjects ' +
+ 'call');
+ }
+ delete goog.Disposable.instances_[uid];
+ }
}
- return /** @type {!Array} */ (value);
};
/**
- * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
- * @param {*} value The value to check.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @return {boolean} The value, guaranteed to be a boolean when asserts are
- * enabled.
- * @throws {goog.asserts.AssertionError} When the value is not a boolean.
+ * Associates a disposable object with this object so that they will be disposed
+ * together.
+ * @param {goog.disposable.IDisposable} disposable that will be disposed when
+ * this object is disposed.
*/
-goog.asserts.assertBoolean = function(value, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
- goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.',
- [goog.typeOf(value), value], opt_message,
- Array.prototype.slice.call(arguments, 2));
+goog.Disposable.prototype.registerDisposable = function(disposable) {
+ this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));
+};
+
+
+/**
+ * Invokes a callback function when this object is disposed. Callbacks are
+ * invoked in the order in which they were added.
+ * @param {function(this:T):?} callback The callback function.
+ * @param {T=} opt_scope An optional scope to call the callback in.
+ * @template T
+ */
+goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
+ if (!this.onDisposeCallbacks_) {
+ this.onDisposeCallbacks_ = [];
}
- return /** @type {boolean} */ (value);
+ this.onDisposeCallbacks_.push(goog.bind(callback, opt_scope));
};
/**
- * Checks if the value is an instance of the user-defined type if
- * goog.asserts.ENABLE_ASSERTS is true.
- *
- * The compiler may tighten the type returned by this function.
+ * Deletes or nulls out any references to COM objects, DOM nodes, or other
+ * disposable objects. Classes that extend {@code goog.Disposable} should
+ * override this method.
+ * Not reentrant. To avoid calling it twice, it must only be called from the
+ * subclass' {@code disposeInternal} method. Everywhere else the public
+ * {@code dispose} method must be used.
+ * For example:
+ *
+ * mypackage.MyClass = function() {
+ * goog.base(this);
+ * // Constructor logic specific to MyClass.
+ * ...
+ * };
+ * goog.inherits(mypackage.MyClass, goog.Disposable);
*
- * @param {*} value The value to check.
- * @param {!Function} type A user-defined constructor.
- * @param {string=} opt_message Error message in case of failure.
- * @param {...*} var_args The items to substitute into the failure message.
- * @throws {goog.asserts.AssertionError} When the value is not an instance of
- * type.
- * @return {!Object}
+ * mypackage.MyClass.prototype.disposeInternal = function() {
+ * // Dispose logic specific to MyClass.
+ * ...
+ * // Call superclass's disposeInternal at the end of the subclass's, like
+ * // in C++, to avoid hard-to-catch issues.
+ * goog.base(this, 'disposeInternal');
+ * };
+ *
+ * @protected
*/
-goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
- if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
- goog.asserts.doAssertFailure_('instanceof check failed.', null,
- opt_message, Array.prototype.slice.call(arguments, 3));
+goog.Disposable.prototype.disposeInternal = function() {
+ if (this.onDisposeCallbacks_) {
+ while (this.onDisposeCallbacks_.length) {
+ this.onDisposeCallbacks_.shift()();
+ }
+ }
+};
+
+
+/**
+ * Returns True if we can verify the object is disposed.
+ * Calls {@code isDisposed} on the argument if it supports it. If obj
+ * is not an object with an isDisposed() method, return false.
+ * @param {*} obj The object to investigate.
+ * @return {boolean} True if we can verify the object is disposed.
+ */
+goog.Disposable.isDisposed = function(obj) {
+ if (obj && typeof obj.isDisposed == 'function') {
+ return obj.isDisposed();
+ }
+ return false;
+};
+
+
+/**
+ * Calls {@code dispose} on the argument if it supports it. If obj is not an
+ * object with a dispose() method, this is a no-op.
+ * @param {*} obj The object to dispose of.
+ */
+goog.dispose = function(obj) {
+ if (obj && typeof obj.dispose == 'function') {
+ obj.dispose();
}
- return /** @type {!Object} */(value);
};
+
+/**
+ * Calls {@code dispose} on each member of the list that supports it. (If the
+ * member is an ArrayLike, then {@code goog.disposeAll()} will be called
+ * recursively on each of its members.) If the member is not an object with a
+ * {@code dispose()} method, then it is ignored.
+ * @param {...*} var_args The list.
+ */
+goog.disposeAll = function(var_args) {
+ for (var i = 0, len = arguments.length; i < len; ++i) {
+ var disposable = arguments[i];
+ if (goog.isArrayLike(disposable)) {
+ goog.disposeAll.apply(null, disposable);
+ } else {
+ goog.dispose(disposable);
+ }
+ }
+};
}
-if(!lt.util.load.provided_QMARK_('goog.debug.entryPointRegistry')) {
-// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.events.EventId')) {
+// Copyright 2013 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.
@@ -350,153 +359,202 @@ if(!lt.util.load.provided_QMARK_('goog.debug.entryPointRegistry')) {
// See the License for the specific language governing permissions and
// limitations under the License.
+goog.provide('goog.events.EventId');
+
+
+
/**
- * @fileoverview A global registry for entry points into a program,
- * so that they can be instrumented. Each module should register their
- * entry points with this registry. Designed to be compiled out
- * if no instrumentation is requested.
+ * A templated class that is used when registering for events. Typical usage:
+ *
+ * /** @type {goog.events.EventId.}
+ * var myEventId = new goog.events.EventId(
+ * goog.events.getUniqueId(('someEvent'));
*
- * Entry points may be registered before or after a call to
- * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
- * later, the existing monitor will instrument the new entry point.
+ * // No need to cast or declare here since the compiler knows the correct
+ * // type of 'evt' (MyEventObj).
+ * something.listen(myEventId, function(evt) {});
+ *
*
- * @author nicksantos@google.com (Nick Santos)
+ * @param {string} eventId
+ * @template T
+ * @constructor
+ * @struct
+ * @final
*/
+goog.events.EventId = function(eventId) {
+ /** @const */ this.id = eventId;
+};
-goog.provide('goog.debug.EntryPointMonitor');
-goog.provide('goog.debug.entryPointRegistry');
-goog.require('goog.asserts');
+/**
+ * @override
+ */
+goog.events.EventId.prototype.toString = function() {
+ return this.id;
+};
+}
+if(!lt.util.load.provided_QMARK_('goog.events.Event')) {
+// 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 A base class for event objects.
+ *
+ */
+goog.provide('goog.events.Event');
+goog.provide('goog.events.EventLike');
/**
- * @interface
+ * goog.events.Event no longer depends on goog.Disposable. Keep requiring
+ * goog.Disposable here to not break projects which assume this dependency.
+ * @suppress {extraRequire}
*/
-goog.debug.EntryPointMonitor = function() {};
+goog.require('goog.Disposable');
+goog.require('goog.events.EventId');
/**
- * Instruments a function.
- *
- * @param {!Function} fn A function to instrument.
- * @return {!Function} The instrumented function.
+ * A typedef for event like objects that are dispatchable via the
+ * goog.events.dispatchEvent function. strings are treated as the type for a
+ * goog.events.Event. Objects are treated as an extension of a new
+ * goog.events.Event with the type property of the object being used as the type
+ * of the Event.
+ * @typedef {string|Object|goog.events.Event|goog.events.EventId}
*/
-goog.debug.EntryPointMonitor.prototype.wrap;
+goog.events.EventLike;
+
/**
- * Try to remove an instrumentation wrapper created by this monitor.
- * If the function passed to unwrap is not a wrapper created by this
- * monitor, then we will do nothing.
- *
- * Notice that some wrappers may not be unwrappable. For example, if other
- * monitors have applied their own wrappers, then it will be impossible to
- * unwrap them because their wrappers will have captured our wrapper.
- *
- * So it is important that entry points are unwrapped in the reverse
- * order that they were wrapped.
+ * A base class for event objects, so that they can support preventDefault and
+ * stopPropagation.
*
- * @param {!Function} fn A function to unwrap.
- * @return {!Function} The unwrapped function, or {@code fn} if it was not
- * a wrapped function created by this monitor.
+ * @param {string|!goog.events.EventId} type Event Type.
+ * @param {Object=} opt_target Reference to the object that is the target of
+ * this event. It has to implement the {@code EventTarget} interface
+ * declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
+ * @constructor
*/
-goog.debug.EntryPointMonitor.prototype.unwrap;
+goog.events.Event = function(type, opt_target) {
+ /**
+ * Event type.
+ * @type {string}
+ */
+ this.type = type instanceof goog.events.EventId ? String(type) : type;
+
+ /**
+ * Target of the event.
+ * @type {Object|undefined}
+ */
+ this.target = opt_target;
+
+ /**
+ * Object that had the listener attached.
+ * @type {Object|undefined}
+ */
+ this.currentTarget = this.target;
+
+ /**
+ * Whether to cancel the event in internal capture/bubble processing for IE.
+ * @type {boolean}
+ * @public
+ * @suppress {underscore|visibility} Technically public, but referencing this
+ * outside this package is strongly discouraged.
+ */
+ this.propagationStopped_ = false;
+
+ /**
+ * Whether the default action has been prevented.
+ * This is a property to match the W3C specification at
+ * {@link http://www.w3.org/TR/DOM-Level-3-Events/
+ * #events-event-type-defaultPrevented}.
+ * Must be treated as read-only outside the class.
+ * @type {boolean}
+ */
+ this.defaultPrevented = false;
+
+ /**
+ * Return value for in internal capture/bubble processing for IE.
+ * @type {boolean}
+ * @public
+ * @suppress {underscore|visibility} Technically public, but referencing this
+ * outside this package is strongly discouraged.
+ */
+ this.returnValue_ = true;
+};
/**
- * An array of entry point callbacks.
- * @type {!Array.}
- * @private
+ * For backwards compatibility (goog.events.Event used to inherit
+ * goog.Disposable).
+ * @deprecated Events don't need to be disposed.
*/
-goog.debug.entryPointRegistry.refList_ = [];
+goog.events.Event.prototype.disposeInternal = function() {
+};
/**
- * Monitors that should wrap all the entry points.
- * @type {!Array.}
- * @private
+ * For backwards compatibility (goog.events.Event used to inherit
+ * goog.Disposable).
+ * @deprecated Events don't need to be disposed.
*/
-goog.debug.entryPointRegistry.monitors_ = [];
+goog.events.Event.prototype.dispose = function() {
+};
/**
- * Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
- * Checking this allows the compiler to optimize out the registrations.
- * @type {boolean}
- * @private
+ * Stops event propagation.
*/
-goog.debug.entryPointRegistry.monitorsMayExist_ = false;
+goog.events.Event.prototype.stopPropagation = function() {
+ this.propagationStopped_ = true;
+};
/**
- * Register an entry point with this module.
- *
- * The entry point will be instrumented when a monitor is passed to
- * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
- * entry point is instrumented immediately.
- *
- * @param {function(!Function)} callback A callback function which is called
- * with a transforming function to instrument the entry point. The callback
- * is responsible for wrapping the relevant entry point with the
- * transforming function.
+ * Prevents the default action, for example a link redirecting to a url.
*/
-goog.debug.entryPointRegistry.register = function(callback) {
- // Don't use push(), so that this can be compiled out.
- goog.debug.entryPointRegistry.refList_[
- goog.debug.entryPointRegistry.refList_.length] = callback;
- // If no one calls monitorAll, this can be compiled out.
- if (goog.debug.entryPointRegistry.monitorsMayExist_) {
- var monitors = goog.debug.entryPointRegistry.monitors_;
- for (var i = 0; i < monitors.length; i++) {
- callback(goog.bind(monitors[i].wrap, monitors[i]));
- }
- }
+goog.events.Event.prototype.preventDefault = function() {
+ this.defaultPrevented = true;
+ this.returnValue_ = false;
};
/**
- * Configures a monitor to wrap all entry points.
- *
- * Entry points that have already been registered are immediately wrapped by
- * the monitor. When an entry point is registered in the future, it will also
- * be wrapped by the monitor when it is registered.
- *
- * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
+ * Stops the propagation of the event. It is equivalent to
+ * {@code e.stopPropagation()}, but can be used as the callback argument of
+ * {@link goog.events.listen} without declaring another function.
+ * @param {!goog.events.Event} e An event.
*/
-goog.debug.entryPointRegistry.monitorAll = function(monitor) {
- goog.debug.entryPointRegistry.monitorsMayExist_ = true;
- var transformer = goog.bind(monitor.wrap, monitor);
- for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
- goog.debug.entryPointRegistry.refList_[i](transformer);
- }
- goog.debug.entryPointRegistry.monitors_.push(monitor);
+goog.events.Event.stopPropagation = function(e) {
+ e.stopPropagation();
};
/**
- * Try to unmonitor all the entry points that have already been registered. If
- * an entry point is registered in the future, it will not be wrapped by the
- * monitor when it is registered. Note that this may fail if the entry points
- * have additional wrapping.
- *
- * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
- * the entry points.
- * @throws {Error} If the monitor is not the most recently configured monitor.
+ * Prevents the default action. It is equivalent to
+ * {@code e.preventDefault()}, but can be used as the callback argument of
+ * {@link goog.events.listen} without declaring another function.
+ * @param {!goog.events.Event} e An event.
*/
-goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
- var monitors = goog.debug.entryPointRegistry.monitors_;
- goog.asserts.assert(monitor == monitors[monitors.length - 1],
- 'Only the most recent monitor can be unwrapped.');
- var transformer = goog.bind(monitor.unwrap, monitor);
- for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
- goog.debug.entryPointRegistry.refList_[i](transformer);
- }
- monitors.length--;
+goog.events.Event.preventDefault = function(e) {
+ e.preventDefault();
};
}
-if(!lt.util.load.provided_QMARK_('goog.disposable.IDisposable')) {
-// Copyright 2011 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.debug.Error')) {
+// Copyright 2009 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.
@@ -511,39 +569,98 @@ if(!lt.util.load.provided_QMARK_('goog.disposable.IDisposable')) {
// limitations under the License.
/**
- * @fileoverview Definition of the disposable interface. A disposable object
- * has a dispose method to to clean up references and resources.
- * @author nnaze@google.com (Nathan Naze)
+ * @fileoverview Provides a base class for custom Error objects such that the
+ * stack is correctly maintained.
+ *
+ * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is
+ * sufficient.
+ *
*/
-
-goog.provide('goog.disposable.IDisposable');
+goog.provide('goog.debug.Error');
/**
- * Interface for a disposable object. If a instance requires cleanup
- * (references COM objects, DOM notes, or other disposable objects), it should
- * implement this interface (it may subclass goog.Disposable).
- * @interface
+ * Base class for custom error objects.
+ * @param {*=} opt_msg The message associated with the error.
+ * @constructor
+ * @extends {Error}
*/
-goog.disposable.IDisposable = function() {};
+goog.debug.Error = function(opt_msg) {
+
+ // Attempt to ensure there is a stack trace.
+ if (Error.captureStackTrace) {
+ Error.captureStackTrace(this, goog.debug.Error);
+ } else {
+ var stack = new Error().stack;
+ if (stack) {
+ this.stack = stack;
+ }
+ }
+
+ if (opt_msg) {
+ this.message = String(opt_msg);
+ }
+};
+goog.inherits(goog.debug.Error, Error);
+
+/** @override */
+goog.debug.Error.prototype.name = 'CustomError';
+}
+if(!lt.util.load.provided_QMARK_('goog.dom.NodeType')) {
+// Copyright 2006 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.
/**
- * Disposes of the object and its resources.
- * @return {void} Nothing.
+ * @fileoverview Definition of goog.dom.NodeType.
*/
-goog.disposable.IDisposable.prototype.dispose;
+
+goog.provide('goog.dom.NodeType');
/**
- * @return {boolean} Whether the object has been disposed of.
+ * Constants for the nodeType attribute in the Node interface.
+ *
+ * These constants match those specified in the Node interface. These are
+ * usually present on the Node object in recent browsers, but not in older
+ * browsers (specifically, early IEs) and thus are given here.
+ *
+ * In some browsers (early IEs), these are not defined on the Node object,
+ * so they are provided here.
+ *
+ * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247
+ * @enum {number}
*/
-goog.disposable.IDisposable.prototype.isDisposed;
+goog.dom.NodeType = {
+ ELEMENT: 1,
+ ATTRIBUTE: 2,
+ TEXT: 3,
+ CDATA_SECTION: 4,
+ ENTITY_REFERENCE: 5,
+ ENTITY: 6,
+ PROCESSING_INSTRUCTION: 7,
+ COMMENT: 8,
+ DOCUMENT: 9,
+ DOCUMENT_TYPE: 10,
+ DOCUMENT_FRAGMENT: 11,
+ NOTATION: 12
+};
}
-if(!lt.util.load.provided_QMARK_('goog.Disposable')) {
-// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.asserts')) {
+// Copyright 2008 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.
@@ -558,279 +675,310 @@ if(!lt.util.load.provided_QMARK_('goog.Disposable')) {
// limitations under the License.
/**
- * @fileoverview Implements the disposable interface. The dispose method is used
- * to clean up references and resources.
- * @author arv@google.com (Erik Arvidsson)
+ * @fileoverview Utilities to check the preconditions, postconditions and
+ * invariants runtime.
+ *
+ * Methods in this package should be given special treatment by the compiler
+ * for type-inference. For example, goog.asserts.assert(foo)
+ * will restrict foo to a truthy value.
+ *
+ * The compiler has an option to disable asserts. So code like:
+ *
+ * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar());
+ *
+ * will be transformed into:
+ *
+ * var x = foo();
+ *
+ * The compiler will leave in foo() (because its return value is used),
+ * but it will remove bar() because it assumes it does not have side-effects.
+ *
*/
+goog.provide('goog.asserts');
+goog.provide('goog.asserts.AssertionError');
-goog.provide('goog.Disposable');
-goog.provide('goog.dispose');
+goog.require('goog.debug.Error');
+goog.require('goog.dom.NodeType');
+goog.require('goog.string');
-goog.require('goog.disposable.IDisposable');
+
+/**
+ * @define {boolean} Whether to strip out asserts or to leave them in.
+ */
+goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG);
/**
- * Class that provides the basic implementation for disposable objects. If your
- * class holds one or more references to COM objects, DOM nodes, or other
- * disposable objects, it should extend this class or implement the disposable
- * interface (defined in goog.disposable.IDisposable).
+ * Error object for failed assertions.
+ * @param {string} messagePattern The pattern that was used to form message.
+ * @param {!Array.<*>} messageArgs The items to substitute into the pattern.
* @constructor
- * @implements {goog.disposable.IDisposable}
+ * @extends {goog.debug.Error}
+ * @final
*/
-goog.Disposable = function() {
- if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
- this.creationStack = new Error().stack;
- goog.Disposable.instances_[goog.getUid(this)] = this;
- }
-};
-
+goog.asserts.AssertionError = function(messagePattern, messageArgs) {
+ messageArgs.unshift(messagePattern);
+ goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs));
+ // Remove the messagePattern afterwards to avoid permenantly modifying the
+ // passed in array.
+ messageArgs.shift();
-/**
- * @enum {number} Different monitoring modes for Disposable.
- */
-goog.Disposable.MonitoringMode = {
- /**
- * No monitoring.
- */
- OFF: 0,
- /**
- * Creating and disposing the goog.Disposable instances is monitored. All
- * disposable objects need to call the {@code goog.Disposable} base
- * constructor. The PERMANENT mode must bet switched on before creating any
- * goog.Disposable instances.
- */
- PERMANENT: 1,
/**
- * INTERACTIVE mode can be switched on and off on the fly without producing
- * errors. It also doesn't warn if the disposable objects don't call the
- * {@code goog.Disposable} base constructor.
+ * The message pattern used to format the error message. Error handlers can
+ * use this to uniquely identify the assertion.
+ * @type {string}
*/
- INTERACTIVE: 2
+ this.messagePattern = messagePattern;
};
+goog.inherits(goog.asserts.AssertionError, goog.debug.Error);
-/**
- * @define {number} The monitoring mode of the goog.Disposable
- * instances. Default is OFF. Switching on the monitoring is only
- * recommended for debugging because it has a significant impact on
- * performance and memory usage. If switched off, the monitoring code
- * compiles down to 0 bytes.
- */
-goog.Disposable.MONITORING_MODE = 0;
+/** @override */
+goog.asserts.AssertionError.prototype.name = 'AssertionError';
/**
- * Maps the unique ID of every undisposed {@code goog.Disposable} object to
- * the object itself.
- * @type {!Object.}
+ * Throws an exception with the given message and "Assertion failed" prefixed
+ * onto it.
+ * @param {string} defaultMessage The message to use if givenMessage is empty.
+ * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage.
+ * @param {string|undefined} givenMessage Message supplied by the caller.
+ * @param {Array.<*>} givenArgs The substitution arguments for givenMessage.
+ * @throws {goog.asserts.AssertionError} When the value is not a number.
* @private
*/
-goog.Disposable.instances_ = {};
-
-
-/**
- * @return {!Array.} All {@code goog.Disposable} objects that
- * haven't been disposed of.
- */
-goog.Disposable.getUndisposedObjects = function() {
- var ret = [];
- for (var id in goog.Disposable.instances_) {
- if (goog.Disposable.instances_.hasOwnProperty(id)) {
- ret.push(goog.Disposable.instances_[Number(id)]);
- }
+goog.asserts.doAssertFailure_ =
+ function(defaultMessage, defaultArgs, givenMessage, givenArgs) {
+ var message = 'Assertion failed';
+ if (givenMessage) {
+ message += ': ' + givenMessage;
+ var args = givenArgs;
+ } else if (defaultMessage) {
+ message += ': ' + defaultMessage;
+ args = defaultArgs;
}
- return ret;
+ // The '' + works around an Opera 10 bug in the unit tests. Without it,
+ // a stack trace is added to var message above. With this, a stack trace is
+ // not added until this line (it causes the extra garbage to be added after
+ // the assertion message instead of in the middle of it).
+ throw new goog.asserts.AssertionError('' + message, args || []);
};
/**
- * Clears the registry of undisposed objects but doesn't dispose of them.
+ * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
+ * true.
+ * @template T
+ * @param {T} condition The condition to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {T} The value of the condition.
+ * @throws {goog.asserts.AssertionError} When the condition evaluates to false.
*/
-goog.Disposable.clearUndisposedObjects = function() {
- goog.Disposable.instances_ = {};
+goog.asserts.assert = function(condition, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !condition) {
+ goog.asserts.doAssertFailure_('', null, opt_message,
+ Array.prototype.slice.call(arguments, 2));
+ }
+ return condition;
};
/**
- * Whether the object has been disposed of.
- * @type {boolean}
- * @private
+ * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case
+ * when we want to add a check in the unreachable area like switch-case
+ * statement:
+ *
+ *
+ * switch(type) {
+ * case FOO: doSomething(); break;
+ * case BAR: doSomethingElse(); break;
+ * default: goog.assert.fail('Unrecognized type: ' + type);
+ * // We have only 2 types - "default:" section is unreachable code.
+ * }
+ *
+ *
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @throws {goog.asserts.AssertionError} Failure.
*/
-goog.Disposable.prototype.disposed_ = false;
+goog.asserts.fail = function(opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS) {
+ throw new goog.asserts.AssertionError(
+ 'Failure' + (opt_message ? ': ' + opt_message : ''),
+ Array.prototype.slice.call(arguments, 1));
+ }
+};
/**
- * Callbacks to invoke when this object is disposed.
- * @type {Array.}
- * @private
+ * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {number} The value, guaranteed to be a number when asserts enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a number.
*/
-goog.Disposable.prototype.onDisposeCallbacks_;
+goog.asserts.assertNumber = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) {
+ goog.asserts.doAssertFailure_('Expected number but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {number} */ (value);
+};
/**
- * If monitoring the goog.Disposable instances is enabled, stores the creation
- * stack trace of the Disposable instance.
- * @type {string}
+ * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {string} The value, guaranteed to be a string when asserts enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a string.
*/
-goog.Disposable.prototype.creationStack;
+goog.asserts.assertString = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) {
+ goog.asserts.doAssertFailure_('Expected string but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {string} */ (value);
+};
/**
- * @return {boolean} Whether the object has been disposed of.
- * @override
+ * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Function} The value, guaranteed to be a function when asserts
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a function.
*/
-goog.Disposable.prototype.isDisposed = function() {
- return this.disposed_;
+goog.asserts.assertFunction = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) {
+ goog.asserts.doAssertFailure_('Expected function but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {!Function} */ (value);
};
/**
- * @return {boolean} Whether the object has been disposed of.
- * @deprecated Use {@link #isDisposed} instead.
+ * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Object} The value, guaranteed to be a non-null object.
+ * @throws {goog.asserts.AssertionError} When the value is not an object.
*/
-goog.Disposable.prototype.getDisposed = goog.Disposable.prototype.isDisposed;
+goog.asserts.assertObject = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) {
+ goog.asserts.doAssertFailure_('Expected object but got %s: %s.',
+ [goog.typeOf(value), value],
+ opt_message, Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {!Object} */ (value);
+};
/**
- * Disposes of the object. If the object hasn't already been disposed of, calls
- * {@link #disposeInternal}. Classes that extend {@code goog.Disposable} should
- * override {@link #disposeInternal} in order to delete references to COM
- * objects, DOM nodes, and other disposable objects. Reentrant.
- *
- * @return {void} Nothing.
- * @override
+ * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Array} The value, guaranteed to be a non-null array.
+ * @throws {goog.asserts.AssertionError} When the value is not an array.
*/
-goog.Disposable.prototype.dispose = function() {
- if (!this.disposed_) {
- // Set disposed_ to true first, in case during the chain of disposal this
- // gets disposed recursively.
- this.disposed_ = true;
- this.disposeInternal();
- if (goog.Disposable.MONITORING_MODE != goog.Disposable.MonitoringMode.OFF) {
- var uid = goog.getUid(this);
- if (goog.Disposable.MONITORING_MODE ==
- goog.Disposable.MonitoringMode.PERMANENT &&
- !goog.Disposable.instances_.hasOwnProperty(uid)) {
- throw Error(this + ' did not call the goog.Disposable base ' +
- 'constructor or was disposed of after a clearUndisposedObjects ' +
- 'call');
- }
- delete goog.Disposable.instances_[uid];
- }
+goog.asserts.assertArray = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) {
+ goog.asserts.doAssertFailure_('Expected array but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
}
+ return /** @type {!Array} */ (value);
};
/**
- * Associates a disposable object with this object so that they will be disposed
- * together.
- * @param {goog.disposable.IDisposable} disposable that will be disposed when
- * this object is disposed.
+ * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {boolean} The value, guaranteed to be a boolean when asserts are
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a boolean.
*/
-goog.Disposable.prototype.registerDisposable = function(disposable) {
- this.addOnDisposeCallback(goog.partial(goog.dispose, disposable));
+goog.asserts.assertBoolean = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) {
+ goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
+ }
+ return /** @type {boolean} */ (value);
};
/**
- * Invokes a callback function when this object is disposed. Callbacks are
- * invoked in the order in which they were added.
- * @param {function(this:T):?} callback The callback function.
- * @param {T=} opt_scope An optional scope to call the callback in.
- * @template T
+ * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true.
+ * @param {*} value The value to check.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @return {!Element} The value, likely to be a DOM Element when asserts are
+ * enabled.
+ * @throws {goog.asserts.AssertionError} When the value is not a boolean.
*/
-goog.Disposable.prototype.addOnDisposeCallback = function(callback, opt_scope) {
- if (!this.onDisposeCallbacks_) {
- this.onDisposeCallbacks_ = [];
+goog.asserts.assertElement = function(value, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) ||
+ value.nodeType != goog.dom.NodeType.ELEMENT)) {
+ goog.asserts.doAssertFailure_('Expected Element but got %s: %s.',
+ [goog.typeOf(value), value], opt_message,
+ Array.prototype.slice.call(arguments, 2));
}
- this.onDisposeCallbacks_.push(goog.bind(callback, opt_scope));
+ return /** @type {!Element} */ (value);
};
/**
- * Deletes or nulls out any references to COM objects, DOM nodes, or other
- * disposable objects. Classes that extend {@code goog.Disposable} should
- * override this method.
- * Not reentrant. To avoid calling it twice, it must only be called from the
- * subclass' {@code disposeInternal} method. Everywhere else the public
- * {@code dispose} method must be used.
- * For example:
- *
- * mypackage.MyClass = function() {
- * goog.base(this);
- * // Constructor logic specific to MyClass.
- * ...
- * };
- * goog.inherits(mypackage.MyClass, goog.Disposable);
+ * Checks if the value is an instance of the user-defined type if
+ * goog.asserts.ENABLE_ASSERTS is true.
*
- * mypackage.MyClass.prototype.disposeInternal = function() {
- * // Dispose logic specific to MyClass.
- * ...
- * // Call superclass's disposeInternal at the end of the subclass's, like
- * // in C++, to avoid hard-to-catch issues.
- * goog.base(this, 'disposeInternal');
- * };
- *
- * @protected
- */
-goog.Disposable.prototype.disposeInternal = function() {
- if (this.onDisposeCallbacks_) {
- while (this.onDisposeCallbacks_.length) {
- this.onDisposeCallbacks_.shift()();
- }
- }
-};
-
-
-/**
- * Returns True if we can verify the object is disposed.
- * Calls {@code isDisposed} on the argument if it supports it. If obj
- * is not an object with an isDisposed() method, return false.
- * @param {*} obj The object to investigate.
- * @return {boolean} True if we can verify the object is disposed.
- */
-goog.Disposable.isDisposed = function(obj) {
- if (obj && typeof obj.isDisposed == 'function') {
- return obj.isDisposed();
- }
- return false;
-};
-
-
-/**
- * Calls {@code dispose} on the argument if it supports it. If obj is not an
- * object with a dispose() method, this is a no-op.
- * @param {*} obj The object to dispose of.
+ * The compiler may tighten the type returned by this function.
+ *
+ * @param {*} value The value to check.
+ * @param {function(new: T, ...)} type A user-defined constructor.
+ * @param {string=} opt_message Error message in case of failure.
+ * @param {...*} var_args The items to substitute into the failure message.
+ * @throws {goog.asserts.AssertionError} When the value is not an instance of
+ * type.
+ * @return {!T}
+ * @template T
*/
-goog.dispose = function(obj) {
- if (obj && typeof obj.dispose == 'function') {
- obj.dispose();
+goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) {
+ if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) {
+ goog.asserts.doAssertFailure_('instanceof check failed.', null,
+ opt_message, Array.prototype.slice.call(arguments, 3));
}
+ return value;
};
/**
- * Calls {@code dispose} on each member of the list that supports it. (If the
- * member is an ArrayLike, then {@code goog.disposeAll()} will be called
- * recursively on each of its members.) If the member is not an object with a
- * {@code dispose()} method, then it is ignored.
- * @param {...*} var_args The list.
+ * Checks that no enumerable keys are present in Object.prototype. Such keys
+ * would break most code that use {@code for (var ... in ...)} loops.
*/
-goog.disposeAll = function(var_args) {
- for (var i = 0, len = arguments.length; i < len; ++i) {
- var disposable = arguments[i];
- if (goog.isArrayLike(disposable)) {
- goog.disposeAll.apply(null, disposable);
- } else {
- goog.dispose(disposable);
- }
+goog.asserts.assertObjectPrototypeIsIntact = function() {
+ for (var key in Object.prototype) {
+ goog.asserts.fail(key + ' should not be enumerable in Object.prototype.');
}
};
}
-if(!lt.util.load.provided_QMARK_('goog.events.Listener')) {
-// Copyright 2005 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.debug.entryPointRegistry')) {
+// Copyright 2010 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.
@@ -845,167 +993,285 @@ if(!lt.util.load.provided_QMARK_('goog.events.Listener')) {
// limitations under the License.
/**
- * @fileoverview Listener object.
- * @see ../demos/events.html
+ * @fileoverview A global registry for entry points into a program,
+ * so that they can be instrumented. Each module should register their
+ * entry points with this registry. Designed to be compiled out
+ * if no instrumentation is requested.
+ *
+ * Entry points may be registered before or after a call to
+ * goog.debug.entryPointRegistry.monitorAll. If an entry point is registered
+ * later, the existing monitor will instrument the new entry point.
+ *
+ * @author nicksantos@google.com (Nick Santos)
*/
-goog.provide('goog.events.Listener');
+goog.provide('goog.debug.EntryPointMonitor');
+goog.provide('goog.debug.entryPointRegistry');
-goog.require('goog.events.ListenableKey');
+goog.require('goog.asserts');
/**
- * Simple class that stores information about a listener
- * @implements {goog.events.ListenableKey}
- * @constructor
+ * @interface
*/
-goog.events.Listener = function() {
- if (goog.events.Listener.ENABLE_MONITORING) {
- this.creationStack = new Error().stack;
- }
-};
+goog.debug.EntryPointMonitor = function() {};
/**
- * @define {boolean} Whether to enable the monitoring of the
- * goog.events.Listener instances. Switching on the monitoring is only
- * recommended for debugging because it has a significant impact on
- * performance and memory usage. If switched off, the monitoring code
- * compiles down to 0 bytes.
+ * Instruments a function.
+ *
+ * @param {!Function} fn A function to instrument.
+ * @return {!Function} The instrumented function.
*/
-goog.events.Listener.ENABLE_MONITORING = false;
+goog.debug.EntryPointMonitor.prototype.wrap;
/**
- * Whether the listener is a function or an object that implements handleEvent.
- * @type {boolean}
- * @private
+ * Try to remove an instrumentation wrapper created by this monitor.
+ * If the function passed to unwrap is not a wrapper created by this
+ * monitor, then we will do nothing.
+ *
+ * Notice that some wrappers may not be unwrappable. For example, if other
+ * monitors have applied their own wrappers, then it will be impossible to
+ * unwrap them because their wrappers will have captured our wrapper.
+ *
+ * So it is important that entry points are unwrapped in the reverse
+ * order that they were wrapped.
+ *
+ * @param {!Function} fn A function to unwrap.
+ * @return {!Function} The unwrapped function, or {@code fn} if it was not
+ * a wrapped function created by this monitor.
*/
-goog.events.Listener.prototype.isFunctionListener_;
+goog.debug.EntryPointMonitor.prototype.unwrap;
/**
- * Call back function or an object with a handleEvent function.
- * @type {Function|Object|null}
+ * An array of entry point callbacks.
+ * @type {!Array.}
+ * @private
*/
-goog.events.Listener.prototype.listener;
+goog.debug.entryPointRegistry.refList_ = [];
/**
- * Proxy for callback that passes through {@link goog.events#HandleEvent_}
- * @type {Function}
+ * Monitors that should wrap all the entry points.
+ * @type {!Array.}
+ * @private
*/
-goog.events.Listener.prototype.proxy;
+goog.debug.entryPointRegistry.monitors_ = [];
/**
- * Object or node that callback is listening to
- * @type {Object|goog.events.Listenable|goog.events.EventTarget}
+ * Whether goog.debug.entryPointRegistry.monitorAll has ever been called.
+ * Checking this allows the compiler to optimize out the registrations.
+ * @type {boolean}
+ * @private
*/
-goog.events.Listener.prototype.src;
+goog.debug.entryPointRegistry.monitorsMayExist_ = false;
/**
- * Type of event
- * @type {string}
+ * Register an entry point with this module.
+ *
+ * The entry point will be instrumented when a monitor is passed to
+ * goog.debug.entryPointRegistry.monitorAll. If this has already occurred, the
+ * entry point is instrumented immediately.
+ *
+ * @param {function(!Function)} callback A callback function which is called
+ * with a transforming function to instrument the entry point. The callback
+ * is responsible for wrapping the relevant entry point with the
+ * transforming function.
*/
-goog.events.Listener.prototype.type;
+goog.debug.entryPointRegistry.register = function(callback) {
+ // Don't use push(), so that this can be compiled out.
+ goog.debug.entryPointRegistry.refList_[
+ goog.debug.entryPointRegistry.refList_.length] = callback;
+ // If no one calls monitorAll, this can be compiled out.
+ if (goog.debug.entryPointRegistry.monitorsMayExist_) {
+ var monitors = goog.debug.entryPointRegistry.monitors_;
+ for (var i = 0; i < monitors.length; i++) {
+ callback(goog.bind(monitors[i].wrap, monitors[i]));
+ }
+ }
+};
/**
- * Whether the listener is being called in the capture or bubble phase
- * @type {boolean}
+ * Configures a monitor to wrap all entry points.
+ *
+ * Entry points that have already been registered are immediately wrapped by
+ * the monitor. When an entry point is registered in the future, it will also
+ * be wrapped by the monitor when it is registered.
+ *
+ * @param {!goog.debug.EntryPointMonitor} monitor An entry point monitor.
*/
-goog.events.Listener.prototype.capture;
+goog.debug.entryPointRegistry.monitorAll = function(monitor) {
+ goog.debug.entryPointRegistry.monitorsMayExist_ = true;
+ var transformer = goog.bind(monitor.wrap, monitor);
+ for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
+ goog.debug.entryPointRegistry.refList_[i](transformer);
+ }
+ goog.debug.entryPointRegistry.monitors_.push(monitor);
+};
/**
- * Optional object whose context to execute the listener in
- * @type {Object|undefined}
+ * Try to unmonitor all the entry points that have already been registered. If
+ * an entry point is registered in the future, it will not be wrapped by the
+ * monitor when it is registered. Note that this may fail if the entry points
+ * have additional wrapping.
+ *
+ * @param {!goog.debug.EntryPointMonitor} monitor The last monitor to wrap
+ * the entry points.
+ * @throws {Error} If the monitor is not the most recently configured monitor.
*/
-goog.events.Listener.prototype.handler;
-
+goog.debug.entryPointRegistry.unmonitorAllIfPossible = function(monitor) {
+ var monitors = goog.debug.entryPointRegistry.monitors_;
+ goog.asserts.assert(monitor == monitors[monitors.length - 1],
+ 'Only the most recent monitor can be unwrapped.');
+ var transformer = goog.bind(monitor.unwrap, monitor);
+ for (var i = 0; i < goog.debug.entryPointRegistry.refList_.length; i++) {
+ goog.debug.entryPointRegistry.refList_[i](transformer);
+ }
+ monitors.length--;
+};
+}
+if(!lt.util.load.provided_QMARK_('goog.events.Listener')) {
+// 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.
/**
- * The key of the listener.
- * @type {number}
- * @override
+ * @fileoverview Listener object.
+ * @see ../demos/events.html
*/
-goog.events.Listener.prototype.key = 0;
-
-/**
- * Whether the listener has been removed.
- * @type {boolean}
- */
-goog.events.Listener.prototype.removed = false;
+goog.provide('goog.events.Listener');
+goog.require('goog.events.ListenableKey');
-/**
- * Whether to remove the listener after it has been called.
- * @type {boolean}
- */
-goog.events.Listener.prototype.callOnce = false;
/**
- * If monitoring the goog.events.Listener instances is enabled, stores the
- * creation stack trace of the Disposable instance.
- * @type {string}
- */
-goog.events.Listener.prototype.creationStack;
-
-
-/**
- * Initializes the listener.
- * @param {Function|Object} listener Callback function, or an object with a
- * handleEvent function.
+ * Simple class that stores information about a listener
+ * @param {!Function} listener Callback function.
* @param {Function} proxy Wrapper for the listener that patches the event.
- * @param {Object} src Source object for the event.
+ * @param {EventTarget|goog.events.Listenable} src Source object for
+ * the event.
* @param {string} type Event type.
* @param {boolean} capture Whether in capture or bubble phase.
* @param {Object=} opt_handler Object in whose context to execute the callback.
+ * @implements {goog.events.ListenableKey}
+ * @constructor
*/
-goog.events.Listener.prototype.init = function(listener, proxy, src, type,
- capture, opt_handler) {
- // we do the test of the listener here so that we do not need to
- // continiously do this inside handleEvent
- if (goog.isFunction(listener)) {
- this.isFunctionListener_ = true;
- } else if (listener && listener.handleEvent &&
- goog.isFunction(listener.handleEvent)) {
- this.isFunctionListener_ = false;
- } else {
- throw Error('Invalid listener argument');
+goog.events.Listener = function(
+ listener, proxy, src, type, capture, opt_handler) {
+ if (goog.events.Listener.ENABLE_MONITORING) {
+ this.creationStack = new Error().stack;
}
+ /**
+ * Callback function.
+ * @type {Function}
+ */
this.listener = listener;
+
+ /**
+ * A wrapper over the original listener. This is used solely to
+ * handle native browser events (it is used to simulate the capture
+ * phase and to patch the event object).
+ * @type {Function}
+ */
this.proxy = proxy;
+
+ /**
+ * Object or node that callback is listening to
+ * @type {EventTarget|goog.events.Listenable}
+ */
this.src = src;
+
+ /**
+ * The event type.
+ * @const {string}
+ */
this.type = type;
+
+ /**
+ * Whether the listener is being called in the capture or bubble phase
+ * @const {boolean}
+ */
this.capture = !!capture;
+
+ /**
+ * Optional object whose context to execute the listener in
+ * @type {Object|undefined}
+ */
this.handler = opt_handler;
- this.callOnce = false;
+
+ /**
+ * The key of the listener.
+ * @const {number}
+ * @override
+ */
this.key = goog.events.ListenableKey.reserveKey();
+
+ /**
+ * Whether to remove the listener after it has been called.
+ * @type {boolean}
+ */
+ this.callOnce = false;
+
+ /**
+ * Whether the listener has been removed.
+ * @type {boolean}
+ */
this.removed = false;
};
/**
- * Calls the internal listener
- * @param {Object} eventObject Event object to be passed to listener.
- * @return {boolean} The result of the internal listener call.
+ * @define {boolean} Whether to enable the monitoring of the
+ * goog.events.Listener instances. Switching on the monitoring is only
+ * recommended for debugging because it has a significant impact on
+ * performance and memory usage. If switched off, the monitoring code
+ * compiles down to 0 bytes.
*/
-goog.events.Listener.prototype.handleEvent = function(eventObject) {
- if (this.isFunctionListener_) {
- return this.listener.call(this.handler || this.src, eventObject);
- }
- return this.listener.handleEvent.call(this.listener, eventObject);
+goog.define('goog.events.Listener.ENABLE_MONITORING', false);
+
+
+/**
+ * If monitoring the goog.events.Listener instances is enabled, stores the
+ * creation stack trace of the Disposable instance.
+ * @type {string}
+ */
+goog.events.Listener.prototype.creationStack;
+
+
+/**
+ * Marks this listener as removed. This also remove references held by
+ * this listener object (such as listener and event source).
+ */
+goog.events.Listener.prototype.markAsRemoved = function() {
+ this.removed = true;
+ this.listener = null;
+ this.proxy = null;
+ this.src = null;
+ this.handler = null;
};
}
-if(!lt.util.load.provided_QMARK_('goog.reflect')) {
-// Copyright 2009 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.object')) {
+// Copyright 2006 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.
@@ -1020,539 +1286,631 @@ if(!lt.util.load.provided_QMARK_('goog.reflect')) {
// limitations under the License.
/**
- * @fileoverview Useful compiler idioms.
- *
+ * @fileoverview Utilities for manipulating objects/maps/hashes.
*/
-goog.provide('goog.reflect');
+goog.provide('goog.object');
/**
- * Syntax for object literal casts.
- * @see http://go/jscompiler-renaming
- * @see http://code.google.com/p/closure-compiler/wiki/
- * ExperimentalTypeBasedPropertyRenaming
- *
- * Use this if you have an object literal whose keys need to have the same names
- * as the properties of some class even after they are renamed by the compiler.
+ * Calls a function for each element in an object/map/hash.
*
- * @param {!Function} type Type to cast to.
- * @param {Object} object Object literal to cast.
- * @return {Object} The object literal.
+ * @param {Object.} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object.):?} f The function to call
+ * for every element. This function takes 3 arguments (the element, the
+ * index and the object) and the return value is ignored.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @template T,K,V
*/
-goog.reflect.object = function(type, object) {
- return object;
+goog.object.forEach = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ f.call(opt_obj, obj[key], key, obj);
+ }
};
/**
- * To assert to the compiler that an operation is needed when it would
- * otherwise be stripped. For example:
- *
- * // Force a layout
- * goog.reflect.sinkValue(dialog.offsetHeight);
- *
- * @type {!Function}
+ * Calls a function for each element in an object/map/hash. If that call returns
+ * true, adds the element to a new object.
+ *
+ * @param {Object.} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object.):boolean} f The function to call
+ * for every element. This
+ * function takes 3 arguments (the element, the index and the object)
+ * and should return a boolean. If the return value is true the
+ * element is added to the result object. If it is false the
+ * element is not included.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object.} a new object in which only elements that passed the
+ * test are present.
+ * @template T,K,V
*/
-goog.reflect.sinkValue = function(x) {
- goog.reflect.sinkValue[' '](x);
- return x;
+goog.object.filter = function(obj, f, opt_obj) {
+ var res = {};
+ for (var key in obj) {
+ if (f.call(opt_obj, obj[key], key, obj)) {
+ res[key] = obj[key];
+ }
+ }
+ return res;
};
/**
- * The compiler should optimize this function away iff no one ever uses
- * goog.reflect.sinkValue.
+ * For every element in an object/map/hash calls a function and inserts the
+ * result into a new object.
+ *
+ * @param {Object.} obj The object over which to iterate.
+ * @param {function(this:T,V,?,Object.):R} f The function to call
+ * for every element. This function
+ * takes 3 arguments (the element, the index and the object)
+ * and should return something. The result will be inserted
+ * into a new object.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {!Object.} a new object with the results from f.
+ * @template T,K,V,R
*/
-goog.reflect.sinkValue[' '] = goog.nullFunction;
+goog.object.map = function(obj, f, opt_obj) {
+ var res = {};
+ for (var key in obj) {
+ res[key] = f.call(opt_obj, obj[key], key, obj);
+ }
+ return res;
+};
/**
- * Check if a property can be accessed without throwing an exception.
- * @param {Object} obj The owner of the property.
- * @param {string} prop The property name.
- * @return {boolean} Whether the property is accessible. Will also return true
- * if obj is null.
+ * Calls a function for each element in an object/map/hash. If any
+ * call returns true, returns true (without checking the rest). If
+ * all calls return false, returns false.
+ *
+ * @param {Object.} obj The object to check.
+ * @param {function(this:T,V,?,Object.):boolean} f The function to
+ * call for every element. This function
+ * takes 3 arguments (the element, the index and the object) and should
+ * return a boolean.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {boolean} true if any element passes the test.
+ * @template T,K,V
*/
-goog.reflect.canAccessProperty = function(obj, prop) {
- /** @preserveTry */
- try {
- goog.reflect.sinkValue(obj[prop]);
- return true;
- } catch (e) {}
+goog.object.some = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ if (f.call(opt_obj, obj[key], key, obj)) {
+ return true;
+ }
+ }
return false;
};
-}
-if(!lt.util.load.provided_QMARK_('goog.events.EventLike')) {
-// 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 A base class for event objects.
+ * Calls a function for each element in an object/map/hash. If
+ * all calls return true, returns true. If any call returns false, returns
+ * false at this point and does not continue to check the remaining elements.
*
+ * @param {Object.} obj The object to check.
+ * @param {?function(this:T,V,?,Object.):boolean} f The function to
+ * call for every element. This function
+ * takes 3 arguments (the element, the index and the object) and should
+ * return a boolean.
+ * @param {T=} opt_obj This is used as the 'this' object within f.
+ * @return {boolean} false if any element fails the test.
+ * @template T,K,V
*/
+goog.object.every = function(obj, f, opt_obj) {
+ for (var key in obj) {
+ if (!f.call(opt_obj, obj[key], key, obj)) {
+ return false;
+ }
+ }
+ return true;
+};
-goog.provide('goog.events.Event');
-goog.provide('goog.events.EventLike');
-
-// goog.events.Event no longer depends on goog.Disposable. Keep requiring
-// goog.Disposable here to not break projects which assume this dependency.
-goog.require('goog.Disposable');
+/**
+ * Returns the number of key-value pairs in the object map.
+ *
+ * @param {Object} obj The object for which to get the number of key-value
+ * pairs.
+ * @return {number} The number of key-value pairs in the object map.
+ */
+goog.object.getCount = function(obj) {
+ // JS1.5 has __count__ but it has been deprecated so it raises a warning...
+ // in other words do not use. Also __count__ only includes the fields on the
+ // actual object and not in the prototype chain.
+ var rv = 0;
+ for (var key in obj) {
+ rv++;
+ }
+ return rv;
+};
/**
- * A typedef for event like objects that are dispatchable via the
- * goog.events.dispatchEvent function. strings are treated as the type for a
- * goog.events.Event. Objects are treated as an extension of a new
- * goog.events.Event with the type property of the object being used as the type
- * of the Event.
- * @typedef {string|Object|goog.events.Event}
+ * Returns one key from the object map, if any exists.
+ * For map literals the returned key will be the first one in most of the
+ * browsers (a know exception is Konqueror).
+ *
+ * @param {Object} obj The object to pick a key from.
+ * @return {string|undefined} The key or undefined if the object is empty.
*/
-goog.events.EventLike;
-
+goog.object.getAnyKey = function(obj) {
+ for (var key in obj) {
+ return key;
+ }
+};
/**
- * A base class for event objects, so that they can support preventDefault and
- * stopPropagation.
+ * Returns one value from the object map, if any exists.
+ * For map literals the returned value will be the first one in most of the
+ * browsers (a know exception is Konqueror).
*
- * @param {string} type Event Type.
- * @param {Object=} opt_target Reference to the object that is the target of
- * this event. It has to implement the {@code EventTarget} interface
- * declared at {@link http://developer.mozilla.org/en/DOM/EventTarget}.
- * @constructor
+ * @param {Object.} obj The object to pick a value from.
+ * @return {V|undefined} The value or undefined if the object is empty.
+ * @template K,V
*/
-goog.events.Event = function(type, opt_target) {
- /**
- * Event type.
- * @type {string}
- */
- this.type = type;
-
- /**
- * Target of the event.
- * @type {Object|undefined}
- */
- this.target = opt_target;
-
- /**
- * Object that had the listener attached.
- * @type {Object|undefined}
- */
- this.currentTarget = this.target;
+goog.object.getAnyValue = function(obj) {
+ for (var key in obj) {
+ return obj[key];
+ }
};
/**
- * For backwards compatibility (goog.events.Event used to inherit
- * goog.Disposable).
- * @deprecated Events don't need to be disposed.
+ * Whether the object/hash/map contains the given object as a value.
+ * An alias for goog.object.containsValue(obj, val).
+ *
+ * @param {Object.} obj The object in which to look for val.
+ * @param {V} val The object for which to check.
+ * @return {boolean} true if val is present.
+ * @template K,V
*/
-goog.events.Event.prototype.disposeInternal = function() {
+goog.object.contains = function(obj, val) {
+ return goog.object.containsValue(obj, val);
};
/**
- * For backwards compatibility (goog.events.Event used to inherit
- * goog.Disposable).
- * @deprecated Events don't need to be disposed.
+ * Returns the values of the object/map/hash.
+ *
+ * @param {Object.} obj The object from which to get the values.
+ * @return {!Array.} The values in the object/map/hash.
+ * @template K,V
*/
-goog.events.Event.prototype.dispose = function() {
+goog.object.getValues = function(obj) {
+ var res = [];
+ var i = 0;
+ for (var key in obj) {
+ res[i++] = obj[key];
+ }
+ return res;
};
/**
- * Whether to cancel the event in internal capture/bubble processing for IE.
- * @type {boolean}
- * @suppress {underscore} Technically public, but referencing this outside
- * this package is strongly discouraged.
+ * Returns the keys of the object/map/hash.
+ *
+ * @param {Object} obj The object from which to get the keys.
+ * @return {!Array.} Array of property keys.
*/
-goog.events.Event.prototype.propagationStopped_ = false;
+goog.object.getKeys = function(obj) {
+ var res = [];
+ var i = 0;
+ for (var key in obj) {
+ res[i++] = key;
+ }
+ return res;
+};
/**
- * Whether the default action has been prevented.
- * This is a property to match the W3C specification at {@link
- * http://www.w3.org/TR/DOM-Level-3-Events/#events-event-type-defaultPrevented}.
- * Must be treated as read-only outside the class.
- * @type {boolean}
+ * Get a value from an object multiple levels deep. This is useful for
+ * pulling values from deeply nested objects, such as JSON responses.
+ * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
+ *
+ * @param {!Object} obj An object to get the value from. Can be array-like.
+ * @param {...(string|number|!Array.)} var_args A number of keys
+ * (as strings, or numbers, for array-like objects). Can also be
+ * specified as a single array of keys.
+ * @return {*} The resulting value. If, at any point, the value for a key
+ * is undefined, returns undefined.
*/
-goog.events.Event.prototype.defaultPrevented = false;
+goog.object.getValueByKeys = function(obj, var_args) {
+ var isArrayLike = goog.isArrayLike(var_args);
+ var keys = isArrayLike ? var_args : arguments;
+ // Start with the 2nd parameter for the variable parameters syntax.
+ for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
+ obj = obj[keys[i]];
+ if (!goog.isDef(obj)) {
+ break;
+ }
+ }
-/**
- * Return value for in internal capture/bubble processing for IE.
- * @type {boolean}
- * @suppress {underscore} Technically public, but referencing this outside
- * this package is strongly discouraged.
- */
-goog.events.Event.prototype.returnValue_ = true;
+ return obj;
+};
/**
- * Stops event propagation.
+ * Whether the object/map/hash contains the given key.
+ *
+ * @param {Object} obj The object in which to look for key.
+ * @param {*} key The key for which to check.
+ * @return {boolean} true If the map contains the key.
*/
-goog.events.Event.prototype.stopPropagation = function() {
- this.propagationStopped_ = true;
+goog.object.containsKey = function(obj, key) {
+ return key in obj;
};
/**
- * Prevents the default action, for example a link redirecting to a url.
+ * Whether the object/map/hash contains the given value. This is O(n).
+ *
+ * @param {Object.} obj The object in which to look for val.
+ * @param {V} val The value for which to check.
+ * @return {boolean} true If the map contains the value.
+ * @template K,V
*/
-goog.events.Event.prototype.preventDefault = function() {
- this.defaultPrevented = true;
- this.returnValue_ = false;
+goog.object.containsValue = function(obj, val) {
+ for (var key in obj) {
+ if (obj[key] == val) {
+ return true;
+ }
+ }
+ return false;
};
/**
- * Stops the propagation of the event. It is equivalent to
- * {@code e.stopPropagation()}, but can be used as the callback argument of
- * {@link goog.events.listen} without declaring another function.
- * @param {!goog.events.Event} e An event.
+ * Searches an object for an element that satisfies the given condition and
+ * returns its key.
+ * @param {Object.} obj The object to search in.
+ * @param {function(this:T,V,string,Object.):boolean} f The
+ * function to call for every element. Takes 3 arguments (the value,
+ * the key and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
+ * @return {string|undefined} The key of an element for which the function
+ * returns true or undefined if no such element is found.
+ * @template T,K,V
*/
-goog.events.Event.stopPropagation = function(e) {
- e.stopPropagation();
+goog.object.findKey = function(obj, f, opt_this) {
+ for (var key in obj) {
+ if (f.call(opt_this, obj[key], key, obj)) {
+ return key;
+ }
+ }
+ return undefined;
};
/**
- * Prevents the default action. It is equivalent to
- * {@code e.preventDefault()}, but can be used as the callback argument of
- * {@link goog.events.listen} without declaring another function.
- * @param {!goog.events.Event} e An event.
+ * Searches an object for an element that satisfies the given condition and
+ * returns its value.
+ * @param {Object.} obj The object to search in.
+ * @param {function(this:T,V,string,Object.):boolean} f The function
+ * to call for every element. Takes 3 arguments (the value, the key
+ * and the object) and should return a boolean.
+ * @param {T=} opt_this An optional "this" context for the function.
+ * @return {V} The value of an element for which the function returns true or
+ * undefined if no such element is found.
+ * @template T,K,V
*/
-goog.events.Event.preventDefault = function(e) {
- e.preventDefault();
+goog.object.findValue = function(obj, f, opt_this) {
+ var key = goog.object.findKey(obj, f, opt_this);
+ return key && obj[key];
};
-}
-if(!lt.util.load.provided_QMARK_('goog.events.Listenable')) {
-// Copyright 2012 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 interface for a listenable JavaScript object.
+ * Whether the object/map/hash is empty.
*
- * WARNING(chrishenry): DO NOT USE! SUPPORT NOT FULLY IMPLEMENTED.
+ * @param {Object} obj The object to test.
+ * @return {boolean} true if obj is empty.
*/
-
-goog.provide('goog.events.Listenable');
-goog.provide('goog.events.ListenableKey');
-
-goog.require('goog.events.EventLike');
-
+goog.object.isEmpty = function(obj) {
+ for (var key in obj) {
+ return false;
+ }
+ return true;
+};
/**
- * A listenable interface. Also see goog.events.EventTarget.
- * @interface
+ * Removes all key value pairs from the object/map/hash.
+ *
+ * @param {Object} obj The object to clear.
*/
-goog.events.Listenable = function() {};
+goog.object.clear = function(obj) {
+ for (var i in obj) {
+ delete obj[i];
+ }
+};
/**
- * Whether to use the new listenable interface and mechanism in
- * goog.events and goog.events.EventTarget.
- *
- * TODO(user): Remove this once launched and stable.
+ * Removes a key-value pair based on the key.
*
- * @type {boolean}
+ * @param {Object} obj The object from which to remove the key.
+ * @param {*} key The key to remove.
+ * @return {boolean} Whether an element was removed.
*/
-goog.events.Listenable.USE_LISTENABLE_INTERFACE = false;
+goog.object.remove = function(obj, key) {
+ var rv;
+ if ((rv = key in obj)) {
+ delete obj[key];
+ }
+ return rv;
+};
/**
- * An expando property to indicate that an object implements
- * goog.events.Listenable.
- *
- * See addImplementation/isImplementedBy.
+ * Adds a key-value pair to the object. Throws an exception if the key is
+ * already in use. Use set if you want to change an existing pair.
*
- * @type {string}
- * @const
- * @private
+ * @param {Object.} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} val The value to add.
+ * @template K,V
*/
-goog.events.Listenable.IMPLEMENTED_BY_PROP_ = '__closure_listenable';
+goog.object.add = function(obj, key, val) {
+ if (key in obj) {
+ throw Error('The object already contains the key "' + key + '"');
+ }
+ goog.object.set(obj, key, val);
+};
/**
- * Marks a given class (constructor) as an implementation of
- * Listenable, do that we can query that fact at runtime. The class
- * must have already implemented the interface.
- * @param {!Function} cls The class constructor. The corresponding
- * class must have already implemented the interface.
+ * Returns the value for the given key.
+ *
+ * @param {Object.} obj The object from which to get the value.
+ * @param {string} key The key for which to get the value.
+ * @param {R=} opt_val The value to return if no item is found for the given
+ * key (default is undefined).
+ * @return {V|R|undefined} The value for the given key.
+ * @template K,V,R
*/
-goog.events.Listenable.addImplementation = function(cls) {
- cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP_] = true;
+goog.object.get = function(obj, key, opt_val) {
+ if (key in obj) {
+ return obj[key];
+ }
+ return opt_val;
};
/**
- * @param {Object} obj The object to check.
- * @return {boolean} Whether a given instance implements
- * Listenable. The class/superclass of the instance must call
- * addImplementation.
+ * Adds a key-value pair to the object/map/hash.
+ *
+ * @param {Object.} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} value The value to add.
+ * @template K,V
*/
-goog.events.Listenable.isImplementedBy = function(obj) {
- return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP_]);
+goog.object.set = function(obj, key, value) {
+ obj[key] = value;
};
/**
- * Adds an event listener. 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().
+ * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
*
- * @param {string} type Event type or array of event types.
- * @param {!Function} listener Callback method, or an object
- * with a handleEvent function.
- * @param {boolean=} opt_useCapture Whether to fire in capture phase
- * (defaults to false).
- * @param {Object=} opt_listenerScope Object in whose scope to call the
- * listener.
- * @return {goog.events.ListenableKey} Unique key for the listener.
+ * @param {Object.} obj The object to which to add the key-value pair.
+ * @param {string} key The key to add.
+ * @param {V} value The value to add if the key wasn't present.
+ * @return {V} The value of the entry at the end of the function.
+ * @template K,V
*/
-goog.events.Listenable.prototype.listen;
+goog.object.setIfUndefined = function(obj, key, value) {
+ return key in obj ? obj[key] : (obj[key] = value);
+};
/**
- * Adds an event listener that is removed automatically after the
- * listener fired once.
- *
- * 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).
+ * Does a flat clone of the object.
*
- * @param {string} type Event type or array of event types.
- * @param {!Function} listener Callback method, or an object
- * with a handleEvent function.
- * @param {boolean=} opt_useCapture Whether to fire in capture phase
- * (defaults to false).
- * @param {Object=} opt_listenerScope Object in whose scope to call the
- * listener.
- * @return {goog.events.ListenableKey} Unique key for the listener.
+ * @param {Object.} obj Object to clone.
+ * @return {!Object.} Clone of the input object.
+ * @template K,V
*/
-goog.events.Listenable.prototype.listenOnce;
+goog.object.clone = function(obj) {
+ // We cannot use the prototype trick because a lot of methods depend on where
+ // the actual key is set.
+
+ var res = {};
+ for (var key in obj) {
+ res[key] = obj[key];
+ }
+ return res;
+ // We could also use goog.mixin but I wanted this to be independent from that.
+};
/**
- * Removes an event listener which was added with listen() or listenOnce().
+ * Clones a value. The input may be an Object, Array, or basic type. Objects and
+ * arrays will be cloned recursively.
+ *
+ * WARNINGS:
+ * goog.object.unsafeClone does not detect reference loops. Objects
+ * that refer to themselves will cause infinite recursion.
*
- * Implementation needs to call goog.events.cleanUp.
+ * goog.object.unsafeClone is unaware of unique identifiers, and
+ * copies UIDs created by getUid into cloned results.
*
- * @param {string} type Event type or array of event types.
- * @param {!Function} listener Callback method, or an object
- * with a handleEvent function. TODO(user): Consider whether
- * we can remove Object.
- * @param {boolean=} opt_useCapture Whether to fire in capture phase
- * (defaults to false).
- * @param {Object=} opt_listenerScope Object in whose scope to call
- * the listener.
- * @return {boolean} Whether any listener was removed.
+ * @param {*} obj The value to clone.
+ * @return {*} A clone of the input value.
*/
-goog.events.Listenable.prototype.unlisten;
+goog.object.unsafeClone = function(obj) {
+ var type = goog.typeOf(obj);
+ if (type == 'object' || type == 'array') {
+ if (obj.clone) {
+ return obj.clone();
+ }
+ var clone = type == 'array' ? [] : {};
+ for (var key in obj) {
+ clone[key] = goog.object.unsafeClone(obj[key]);
+ }
+ return clone;
+ }
+
+ return obj;
+};
/**
- * Removes an event listener which was added with listen() by the key
- * returned by listen().
- *
- * Implementation needs to call goog.events.cleanUp.
+ * Returns a new object in which all the keys and values are interchanged
+ * (keys become values and values become keys). If multiple keys map to the
+ * same value, the chosen transposed value is implementation-dependent.
*
- * @param {goog.events.ListenableKey} key The key returned by
- * listen() or listenOnce().
- * @return {boolean} Whether any listener was removed.
+ * @param {Object} obj The object to transpose.
+ * @return {!Object} The transposed object.
*/
-goog.events.Listenable.prototype.unlistenByKey;
+goog.object.transpose = function(obj) {
+ var transposed = {};
+ for (var key in obj) {
+ transposed[obj[key]] = key;
+ }
+ return transposed;
+};
/**
- * 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.EventLike} e Event object.
- * @return {boolean} If anyone called preventDefault on the event object (or
- * if any of the listeners returns false this will also return false.
+ * The names of the fields that are defined on Object.prototype.
+ * @type {Array.}
+ * @private
*/
-goog.events.Listenable.prototype.dispatchEvent;
+goog.object.PROTOTYPE_FIELDS_ = [
+ 'constructor',
+ 'hasOwnProperty',
+ 'isPrototypeOf',
+ 'propertyIsEnumerable',
+ 'toLocaleString',
+ 'toString',
+ 'valueOf'
+];
/**
- * Removes all listeners from this listenable. If type is specified,
- * it will only remove listeners of the particular type. otherwise all
- * registered listeners will be removed.
+ * Extends an object with another object.
+ * This operates 'in-place'; it does not create a new Object.
*
- * Implementation needs to call goog.events.cleanUp for each removed
- * listener.
+ * Example:
+ * var o = {};
+ * goog.object.extend(o, {a: 0, b: 1});
+ * o; // {a: 0, b: 1}
+ * goog.object.extend(o, {b: 2, c: 3});
+ * o; // {a: 0, b: 2, c: 3}
*
- * @param {string=} opt_type Type of event to remove, default is to
- * remove all types.
- * @return {number} Number of listeners removed.
+ * @param {Object} target The object to modify. Existing properties will be
+ * overwritten if they are also present in one of the objects in
+ * {@code var_args}.
+ * @param {...Object} var_args The objects from which values will be copied.
*/
-goog.events.Listenable.prototype.removeAllListeners;
+goog.object.extend = function(target, var_args) {
+ var key, source;
+ for (var i = 1; i < arguments.length; i++) {
+ source = arguments[i];
+ for (key in source) {
+ target[key] = source[key];
+ }
+ // For IE the for-in-loop does not contain any properties that are not
+ // enumerable on the prototype object (for example isPrototypeOf from
+ // Object.prototype) and it will also not include 'replace' on objects that
+ // extend String and change 'replace' (not that it is common for anyone to
+ // extend anything except Object).
-/**
- * Fires all registered listeners in this listenable for the given
- * type and capture mode, passing them the given eventObject. This
- * does not perform actual capture/bubble. Only implementors of the
- * interface should be using this.
- *
- * @param {string} type The type of the listeners to fire.
- * @param {boolean} capture The capture mode of the listeners to fire.
- * @param {goog.events.Event} eventObject The event object to fire.
- * @return {boolean} Whether all listeners succeeded without
- * attempting to prevent default behavior. If any listener returns
- * false or called goog.events.Event#preventDefault, this returns
- * false.
- */
-goog.events.Listenable.prototype.fireListeners;
+ for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
+ key = goog.object.PROTOTYPE_FIELDS_[j];
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
+ target[key] = source[key];
+ }
+ }
+ }
+};
/**
- * Gets all listeners in this listenable for the given type and
- * capture mode.
- *
- * @param {string} type The type of the listeners to fire.
- * @param {boolean} capture The capture mode of the listeners to fire.
- * @return {!Array.} An array of registered
- * listeners.
+ * Creates a new object built from the key-value pairs provided as arguments.
+ * @param {...*} var_args If only one argument is provided and it is an array
+ * then this is used as the arguments, otherwise even arguments are used as
+ * the property names and odd arguments are used as the property values.
+ * @return {!Object} The new object.
+ * @throws {Error} If there are uneven number of arguments or there is only one
+ * non array argument.
*/
-goog.events.Listenable.prototype.getListeners;
-
+goog.object.create = function(var_args) {
+ var argLength = arguments.length;
+ if (argLength == 1 && goog.isArray(arguments[0])) {
+ return goog.object.create.apply(null, arguments[0]);
+ }
-/**
- * Gets the goog.events.ListenableKey for the event or null if no such
- * listener is in use.
- *
- * @param {string} type The name of the event without the 'on' prefix.
- * @param {!Function} listener The listener function to get.
- * @param {boolean=} capture Whether the listener is a capturing listener.
- * @param {Object=} opt_listenerScope Object in whose scope to call the
- * listener.
- * @return {goog.events.ListenableKey} the found listener or null if not found.
- */
-goog.events.Listenable.prototype.getListener;
+ if (argLength % 2) {
+ throw Error('Uneven number of arguments');
+ }
+ var rv = {};
+ for (var i = 0; i < argLength; i += 2) {
+ rv[arguments[i]] = arguments[i + 1];
+ }
+ return rv;
+};
/**
- * Whether there is 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 {string=} opt_type Event type.
- * @param {boolean=} opt_capture Whether to check for capture or bubble
- * listeners.
- * @return {boolean} Whether there is any active listeners matching
- * the requested type and/or capture phase.
+ * Creates a new object where the property names come from the arguments but
+ * the value is always set to true
+ * @param {...*} var_args If only one argument is provided and it is an array
+ * then this is used as the arguments, otherwise the arguments are used
+ * as the property names.
+ * @return {!Object} The new object.
*/
-goog.events.Listenable.prototype.hasListener;
-
-
+goog.object.createSet = function(var_args) {
+ var argLength = arguments.length;
+ if (argLength == 1 && goog.isArray(arguments[0])) {
+ return goog.object.createSet.apply(null, arguments[0]);
+ }
-/**
- * An interface that describes a single registered listener.
- * @interface
- */
-goog.events.ListenableKey = function() {};
+ var rv = {};
+ for (var i = 0; i < argLength; i++) {
+ rv[arguments[i]] = true;
+ }
+ return rv;
+};
/**
- * Counter used to create a unique key
- * @type {number}
- * @private
- */
-goog.events.ListenableKey.counter_ = 0;
-
-
-/**
- * Reserves a key to be used for ListenableKey#key field.
- * @return {number} A number to be used to fill ListenableKey#key
- * field.
+ * Creates an immutable view of the underlying object, if the browser
+ * supports immutable objects.
+ *
+ * In default mode, writes to this view will fail silently. In strict mode,
+ * they will throw an error.
+ *
+ * @param {!Object.} obj An object.
+ * @return {!Object.} An immutable view of that object, or the
+ * original object if this browser does not support immutables.
+ * @template K,V
*/
-goog.events.ListenableKey.reserveKey = function() {
- return ++goog.events.ListenableKey.counter_;
+goog.object.createImmutableView = function(obj) {
+ var result = obj;
+ if (Object.isFrozen && !Object.isFrozen(obj)) {
+ result = Object.create(obj);
+ Object.freeze(result);
+ }
+ return result;
};
/**
- * The source event target.
- * @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
- */
-goog.events.ListenableKey.prototype.src;
-
-
-/**
- * The event type the listener is listening to.
- * @type {string}
- */
-goog.events.ListenableKey.prototype.type;
-
-
-/**
- * The listener function.
- * TODO(user): Narrow the type if possible.
- * @type {Function|Object}
- */
-goog.events.ListenableKey.prototype.listener;
-
-
-/**
- * Whether the listener works on capture phase.
- * @type {boolean}
- */
-goog.events.ListenableKey.prototype.capture;
-
-
-/**
- * The 'this' object for the listener function's scope.
- * @type {Object}
- */
-goog.events.ListenableKey.prototype.handler;
-
-
-/**
- * A globally unique number to identify the key.
- * @type {number}
+ * @param {!Object} obj An object.
+ * @return {boolean} Whether this is an immutable view of the object.
*/
-goog.events.ListenableKey.prototype.key;
+goog.object.isImmutableView = function(obj) {
+ return !!Object.isFrozen && Object.isFrozen(obj);
+};
}
-if(!lt.util.load.provided_QMARK_('goog.userAgent')) {
-// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.events.ListenerMap')) {
+// Copyright 2013 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.
@@ -1567,569 +1925,722 @@ if(!lt.util.load.provided_QMARK_('goog.userAgent')) {
// limitations under the License.
/**
- * @fileoverview Rendering engine detection.
- * @see User agent strings
- * For information on the browser brand (such as Safari versus Chrome), see
- * goog.userAgent.product.
- * @see ../demos/useragent.html
+ * @fileoverview A map of listeners that provides utility functions to
+ * deal with listeners on an event target. Used by
+ * {@code goog.events.EventTarget}.
+ *
+ * WARNING: Do not use this class from outside goog.events package.
+ *
+ * @visibility {//closure/goog/bin/sizetests:__pkg__}
+ * @visibility {//closure/goog/events:__pkg__}
+ * @visibility {//closure/goog/labs/events:__pkg__}
*/
-goog.provide('goog.userAgent');
+goog.provide('goog.events.ListenerMap');
+
+goog.require('goog.array');
+goog.require('goog.events.Listener');
+goog.require('goog.object');
-goog.require('goog.string');
/**
- * @define {boolean} Whether we know at compile-time that the browser is IE.
+ * Creates a new listener map.
+ * @param {EventTarget|goog.events.Listenable} src The src object.
+ * @constructor
+ * @final
*/
-goog.userAgent.ASSUME_IE = false;
+goog.events.ListenerMap = function(src) {
+ /** @type {EventTarget|goog.events.Listenable} */
+ this.src = src;
+ /**
+ * Maps of event type to an array of listeners.
+ * @type {Object.>}
+ */
+ this.listeners = {};
-/**
- * @define {boolean} Whether we know at compile-time that the browser is GECKO.
- */
-goog.userAgent.ASSUME_GECKO = false;
+ /**
+ * The count of types in this map that have registered listeners.
+ * @private {number}
+ */
+ this.typeCount_ = 0;
+};
/**
- * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
+ * @return {number} The count of event types in this map that actually
+ * have registered listeners.
*/
-goog.userAgent.ASSUME_WEBKIT = false;
+goog.events.ListenerMap.prototype.getTypeCount = function() {
+ return this.typeCount_;
+};
/**
- * @define {boolean} Whether we know at compile-time that the browser is a
- * mobile device running WebKit e.g. iPhone or Android.
+ * @return {number} Total number of registered listeners.
*/
-goog.userAgent.ASSUME_MOBILE_WEBKIT = false;
+goog.events.ListenerMap.prototype.getListenerCount = function() {
+ var count = 0;
+ for (var type in this.listeners) {
+ count += this.listeners[type].length;
+ }
+ return count;
+};
/**
- * @define {boolean} Whether we know at compile-time that the browser is OPERA.
+ * Adds an event listener. 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 {string|!goog.events.EventId} type The listener event type.
+ * @param {!Function} listener This listener callback method.
+ * @param {boolean} callOnce Whether the listener is a one-off
+ * listener.
+ * @param {boolean=} opt_useCapture The capture mode of the listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} Unique key for the listener.
*/
-goog.userAgent.ASSUME_OPERA = false;
+goog.events.ListenerMap.prototype.add = function(
+ type, listener, callOnce, opt_useCapture, opt_listenerScope) {
+ var typeStr = type.toString();
+ var listenerArray = this.listeners[typeStr];
+ if (!listenerArray) {
+ listenerArray = this.listeners[typeStr] = [];
+ this.typeCount_++;
+ }
+
+ var listenerObj;
+ var index = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, opt_useCapture, opt_listenerScope);
+ if (index > -1) {
+ listenerObj = listenerArray[index];
+ if (!callOnce) {
+ // Ensure that, if there is an existing callOnce listener, it is no
+ // longer a callOnce listener.
+ listenerObj.callOnce = false;
+ }
+ } else {
+ listenerObj = new goog.events.Listener(
+ listener, null, this.src, typeStr, !!opt_useCapture, opt_listenerScope);
+ listenerObj.callOnce = callOnce;
+ listenerArray.push(listenerObj);
+ }
+ return listenerObj;
+};
/**
- * @define {boolean} Whether the {@code goog.userAgent.isVersion} function will
- * return true for any version.
+ * Removes a matching listener.
+ * @param {string|!goog.events.EventId} type The listener event type.
+ * @param {!Function} listener This listener callback method.
+ * @param {boolean=} opt_useCapture The capture mode of the listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {boolean} Whether any listener was removed.
*/
-goog.userAgent.ASSUME_ANY_VERSION = false;
+goog.events.ListenerMap.prototype.remove = function(
+ type, listener, opt_useCapture, opt_listenerScope) {
+ var typeStr = type.toString();
+ if (!(typeStr in this.listeners)) {
+ return false;
+ }
+
+ var listenerArray = this.listeners[typeStr];
+ var index = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, opt_useCapture, opt_listenerScope);
+ if (index > -1) {
+ var listenerObj = listenerArray[index];
+ listenerObj.markAsRemoved();
+ goog.array.removeAt(listenerArray, index);
+ if (listenerArray.length == 0) {
+ delete this.listeners[typeStr];
+ this.typeCount_--;
+ }
+ return true;
+ }
+ return false;
+};
/**
- * Whether we know the browser engine at compile-time.
- * @type {boolean}
- * @private
+ * Removes the given listener object.
+ * @param {goog.events.ListenableKey} listener The listener to remove.
+ * @return {boolean} Whether the listener is removed.
*/
-goog.userAgent.BROWSER_KNOWN_ =
- goog.userAgent.ASSUME_IE ||
- goog.userAgent.ASSUME_GECKO ||
- goog.userAgent.ASSUME_MOBILE_WEBKIT ||
- goog.userAgent.ASSUME_WEBKIT ||
- goog.userAgent.ASSUME_OPERA;
+goog.events.ListenerMap.prototype.removeByKey = function(listener) {
+ var type = listener.type;
+ if (!(type in this.listeners)) {
+ return false;
+ }
+
+ var removed = goog.array.remove(this.listeners[type], listener);
+ if (removed) {
+ listener.markAsRemoved();
+ if (this.listeners[type].length == 0) {
+ delete this.listeners[type];
+ this.typeCount_--;
+ }
+ }
+ return removed;
+};
/**
- * Returns the userAgent string for the current browser.
- * Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a
- * navigator object off the global scope. In that case we return null.
- *
- * @return {?string} The userAgent string or null if there is none.
+ * Removes all listeners from this map. If opt_type is provided, only
+ * listeners that match the given type are removed.
+ * @param {string|!goog.events.EventId=} opt_type Type of event to remove.
+ * @return {number} Number of listeners removed.
*/
-goog.userAgent.getUserAgentString = function() {
- return goog.global['navigator'] ? goog.global['navigator'].userAgent : null;
+goog.events.ListenerMap.prototype.removeAll = function(opt_type) {
+ var typeStr = opt_type && opt_type.toString();
+ var count = 0;
+ for (var type in this.listeners) {
+ if (!typeStr || type == typeStr) {
+ var listenerArray = this.listeners[type];
+ for (var i = 0; i < listenerArray.length; i++) {
+ ++count;
+ listenerArray[i].markAsRemoved();
+ }
+ delete this.listeners[type];
+ this.typeCount_--;
+ }
+ }
+ return count;
};
/**
- * @return {Object} The native navigator object.
+ * Gets all listeners that match the given type and capture mode. The
+ * returned array is a copy (but the listener objects are not).
+ * @param {string|!goog.events.EventId} type The type of the listeners
+ * to retrieve.
+ * @param {boolean} capture The capture mode of the listeners to retrieve.
+ * @return {!Array.} An array of matching
+ * listeners.
*/
-goog.userAgent.getNavigator = function() {
- // Need a local navigator reference instead of using the global one,
- // to avoid the rare case where they reference different objects.
- // (in a WorkerPool, for example).
- return goog.global['navigator'];
+goog.events.ListenerMap.prototype.getListeners = function(type, capture) {
+ var listenerArray = this.listeners[type.toString()];
+ var rv = [];
+ if (listenerArray) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ var listenerObj = listenerArray[i];
+ if (listenerObj.capture == capture) {
+ rv.push(listenerObj);
+ }
+ }
+ }
+ return rv;
};
/**
- * Initializer for goog.userAgent.
+ * Gets the goog.events.ListenableKey for the event or null if no such
+ * listener is in use.
*
- * This is a named function so that it can be stripped via the jscompiler
- * option for stripping types.
- * @private
+ * @param {string|!goog.events.EventId} type The type of the listener
+ * to retrieve.
+ * @param {!Function} listener The listener function to get.
+ * @param {boolean} capture Whether the listener is a capturing listener.
+ * @param {Object=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} the found listener or null if not found.
*/
-goog.userAgent.init_ = function() {
- /**
- * Whether the user agent string denotes Opera.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedOpera_ = false;
-
- /**
- * Whether the user agent string denotes Internet Explorer. This includes
- * other browsers using Trident as its rendering engine. For example AOL
- * and Netscape 8
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedIe_ = false;
-
- /**
- * Whether the user agent string denotes WebKit. WebKit is the rendering
- * engine that Safari, Android and others use.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedWebkit_ = false;
-
- /**
- * Whether the user agent string denotes a mobile device.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedMobile_ = false;
-
- /**
- * Whether the user agent string denotes Gecko. Gecko is the rendering
- * engine used by Mozilla, Mozilla Firefox, Camino and many more.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedGecko_ = false;
-
- var ua;
- if (!goog.userAgent.BROWSER_KNOWN_ &&
- (ua = goog.userAgent.getUserAgentString())) {
- var navigator = goog.userAgent.getNavigator();
- goog.userAgent.detectedOpera_ = ua.indexOf('Opera') == 0;
- goog.userAgent.detectedIe_ = !goog.userAgent.detectedOpera_ &&
- ua.indexOf('MSIE') != -1;
- goog.userAgent.detectedWebkit_ = !goog.userAgent.detectedOpera_ &&
- ua.indexOf('WebKit') != -1;
- // WebKit also gives navigator.product string equal to 'Gecko'.
- goog.userAgent.detectedMobile_ = goog.userAgent.detectedWebkit_ &&
- ua.indexOf('Mobile') != -1;
- goog.userAgent.detectedGecko_ = !goog.userAgent.detectedOpera_ &&
- !goog.userAgent.detectedWebkit_ && navigator.product == 'Gecko';
+goog.events.ListenerMap.prototype.getListener = function(
+ type, listener, capture, opt_listenerScope) {
+ var listenerArray = this.listeners[type.toString()];
+ var i = -1;
+ if (listenerArray) {
+ i = goog.events.ListenerMap.findListenerIndex_(
+ listenerArray, listener, capture, opt_listenerScope);
}
+ return i > -1 ? listenerArray[i] : null;
};
-if (!goog.userAgent.BROWSER_KNOWN_) {
- goog.userAgent.init_();
-}
-
-
/**
- * Whether the user agent is Opera.
- * @type {boolean}
+ * Whether there is a matching listener. If either the type or capture
+ * parameters are unspecified, the function will match on the
+ * remaining criteria.
+ *
+ * @param {string|!goog.events.EventId=} opt_type The type of the listener.
+ * @param {boolean=} opt_capture The capture mode of the listener.
+ * @return {boolean} Whether there is an active listener matching
+ * the requested type and/or capture phase.
*/
-goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
- goog.userAgent.ASSUME_OPERA : goog.userAgent.detectedOpera_;
+goog.events.ListenerMap.prototype.hasListener = function(
+ opt_type, opt_capture) {
+ var hasType = goog.isDef(opt_type);
+ var typeStr = hasType ? opt_type.toString() : '';
+ var hasCapture = goog.isDef(opt_capture);
+
+ return goog.object.some(
+ this.listeners, function(listenerArray, type) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ if ((!hasType || listenerArray[i].type == typeStr) &&
+ (!hasCapture || listenerArray[i].capture == opt_capture)) {
+ return true;
+ }
+ }
+
+ return false;
+ });
+};
/**
- * Whether the user agent is Internet Explorer. This includes other browsers
- * using Trident as its rendering engine. For example AOL and Netscape 8
- * @type {boolean}
+ * Finds the index of a matching goog.events.Listener in the given
+ * listenerArray.
+ * @param {!Array.} listenerArray Array of listener.
+ * @param {!Function} listener The listener function.
+ * @param {boolean=} opt_useCapture The capture flag for the listener.
+ * @param {Object=} opt_listenerScope The listener scope.
+ * @return {number} The index of the matching listener within the
+ * listenerArray.
+ * @private
*/
-goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
- goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;
-
+goog.events.ListenerMap.findListenerIndex_ = function(
+ listenerArray, listener, opt_useCapture, opt_listenerScope) {
+ for (var i = 0; i < listenerArray.length; ++i) {
+ var listenerObj = listenerArray[i];
+ if (!listenerObj.removed &&
+ listenerObj.listener == listener &&
+ listenerObj.capture == !!opt_useCapture &&
+ listenerObj.handler == opt_listenerScope) {
+ return i;
+ }
+ }
+ return -1;
+};
+}
+if(!lt.util.load.provided_QMARK_('goog.reflect')) {
+// Copyright 2009 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.
/**
- * Whether the user agent is Gecko. Gecko is the rendering engine used by
- * Mozilla, Mozilla Firefox, Camino and many more.
- * @type {boolean}
+ * @fileoverview Useful compiler idioms.
+ *
*/
-goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
- goog.userAgent.ASSUME_GECKO :
- goog.userAgent.detectedGecko_;
+
+goog.provide('goog.reflect');
/**
- * Whether the user agent is WebKit. WebKit is the rendering engine that
- * Safari, Android and others use.
- * @type {boolean}
+ * Syntax for object literal casts.
+ * @see http://go/jscompiler-renaming
+ * @see http://code.google.com/p/closure-compiler/wiki/
+ * ExperimentalTypeBasedPropertyRenaming
+ *
+ * Use this if you have an object literal whose keys need to have the same names
+ * as the properties of some class even after they are renamed by the compiler.
+ *
+ * @param {!Function} type Type to cast to.
+ * @param {Object} object Object literal to cast.
+ * @return {Object} The object literal.
*/
-goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
- goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
- goog.userAgent.detectedWebkit_;
+goog.reflect.object = function(type, object) {
+ return object;
+};
/**
- * Whether the user agent is running on a mobile device.
- * @type {boolean}
+ * To assert to the compiler that an operation is needed when it would
+ * otherwise be stripped. For example:
+ *
+ * // Force a layout
+ * goog.reflect.sinkValue(dialog.offsetHeight);
+ *
+ * @type {!Function}
*/
-goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
- goog.userAgent.detectedMobile_;
+goog.reflect.sinkValue = function(x) {
+ goog.reflect.sinkValue[' '](x);
+ return x;
+};
/**
- * Used while transitioning code to use WEBKIT instead.
- * @type {boolean}
- * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
- * TODO(nicksantos): Delete this from goog.userAgent.
+ * The compiler should optimize this function away iff no one ever uses
+ * goog.reflect.sinkValue.
*/
-goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
+goog.reflect.sinkValue[' '] = goog.nullFunction;
/**
- * @return {string} the platform (operating system) the user agent is running
- * on. Default to empty string because navigator.platform may not be defined
- * (on Rhino, for example).
- * @private
+ * Check if a property can be accessed without throwing an exception.
+ * @param {Object} obj The owner of the property.
+ * @param {string} prop The property name.
+ * @return {boolean} Whether the property is accessible. Will also return true
+ * if obj is null.
*/
-goog.userAgent.determinePlatform_ = function() {
- var navigator = goog.userAgent.getNavigator();
- return navigator && navigator.platform || '';
+goog.reflect.canAccessProperty = function(obj, prop) {
+ /** @preserveTry */
+ try {
+ goog.reflect.sinkValue(obj[prop]);
+ return true;
+ } catch (e) {}
+ return false;
};
+}
+if(!lt.util.load.provided_QMARK_('goog.events.Listenable')) {
+// Copyright 2012 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 interface for a listenable JavaScript object.
+ */
+
+goog.provide('goog.events.Listenable');
+goog.provide('goog.events.ListenableKey');
+
+/** @suppress {extraRequire} */
+goog.require('goog.events.EventId');
+
/**
- * The platform (operating system) the user agent is running on. Default to
- * empty string because navigator.platform may not be defined (on Rhino, for
- * example).
+ * A listenable interface. A listenable is an object with the ability
+ * to dispatch/broadcast events to "event listeners" registered via
+ * listen/listenOnce.
+ *
+ * The interface allows for an event propagation mechanism similar
+ * to one offered by native browser event targets, such as
+ * capture/bubble mechanism, stopping propagation, and preventing
+ * default actions. Capture/bubble mechanism depends on the ancestor
+ * tree constructed via {@code #getParentEventTarget}; this tree
+ * must be directed acyclic graph. The meaning of default action(s)
+ * in preventDefault is specific to a particular use case.
+ *
+ * Implementations that do not support capture/bubble or can not have
+ * a parent listenable can simply not implement any ability to set the
+ * parent listenable (and have {@code #getParentEventTarget} return
+ * null).
+ *
+ * Implementation of this class can be used with or independently from
+ * goog.events.
+ *
+ * Implementation must call {@code #addImplementation(implClass)}.
+ *
+ * @interface
+ * @see goog.events
+ * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html
+ */
+goog.events.Listenable = function() {};
+
+
+/**
+ * An expando property to indicate that an object implements
+ * goog.events.Listenable.
+ *
+ * See addImplementation/isImplementedBy.
+ *
* @type {string}
+ * @const
*/
-goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
+goog.events.Listenable.IMPLEMENTED_BY_PROP =
+ 'closure_listenable_' + ((Math.random() * 1e6) | 0);
/**
- * @define {boolean} Whether the user agent is running on a Macintosh operating
- * system.
+ * Marks a given class (constructor) as an implementation of
+ * Listenable, do that we can query that fact at runtime. The class
+ * must have already implemented the interface.
+ * @param {!Function} cls The class constructor. The corresponding
+ * class must have already implemented the interface.
*/
-goog.userAgent.ASSUME_MAC = false;
+goog.events.Listenable.addImplementation = function(cls) {
+ cls.prototype[goog.events.Listenable.IMPLEMENTED_BY_PROP] = true;
+};
/**
- * @define {boolean} Whether the user agent is running on a Windows operating
- * system.
+ * @param {Object} obj The object to check.
+ * @return {boolean} Whether a given instance implements
+ * Listenable. The class/superclass of the instance must call
+ * addImplementation.
*/
-goog.userAgent.ASSUME_WINDOWS = false;
+goog.events.Listenable.isImplementedBy = function(obj) {
+ try {
+ return !!(obj && obj[goog.events.Listenable.IMPLEMENTED_BY_PROP]);
+ } catch (e) {
+ return false;
+ }
+};
/**
- * @define {boolean} Whether the user agent is running on a Linux operating
- * system.
+ * Adds an event listener. 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 {string|!goog.events.EventId.} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} Unique key for the listener.
+ * @template SCOPE,EVENTOBJ
*/
-goog.userAgent.ASSUME_LINUX = false;
+goog.events.Listenable.prototype.listen;
/**
- * @define {boolean} Whether the user agent is running on a X11 windowing
- * system.
+ * Adds an event listener that is removed automatically after the
+ * listener fired once.
+ *
+ * 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 {string|!goog.events.EventId.} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} Unique key for the listener.
+ * @template SCOPE,EVENTOBJ
*/
-goog.userAgent.ASSUME_X11 = false;
+goog.events.Listenable.prototype.listenOnce;
/**
- * @define {boolean} Whether the user agent is running on Android.
+ * Removes an event listener which was added with listen() or listenOnce().
+ *
+ * @param {string|!goog.events.EventId.} type The event type id.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener Callback
+ * method.
+ * @param {boolean=} opt_useCapture Whether to fire in capture phase
+ * (defaults to false).
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call
+ * the listener.
+ * @return {boolean} Whether any listener was removed.
+ * @template SCOPE,EVENTOBJ
*/
-goog.userAgent.ASSUME_ANDROID = false;
+goog.events.Listenable.prototype.unlisten;
/**
- * @define {boolean} Whether the user agent is running on an iPhone.
+ * Removes an event listener which was added with listen() by the key
+ * returned by listen().
+ *
+ * @param {goog.events.ListenableKey} key The key returned by
+ * listen() or listenOnce().
+ * @return {boolean} Whether any listener was removed.
*/
-goog.userAgent.ASSUME_IPHONE = false;
+goog.events.Listenable.prototype.unlistenByKey;
/**
- * @define {boolean} Whether the user agent is running on an iPad.
+ * 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.EventLike} e Event object.
+ * @return {boolean} If anyone called preventDefault on the event object (or
+ * if any of the listeners returns false) this will also return false.
*/
-goog.userAgent.ASSUME_IPAD = false;
+goog.events.Listenable.prototype.dispatchEvent;
/**
- * @type {boolean}
- * @private
+ * Removes all listeners from this listenable. If type is specified,
+ * it will only remove listeners of the particular type. otherwise all
+ * registered listeners will be removed.
+ *
+ * @param {string=} opt_type Type of event to remove, default is to
+ * remove all types.
+ * @return {number} Number of listeners removed.
*/
-goog.userAgent.PLATFORM_KNOWN_ =
- goog.userAgent.ASSUME_MAC ||
- goog.userAgent.ASSUME_WINDOWS ||
- goog.userAgent.ASSUME_LINUX ||
- goog.userAgent.ASSUME_X11 ||
- goog.userAgent.ASSUME_ANDROID ||
- goog.userAgent.ASSUME_IPHONE ||
- goog.userAgent.ASSUME_IPAD;
+goog.events.Listenable.prototype.removeAllListeners;
/**
- * Initialize the goog.userAgent constants that define which platform the user
- * agent is running on.
- * @private
+ * Returns the parent of this event target to use for capture/bubble
+ * mechanism.
+ *
+ * NOTE(user): The name reflects the original implementation of
+ * custom event target ({@code goog.events.EventTarget}). We decided
+ * that changing the name is not worth it.
+ *
+ * @return {goog.events.Listenable} The parent EventTarget or null if
+ * there is no parent.
*/
-goog.userAgent.initPlatform_ = function() {
- /**
- * Whether the user agent is running on a Macintosh operating system.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,
- 'Mac');
-
- /**
- * Whether the user agent is running on a Windows operating system.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedWindows_ = goog.string.contains(
- goog.userAgent.PLATFORM, 'Win');
-
- /**
- * Whether the user agent is running on a Linux operating system.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,
- 'Linux');
-
- /**
- * Whether the user agent is running on a X11 windowing system.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&
- goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',
- 'X11');
-
- // Need user agent string for Android/IOS detection
- var ua = goog.userAgent.getUserAgentString();
-
- /**
- * Whether the user agent is running on Android.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedAndroid_ = !!ua && ua.indexOf('Android') >= 0;
-
- /**
- * Whether the user agent is running on an iPhone.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedIPhone_ = !!ua && ua.indexOf('iPhone') >= 0;
-
- /**
- * Whether the user agent is running on an iPad.
- * @type {boolean}
- * @private
- */
- goog.userAgent.detectedIPad_ = !!ua && ua.indexOf('iPad') >= 0;
-};
-
-
-if (!goog.userAgent.PLATFORM_KNOWN_) {
- goog.userAgent.initPlatform_();
-}
-
-
-/**
- * Whether the user agent is running on a Macintosh operating system.
- * @type {boolean}
- */
-goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;
+goog.events.Listenable.prototype.getParentEventTarget;
/**
- * Whether the user agent is running on a Windows operating system.
- * @type {boolean}
+ * Fires all registered listeners in this listenable for the given
+ * type and capture mode, passing them the given eventObject. This
+ * does not perform actual capture/bubble. Only implementors of the
+ * interface should be using this.
+ *
+ * @param {string|!goog.events.EventId.} type The type of the
+ * listeners to fire.
+ * @param {boolean} capture The capture mode of the listeners to fire.
+ * @param {EVENTOBJ} eventObject The event object to fire.
+ * @return {boolean} Whether all listeners succeeded without
+ * attempting to prevent default behavior. If any listener returns
+ * false or called goog.events.Event#preventDefault, this returns
+ * false.
+ * @template EVENTOBJ
*/
-goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;
+goog.events.Listenable.prototype.fireListeners;
/**
- * Whether the user agent is running on a Linux operating system.
- * @type {boolean}
+ * Gets all listeners in this listenable for the given type and
+ * capture mode.
+ *
+ * @param {string|!goog.events.EventId} type The type of the listeners to fire.
+ * @param {boolean} capture The capture mode of the listeners to fire.
+ * @return {!Array.} An array of registered
+ * listeners.
+ * @template EVENTOBJ
*/
-goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;
+goog.events.Listenable.prototype.getListeners;
/**
- * Whether the user agent is running on a X11 windowing system.
- * @type {boolean}
+ * Gets the goog.events.ListenableKey for the event or null if no such
+ * listener is in use.
+ *
+ * @param {string|!goog.events.EventId.} type The name of the event
+ * without the 'on' prefix.
+ * @param {function(this:SCOPE, EVENTOBJ):(boolean|undefined)} listener The
+ * listener function to get.
+ * @param {boolean} capture Whether the listener is a capturing listener.
+ * @param {SCOPE=} opt_listenerScope Object in whose scope to call the
+ * listener.
+ * @return {goog.events.ListenableKey} the found listener or null if not found.
+ * @template SCOPE,EVENTOBJ
*/
-goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;
+goog.events.Listenable.prototype.getListener;
/**
- * Whether the user agent is running on Android.
- * @type {boolean}
+ * Whether there is 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 {string|!goog.events.EventId.=} opt_type Event type.
+ * @param {boolean=} opt_capture Whether to check for capture or bubble
+ * listeners.
+ * @return {boolean} Whether there is any active listeners matching
+ * the requested type and/or capture phase.
+ * @template EVENTOBJ
*/
-goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;
-
+goog.events.Listenable.prototype.hasListener;
-/**
- * Whether the user agent is running on an iPhone.
- * @type {boolean}
- */
-goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;
/**
- * Whether the user agent is running on an iPad.
- * @type {boolean}
+ * An interface that describes a single registered listener.
+ * @interface
*/
-goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
- goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;
+goog.events.ListenableKey = function() {};
/**
- * @return {string} The string that describes the version number of the user
- * agent.
+ * Counter used to create a unique key
+ * @type {number}
* @private
*/
-goog.userAgent.determineVersion_ = function() {
- // All browsers have different ways to detect the version and they all have
- // different naming schemes.
-
- // version is a string rather than a number because it may contain 'b', 'a',
- // and so on.
- var version = '', re;
-
- if (goog.userAgent.OPERA && goog.global['opera']) {
- var operaVersion = goog.global['opera'].version;
- version = typeof operaVersion == 'function' ? operaVersion() : operaVersion;
- } else {
- if (goog.userAgent.GECKO) {
- re = /rv\:([^\);]+)(\)|;)/;
- } else if (goog.userAgent.IE) {
- re = /MSIE\s+([^\);]+)(\)|;)/;
- } else if (goog.userAgent.WEBKIT) {
- // WebKit/125.4
- re = /WebKit\/(\S+)/;
- }
- if (re) {
- var arr = re.exec(goog.userAgent.getUserAgentString());
- version = arr ? arr[1] : '';
- }
- }
- if (goog.userAgent.IE) {
- // IE9 can be in document mode 9 but be reporting an inconsistent user agent
- // version. If it is identifying as a version lower than 9 we take the
- // documentMode as the version instead. IE8 has similar behavior.
- // It is recommended to set the X-UA-Compatible header to ensure that IE9
- // uses documentMode 9.
- var docMode = goog.userAgent.getDocumentMode_();
- if (docMode > parseFloat(version)) {
- return String(docMode);
- }
- }
- return version;
-};
+goog.events.ListenableKey.counter_ = 0;
/**
- * @return {number|undefined} Returns the document mode (for testing).
- * @private
+ * Reserves a key to be used for ListenableKey#key field.
+ * @return {number} A number to be used to fill ListenableKey#key
+ * field.
*/
-goog.userAgent.getDocumentMode_ = function() {
- // NOTE(user): goog.userAgent may be used in context where there is no DOM.
- var doc = goog.global['document'];
- return doc ? doc['documentMode'] : undefined;
+goog.events.ListenableKey.reserveKey = function() {
+ return ++goog.events.ListenableKey.counter_;
};
/**
- * The version of the user agent. This is a string because it might contain
- * 'b' (as in beta) as well as multiple dots.
- * @type {string}
+ * The source event target.
+ * @type {!(Object|goog.events.Listenable|goog.events.EventTarget)}
*/
-goog.userAgent.VERSION = goog.userAgent.determineVersion_();
+goog.events.ListenableKey.prototype.src;
/**
- * Compares two version numbers.
- *
- * @param {string} v1 Version of first item.
- * @param {string} v2 Version of second item.
- *
- * @return {number} 1 if first argument is higher
- * 0 if arguments are equal
- * -1 if second argument is higher.
- * @deprecated Use goog.string.compareVersions.
+ * The event type the listener is listening to.
+ * @type {string}
*/
-goog.userAgent.compare = function(v1, v2) {
- return goog.string.compareVersions(v1, v2);
-};
+goog.events.ListenableKey.prototype.type;
/**
- * Cache for {@link goog.userAgent.isVersion}. Calls to compareVersions are
- * surprisingly expensive and as a browsers version number is unlikely to change
- * during a session we cache the results.
- * @type {Object}
- * @private
+ * The listener function.
+ * @type {function(?):?|{handleEvent:function(?):?}|null}
*/
-goog.userAgent.isVersionCache_ = {};
+goog.events.ListenableKey.prototype.listener;
/**
- * Whether the user agent version is higher or the same as the given version.
- * NOTE: When checking the version numbers for Firefox and Safari, be sure to
- * use the engine's version, not the browser's version number. For example,
- * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
- * Opera and Internet Explorer versions match the product release number.
- * @see
- * Webkit
- * @see Gecko
- *
- * @param {string|number} version The version to check.
- * @return {boolean} Whether the user agent version is higher or the same as
- * the given version.
+ * Whether the listener works on capture phase.
+ * @type {boolean}
*/
-goog.userAgent.isVersion = function(version) {
- return goog.userAgent.ASSUME_ANY_VERSION ||
- goog.userAgent.isVersionCache_[version] ||
- (goog.userAgent.isVersionCache_[version] =
- goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
-};
+goog.events.ListenableKey.prototype.capture;
/**
- * Whether the IE effective document mode is higher or the same as the given
- * document mode version.
- * NOTE: Only for IE, return false for another browser.
- *
- * @param {number} documentMode The document mode version to check.
- * @return {boolean} Whether the IE effective document mode is higher or the
- * same as the given version.
+ * The 'this' object for the listener function's scope.
+ * @type {Object}
*/
-goog.userAgent.isDocumentMode = function(documentMode) {
- return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
-};
+goog.events.ListenableKey.prototype.handler;
/**
- * For IE version < 7, documentMode is undefined, so attempt to use the
- * CSS1Compat property to see if we are in standards mode. If we are in
- * standards mode, treat the browser version as the document mode. Otherwise,
- * IE is emulating version 5.
- * @type {number|undefined}
- * @const
+ * A globally unique number to identify the key.
+ * @type {number}
*/
-goog.userAgent.DOCUMENT_MODE = (function() {
- var doc = goog.global['document'];
- if (!doc || !goog.userAgent.IE) {
- return undefined;
- }
- var mode = goog.userAgent.getDocumentMode_();
- return mode || (doc['compatMode'] == 'CSS1Compat' ?
- parseInt(goog.userAgent.VERSION, 10) : 5);
-})();
+goog.events.ListenableKey.prototype.key;
}
-if(!lt.util.load.provided_QMARK_('goog.events.BrowserFeature')) {
-// Copyright 2010 The Closure Library Authors. All Rights Reserved.
+if(!lt.util.load.provided_QMARK_('goog.userAgent')) {
+// Copyright 2006 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.
@@ -2144,1386 +2655,1287 @@ if(!lt.util.load.provided_QMARK_('goog.events.BrowserFeature')) {
// limitations under the License.
/**
- * @fileoverview Browser capability checks for the events package.
- *
+ * @fileoverview Rendering engine detection.
+ * @see User agent strings
+ * For information on the browser brand (such as Safari versus Chrome), see
+ * goog.userAgent.product.
+ * @see ../demos/useragent.html
*/
+goog.provide('goog.userAgent');
-goog.provide('goog.events.BrowserFeature');
+goog.require('goog.string');
-goog.require('goog.userAgent');
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is IE.
+ */
+goog.define('goog.userAgent.ASSUME_IE', false);
/**
- * Enum of browser capabilities.
- * @enum {boolean}
+ * @define {boolean} Whether we know at compile-time that the browser is GECKO.
*/
-goog.events.BrowserFeature = {
- /**
- * Whether the button attribute of the event is W3C compliant. False in
- * Internet Explorer prior to version 9; document-version dependent.
- */
- HAS_W3C_BUTTON: !goog.userAgent.IE || goog.userAgent.isDocumentMode(9),
+goog.define('goog.userAgent.ASSUME_GECKO', false);
- /**
- * Whether the browser supports full W3C event model.
- */
- HAS_W3C_EVENT_SUPPORT: !goog.userAgent.IE || goog.userAgent.isDocumentMode(9),
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is WEBKIT.
+ */
+goog.define('goog.userAgent.ASSUME_WEBKIT', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is a
+ * mobile device running WebKit e.g. iPhone or Android.
+ */
+goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false);
+
+
+/**
+ * @define {boolean} Whether we know at compile-time that the browser is OPERA.
+ */
+goog.define('goog.userAgent.ASSUME_OPERA', false);
+
+
+/**
+ * @define {boolean} Whether the
+ * {@code goog.userAgent.isVersionOrHigher}
+ * function will return true for any version.
+ */
+goog.define('goog.userAgent.ASSUME_ANY_VERSION', false);
+
+
+/**
+ * Whether we know the browser engine at compile-time.
+ * @type {boolean}
+ * @private
+ */
+goog.userAgent.BROWSER_KNOWN_ =
+ goog.userAgent.ASSUME_IE ||
+ goog.userAgent.ASSUME_GECKO ||
+ goog.userAgent.ASSUME_MOBILE_WEBKIT ||
+ goog.userAgent.ASSUME_WEBKIT ||
+ goog.userAgent.ASSUME_OPERA;
+
+
+/**
+ * Returns the userAgent string for the current browser.
+ * Some user agents (I'm thinking of you, Gears WorkerPool) do not expose a
+ * navigator object off the global scope. In that case we return null.
+ *
+ * @return {?string} The userAgent string or null if there is none.
+ */
+goog.userAgent.getUserAgentString = function() {
+ return goog.global['navigator'] ? goog.global['navigator'].userAgent : null;
+};
+
+
+/**
+ * @return {Object} The native navigator object.
+ */
+goog.userAgent.getNavigator = function() {
+ // Need a local navigator reference instead of using the global one,
+ // to avoid the rare case where they reference different objects.
+ // (in a WorkerPool, for example).
+ return goog.global['navigator'];
+};
+
+
+/**
+ * Initializer for goog.userAgent.
+ *
+ * This is a named function so that it can be stripped via the jscompiler
+ * option for stripping types.
+ * @private
+ */
+goog.userAgent.init_ = function() {
/**
- * To prevent default in IE7-8 for certain keydown events we need set the
- * keyCode to -1.
+ * Whether the user agent string denotes Opera.
+ * @type {boolean}
+ * @private
*/
- SET_KEY_CODE_TO_PREVENT_DEFAULT: goog.userAgent.IE &&
- !goog.userAgent.isVersion('9'),
+ goog.userAgent.detectedOpera_ = false;
/**
- * Whether the {@code navigator.onLine} property is supported.
+ * Whether the user agent string denotes Internet Explorer. This includes
+ * other browsers using Trident as its rendering engine. For example AOL
+ * and Netscape 8
+ * @type {boolean}
+ * @private
*/
- HAS_NAVIGATOR_ONLINE_PROPERTY: !goog.userAgent.WEBKIT ||
- goog.userAgent.isVersion('528'),
+ goog.userAgent.detectedIe_ = false;
/**
- * Whether HTML5 network online/offline events are supported.
+ * Whether the user agent string denotes WebKit. WebKit is the rendering
+ * engine that Safari, Android and others use.
+ * @type {boolean}
+ * @private
*/
- HAS_HTML5_NETWORK_EVENT_SUPPORT:
- goog.userAgent.GECKO && goog.userAgent.isVersion('1.9b') ||
- goog.userAgent.IE && goog.userAgent.isVersion('8') ||
- goog.userAgent.OPERA && goog.userAgent.isVersion('9.5') ||
- goog.userAgent.WEBKIT && goog.userAgent.isVersion('528'),
+ goog.userAgent.detectedWebkit_ = false;
/**
- * Whether HTML5 network events fire on document.body, or otherwise the
- * window.
+ * Whether the user agent string denotes a mobile device.
+ * @type {boolean}
+ * @private
*/
- HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
- goog.userAgent.GECKO && !goog.userAgent.isVersion('8') ||
- goog.userAgent.IE && !goog.userAgent.isVersion('9'),
+ goog.userAgent.detectedMobile_ = false;
/**
- * Whether touch is enabled in the browser.
+ * Whether the user agent string denotes Gecko. Gecko is the rendering
+ * engine used by Mozilla, Mozilla Firefox, Camino and many more.
+ * @type {boolean}
+ * @private
*/
- TOUCH_ENABLED:
- ('ontouchstart' in goog.global ||
- !!(goog.global['document'] &&
- document.documentElement &&
- 'ontouchstart' in document.documentElement) ||
- // IE10 uses non-standard touch events, so it has a different check.
- !!(goog.global['navigator'] &&
- goog.global['navigator']['msMaxTouchPoints']))
+ goog.userAgent.detectedGecko_ = false;
+
+ var ua;
+ if (!goog.userAgent.BROWSER_KNOWN_ &&
+ (ua = goog.userAgent.getUserAgentString())) {
+ var navigator = goog.userAgent.getNavigator();
+ goog.userAgent.detectedOpera_ = goog.string.startsWith(ua, 'Opera');
+ goog.userAgent.detectedIe_ = !goog.userAgent.detectedOpera_ &&
+ (goog.string.contains(ua, 'MSIE') ||
+ goog.string.contains(ua, 'Trident'));
+ goog.userAgent.detectedWebkit_ = !goog.userAgent.detectedOpera_ &&
+ goog.string.contains(ua, 'WebKit');
+ // WebKit also gives navigator.product string equal to 'Gecko'.
+ goog.userAgent.detectedMobile_ = goog.userAgent.detectedWebkit_ &&
+ goog.string.contains(ua, 'Mobile');
+ goog.userAgent.detectedGecko_ = !goog.userAgent.detectedOpera_ &&
+ !goog.userAgent.detectedWebkit_ && !goog.userAgent.detectedIe_ &&
+ navigator.product == 'Gecko';
+ }
};
+
+
+if (!goog.userAgent.BROWSER_KNOWN_) {
+ goog.userAgent.init_();
}
-if(!lt.util.load.provided_QMARK_('goog.events.EventType')) {
-// Copyright 2010 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 Event Types.
- *
- * @author arv@google.com (Erik Arvidsson)
- * @author mirkov@google.com (Mirko Visontai)
+ * Whether the user agent is Opera.
+ * @type {boolean}
*/
+goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_OPERA : goog.userAgent.detectedOpera_;
-goog.provide('goog.events.EventType');
+/**
+ * Whether the user agent is Internet Explorer. This includes other browsers
+ * using Trident as its rendering engine. For example AOL and Netscape 8
+ * @type {boolean}
+ */
+goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_IE : goog.userAgent.detectedIe_;
-goog.require('goog.userAgent');
+
+/**
+ * Whether the user agent is Gecko. Gecko is the rendering engine used by
+ * Mozilla, Mozilla Firefox, Camino and many more.
+ * @type {boolean}
+ */
+goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_GECKO :
+ goog.userAgent.detectedGecko_;
/**
- * Constants for event names.
- * @enum {string}
+ * Whether the user agent is WebKit. WebKit is the rendering engine that
+ * Safari, Android and others use.
+ * @type {boolean}
*/
-goog.events.EventType = {
- // Mouse events
- CLICK: 'click',
- DBLCLICK: 'dblclick',
- MOUSEDOWN: 'mousedown',
- MOUSEUP: 'mouseup',
- MOUSEOVER: 'mouseover',
- MOUSEOUT: 'mouseout',
- MOUSEMOVE: 'mousemove',
- SELECTSTART: 'selectstart', // IE, Safari, Chrome
+goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ?
+ goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT :
+ goog.userAgent.detectedWebkit_;
- // Key events
- KEYPRESS: 'keypress',
- KEYDOWN: 'keydown',
- KEYUP: 'keyup',
- // Focus
- BLUR: 'blur',
- FOCUS: 'focus',
- DEACTIVATE: 'deactivate', // IE only
- // NOTE: The following two events are not stable in cross-browser usage.
- // WebKit and Opera implement DOMFocusIn/Out.
- // IE implements focusin/out.
- // Gecko implements neither see bug at
- // https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
- // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
- // http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
- // You can use FOCUS in Capture phase until implementations converge.
- FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
- FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
+/**
+ * Whether the user agent is running on a mobile device.
+ * @type {boolean}
+ */
+goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT ||
+ goog.userAgent.detectedMobile_;
- // Forms
- CHANGE: 'change',
- SELECT: 'select',
- SUBMIT: 'submit',
- INPUT: 'input',
- PROPERTYCHANGE: 'propertychange', // IE only
-
- // Drag and drop
- DRAGSTART: 'dragstart',
- DRAG: 'drag',
- DRAGENTER: 'dragenter',
- DRAGOVER: 'dragover',
- DRAGLEAVE: 'dragleave',
- DROP: 'drop',
- DRAGEND: 'dragend',
-
- // WebKit touch events.
- TOUCHSTART: 'touchstart',
- TOUCHMOVE: 'touchmove',
- TOUCHEND: 'touchend',
- TOUCHCANCEL: 'touchcancel',
-
- // Misc
- BEFOREUNLOAD: 'beforeunload',
- CONTEXTMENU: 'contextmenu',
- ERROR: 'error',
- HELP: 'help',
- LOAD: 'load',
- LOSECAPTURE: 'losecapture',
- READYSTATECHANGE: 'readystatechange',
- RESIZE: 'resize',
- SCROLL: 'scroll',
- UNLOAD: 'unload',
-
- // HTML 5 History events
- // See http://www.w3.org/TR/html5/history.html#event-definitions
- HASHCHANGE: 'hashchange',
- PAGEHIDE: 'pagehide',
- PAGESHOW: 'pageshow',
- POPSTATE: 'popstate',
-
- // Copy and Paste
- // Support is limited. Make sure it works on your favorite browser
- // before using.
- // http://www.quirksmode.org/dom/events/cutcopypaste.html
- COPY: 'copy',
- PASTE: 'paste',
- CUT: 'cut',
- BEFORECOPY: 'beforecopy',
- BEFORECUT: 'beforecut',
- BEFOREPASTE: 'beforepaste',
-
- // HTML5 online/offline events.
- // http://www.w3.org/TR/offline-webapps/#related
- ONLINE: 'online',
- OFFLINE: 'offline',
-
- // HTML 5 worker events
- MESSAGE: 'message',
- CONNECT: 'connect',
-
- // CSS transition events. Based on the browser support described at:
- // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
- TRANSITIONEND: goog.userAgent.WEBKIT ? 'webkitTransitionEnd' :
- (goog.userAgent.OPERA ? 'oTransitionEnd' : 'transitionend'),
-
- // IE specific events.
- // See http://msdn.microsoft.com/en-us/library/ie/hh673557(v=vs.85).aspx
- MSGESTURECHANGE: 'MSGestureChange',
- MSGESTUREEND: 'MSGestureEnd',
- MSGESTUREHOLD: 'MSGestureHold',
- MSGESTURESTART: 'MSGestureStart',
- MSGESTURETAP: 'MSGestureTap',
- MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
- MSINERTIASTART: 'MSInertiaStart',
- MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
- MSPOINTERCANCEL: 'MSPointerCancel',
- MSPOINTERDOWN: 'MSPointerDown',
- MSPOINTERMOVE: 'MSPointerMove',
- MSPOINTEROVER: 'MSPointerOver',
- MSPOINTEROUT: 'MSPointerOut',
- MSPOINTERUP: 'MSPointerUp',
-
- // Native IMEs/input tools events.
- TEXTINPUT: 'textinput',
- COMPOSITIONSTART: 'compositionstart',
- COMPOSITIONUPDATE: 'compositionupdate',
- COMPOSITIONEND: 'compositionend'
-};
-}
-if(!lt.util.load.provided_QMARK_('goog.events.BrowserEvent')) {
-// 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 A patched, standardized event object for browser events.
- *
- *
- * The patched event object contains the following members:
- * - type {string} Event type, e.g. 'click'
- * - timestamp {Date} A date object for when the event was fired
- * - target {Object} The element that actually triggered the event
- * - currentTarget {Object} The element the listener is attached to
- * - relatedTarget {Object} For mouseover and mouseout, the previous object
- * - offsetX {number} X-coordinate relative to target
- * - offsetY {number} Y-coordinate relative to target
- * - clientX {number} X-coordinate relative to viewport
- * - clientY {number} Y-coordinate relative to viewport
- * - screenX {number} X-coordinate relative to the edge of the screen
- * - screenY {number} Y-coordinate relative to the edge of the screen
- * - button {number} Mouse button. Use isButton() to test.
- * - keyCode {number} Key-code
- * - ctrlKey {boolean} Was ctrl key depressed
- * - altKey {boolean} Was alt key depressed
- * - shiftKey {boolean} Was shift key depressed
- * - metaKey {boolean} Was meta key depressed
- * - defaultPrevented {boolean} Whether the default action has been prevented
- * - state {Object} History state object
- *
- * NOTE: The keyCode member contains the raw browser keyCode. For normalized
- * key and character code use {@link goog.events.KeyHandler}.
- *
- *
- */
-
-goog.provide('goog.events.BrowserEvent');
-goog.provide('goog.events.BrowserEvent.MouseButton');
-
-goog.require('goog.events.BrowserFeature');
-goog.require('goog.events.Event');
-goog.require('goog.events.EventType');
-goog.require('goog.reflect');
-goog.require('goog.userAgent');
+/**
+ * Used while transitioning code to use WEBKIT instead.
+ * @type {boolean}
+ * @deprecated Use {@link goog.userAgent.product.SAFARI} instead.
+ * TODO(nicksantos): Delete this from goog.userAgent.
+ */
+goog.userAgent.SAFARI = goog.userAgent.WEBKIT;
/**
- * Accepts a browser event object and creates a patched, cross browser event
- * object.
- * The content of this object will not be initialized if no event object is
- * provided. If this is the case, init() needs to be invoked separately.
- * @param {Event=} opt_e Browser event object.
- * @param {EventTarget=} opt_currentTarget Current target for event.
- * @constructor
- * @extends {goog.events.Event}
+ * @return {string} the platform (operating system) the user agent is running
+ * on. Default to empty string because navigator.platform may not be defined
+ * (on Rhino, for example).
+ * @private
*/
-goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
- if (opt_e) {
- this.init(opt_e, opt_currentTarget);
- }
+goog.userAgent.determinePlatform_ = function() {
+ var navigator = goog.userAgent.getNavigator();
+ return navigator && navigator.platform || '';
};
-goog.inherits(goog.events.BrowserEvent, goog.events.Event);
/**
- * Normalized button constants for the mouse.
- * @enum {number}
+ * The platform (operating system) the user agent is running on. Default to
+ * empty string because navigator.platform may not be defined (on Rhino, for
+ * example).
+ * @type {string}
*/
-goog.events.BrowserEvent.MouseButton = {
- LEFT: 0,
- MIDDLE: 1,
- RIGHT: 2
-};
+goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_();
/**
- * Static data for mapping mouse buttons.
- * @type {Array.}
+ * @define {boolean} Whether the user agent is running on a Macintosh operating
+ * system.
*/
-goog.events.BrowserEvent.IEButtonMap = [
- 1, // LEFT
- 4, // MIDDLE
- 2 // RIGHT
-];
+goog.define('goog.userAgent.ASSUME_MAC', false);
/**
- * Target that fired the event.
- * @override
- * @type {Node}
+ * @define {boolean} Whether the user agent is running on a Windows operating
+ * system.
*/
-goog.events.BrowserEvent.prototype.target = null;
+goog.define('goog.userAgent.ASSUME_WINDOWS', false);
/**
- * Node that had the listener attached.
- * @override
- * @type {Node|undefined}
+ * @define {boolean} Whether the user agent is running on a Linux operating
+ * system.
*/
-goog.events.BrowserEvent.prototype.currentTarget;
+goog.define('goog.userAgent.ASSUME_LINUX', false);
/**
- * For mouseover and mouseout events, the related object for the event.
- * @type {Node}
+ * @define {boolean} Whether the user agent is running on a X11 windowing
+ * system.
*/
-goog.events.BrowserEvent.prototype.relatedTarget = null;
+goog.define('goog.userAgent.ASSUME_X11', false);
/**
- * X-coordinate relative to target.
- * @type {number}
+ * @define {boolean} Whether the user agent is running on Android.
*/
-goog.events.BrowserEvent.prototype.offsetX = 0;
+goog.define('goog.userAgent.ASSUME_ANDROID', false);
/**
- * Y-coordinate relative to target.
- * @type {number}
+ * @define {boolean} Whether the user agent is running on an iPhone.
*/
-goog.events.BrowserEvent.prototype.offsetY = 0;
+goog.define('goog.userAgent.ASSUME_IPHONE', false);
/**
- * X-coordinate relative to the window.
- * @type {number}
+ * @define {boolean} Whether the user agent is running on an iPad.
*/
-goog.events.BrowserEvent.prototype.clientX = 0;
+goog.define('goog.userAgent.ASSUME_IPAD', false);
/**
- * Y-coordinate relative to the window.
- * @type {number}
+ * @type {boolean}
+ * @private
*/
-goog.events.BrowserEvent.prototype.clientY = 0;
+goog.userAgent.PLATFORM_KNOWN_ =
+ goog.userAgent.ASSUME_MAC ||
+ goog.userAgent.ASSUME_WINDOWS ||
+ goog.userAgent.ASSUME_LINUX ||
+ goog.userAgent.ASSUME_X11 ||
+ goog.userAgent.ASSUME_ANDROID ||
+ goog.userAgent.ASSUME_IPHONE ||
+ goog.userAgent.ASSUME_IPAD;
/**
- * X-coordinate relative to the monitor.
- * @type {number}
+ * Initialize the goog.userAgent constants that define which platform the user
+ * agent is running on.
+ * @private
*/
-goog.events.BrowserEvent.prototype.screenX = 0;
+goog.userAgent.initPlatform_ = function() {
+ /**
+ * Whether the user agent is running on a Macintosh operating system.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM,
+ 'Mac');
+
+ /**
+ * Whether the user agent is running on a Windows operating system.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedWindows_ = goog.string.contains(
+ goog.userAgent.PLATFORM, 'Win');
+ /**
+ * Whether the user agent is running on a Linux operating system.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM,
+ 'Linux');
-/**
- * Y-coordinate relative to the monitor.
- * @type {number}
- */
-goog.events.BrowserEvent.prototype.screenY = 0;
+ /**
+ * Whether the user agent is running on a X11 windowing system.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() &&
+ goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '',
+ 'X11');
+ // Need user agent string for Android/IOS detection
+ var ua = goog.userAgent.getUserAgentString();
-/**
- * Which mouse button was pressed.
- * @type {number}
- */
-goog.events.BrowserEvent.prototype.button = 0;
+ /**
+ * Whether the user agent is running on Android.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedAndroid_ = !!ua &&
+ goog.string.contains(ua, 'Android');
+ /**
+ * Whether the user agent is running on an iPhone.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedIPhone_ = !!ua && goog.string.contains(ua, 'iPhone');
-/**
- * Keycode of key press.
- * @type {number}
- */
-goog.events.BrowserEvent.prototype.keyCode = 0;
+ /**
+ * Whether the user agent is running on an iPad.
+ * @type {boolean}
+ * @private
+ */
+ goog.userAgent.detectedIPad_ = !!ua && goog.string.contains(ua, 'iPad');
+};
-/**
- * Keycode of key press.
- * @type {number}
- */
-goog.events.BrowserEvent.prototype.charCode = 0;
+if (!goog.userAgent.PLATFORM_KNOWN_) {
+ goog.userAgent.initPlatform_();
+}
/**
- * Whether control was pressed at time of event.
+ * Whether the user agent is running on a Macintosh operating system.
* @type {boolean}
*/
-goog.events.BrowserEvent.prototype.ctrlKey = false;
+goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_;
/**
- * Whether alt was pressed at time of event.
+ * Whether the user agent is running on a Windows operating system.
* @type {boolean}
*/
-goog.events.BrowserEvent.prototype.altKey = false;
+goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_;
/**
- * Whether shift was pressed at time of event.
+ * Whether the user agent is running on a Linux operating system.
* @type {boolean}
*/
-goog.events.BrowserEvent.prototype.shiftKey = false;
+goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_;
/**
- * Whether the meta key was pressed at time of event.
+ * Whether the user agent is running on a X11 windowing system.
* @type {boolean}
*/
-goog.events.BrowserEvent.prototype.metaKey = false;
+goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_;
/**
- * History state object, only set for PopState events where it's a copy of the
- * state object provided to pushState or replaceState.
- * @type {Object}
+ * Whether the user agent is running on Android.
+ * @type {boolean}
*/
-goog.events.BrowserEvent.prototype.state;
+goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_;
/**
- * Whether the default platform modifier key was pressed at time of event.
- * (This is control for all platforms except Mac, where it's Meta.
+ * Whether the user agent is running on an iPhone.
* @type {boolean}
*/
-goog.events.BrowserEvent.prototype.platformModifierKey = false;
+goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_;
/**
- * The browser event object.
- * @type {Event}
- * @private
+ * Whether the user agent is running on an iPad.
+ * @type {boolean}
*/
-goog.events.BrowserEvent.prototype.event_ = null;
+goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ?
+ goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_;
/**
- * Accepts a browser event object and creates a patched, cross browser event
- * object.
- * @param {Event} e Browser event object.
- * @param {EventTarget=} opt_currentTarget Current target for event.
+ * @return {string} The string that describes the version number of the user
+ * agent.
+ * @private
*/
-goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
- var type = this.type = e.type;
- goog.events.Event.call(this, type);
-
- // TODO(nicksantos): Change this.target to type EventTarget.
- this.target = /** @type {Node} */ (e.target) || e.srcElement;
+goog.userAgent.determineVersion_ = function() {
+ // All browsers have different ways to detect the version and they all have
+ // different naming schemes.
- // TODO(nicksantos): Change this.currentTarget to type EventTarget.
- this.currentTarget = /** @type {Node} */ (opt_currentTarget);
+ // version is a string rather than a number because it may contain 'b', 'a',
+ // and so on.
+ var version = '', re;
- var relatedTarget = /** @type {Node} */ (e.relatedTarget);
- if (relatedTarget) {
- // There's a bug in FireFox where sometimes, relatedTarget will be a
- // chrome element, and accessing any property of it will get a permission
- // denied exception. See:
- // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
+ if (goog.userAgent.OPERA && goog.global['opera']) {
+ var operaVersion = goog.global['opera'].version;
+ version = typeof operaVersion == 'function' ? operaVersion() : operaVersion;
+ } else {
if (goog.userAgent.GECKO) {
- if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
- relatedTarget = null;
- }
+ re = /rv\:([^\);]+)(\)|;)/;
+ } else if (goog.userAgent.IE) {
+ re = /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/;
+ } else if (goog.userAgent.WEBKIT) {
+ // WebKit/125.4
+ re = /WebKit\/(\S+)/;
+ }
+ if (re) {
+ var arr = re.exec(goog.userAgent.getUserAgentString());
+ version = arr ? arr[1] : '';
}
- // TODO(arv): Use goog.events.EventType when it has been refactored into its
- // own file.
- } else if (type == goog.events.EventType.MOUSEOVER) {
- relatedTarget = e.fromElement;
- } else if (type == goog.events.EventType.MOUSEOUT) {
- relatedTarget = e.toElement;
}
-
- this.relatedTarget = relatedTarget;
-
- // Webkit emits a lame warning whenever layerX/layerY is accessed.
- // http://code.google.com/p/chromium/issues/detail?id=101733
- this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
- e.offsetX : e.layerX;
- this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
- e.offsetY : e.layerY;
-
- this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
- this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
- this.screenX = e.screenX || 0;
- this.screenY = e.screenY || 0;
-
- this.button = e.button;
-
- this.keyCode = e.keyCode || 0;
- this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
- this.ctrlKey = e.ctrlKey;
- this.altKey = e.altKey;
- this.shiftKey = e.shiftKey;
- this.metaKey = e.metaKey;
- this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
- this.state = e.state;
- this.event_ = e;
- if (e.defaultPrevented) {
- this.preventDefault();
+ if (goog.userAgent.IE) {
+ // IE9 can be in document mode 9 but be reporting an inconsistent user agent
+ // version. If it is identifying as a version lower than 9 we take the
+ // documentMode as the version instead. IE8 has similar behavior.
+ // It is recommended to set the X-UA-Compatible header to ensure that IE9
+ // uses documentMode 9.
+ var docMode = goog.userAgent.getDocumentMode_();
+ if (docMode > parseFloat(version)) {
+ return String(docMode);
+ }
}
- delete this.propagationStopped_;
+ return version;
};
/**
- * Tests to see which button was pressed during the event. This is really only
- * useful in IE and Gecko browsers. And in IE, it's only useful for
- * mousedown/mouseup events, because click only fires for the left mouse button.
- *
- * Safari 2 only reports the left button being clicked, and uses the value '1'
- * instead of 0. Opera only reports a mousedown event for the middle button, and
- * no mouse events for the right button. Opera has default behavior for left and
- * middle click that can only be overridden via a configuration setting.
- *
- * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
- *
- * @param {goog.events.BrowserEvent.MouseButton} button The button
- * to test for.
- * @return {boolean} True if button was pressed.
+ * @return {number|undefined} Returns the document mode (for testing).
+ * @private
*/
-goog.events.BrowserEvent.prototype.isButton = function(button) {
- if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
- if (this.type == 'click') {
- return button == goog.events.BrowserEvent.MouseButton.LEFT;
- } else {
- return !!(this.event_.button &
- goog.events.BrowserEvent.IEButtonMap[button]);
- }
- } else {
- return this.event_.button == button;
- }
+goog.userAgent.getDocumentMode_ = function() {
+ // NOTE(user): goog.userAgent may be used in context where there is no DOM.
+ var doc = goog.global['document'];
+ return doc ? doc['documentMode'] : undefined;
};
/**
- * Whether this has an "action"-producing mouse button.
+ * The version of the user agent. This is a string because it might contain
+ * 'b' (as in beta) as well as multiple dots.
+ * @type {string}
+ */
+goog.userAgent.VERSION = goog.userAgent.determineVersion_();
+
+
+/**
+ * Compares two version numbers.
*
- * By definition, this includes left-click on windows/linux, and left-click
- * without the ctrl key on Macs.
+ * @param {string} v1 Version of first item.
+ * @param {string} v2 Version of second item.
*
- * @return {boolean} The result.
+ * @return {number} 1 if first argument is higher
+ * 0 if arguments are equal
+ * -1 if second argument is higher.
+ * @deprecated Use goog.string.compareVersions.
*/
-goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
- // Webkit does not ctrl+click to be a right-click, so we
- // normalize it to behave like Gecko and Opera.
- return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
- !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
+goog.userAgent.compare = function(v1, v2) {
+ return goog.string.compareVersions(v1, v2);
};
/**
- * @override
+ * Cache for {@link goog.userAgent.isVersionOrHigher}.
+ * Calls to compareVersions are surprisingly expensive and, as a browser's
+ * version number is unlikely to change during a session, we cache the results.
+ * @const
+ * @private
*/
-goog.events.BrowserEvent.prototype.stopPropagation = function() {
- goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
- if (this.event_.stopPropagation) {
- this.event_.stopPropagation();
- } else {
- this.event_.cancelBubble = true;
- }
-};
+goog.userAgent.isVersionOrHigherCache_ = {};
/**
- * @override
+ * Whether the user agent version is higher or the same as the given version.
+ * NOTE: When checking the version numbers for Firefox and Safari, be sure to
+ * use the engine's version, not the browser's version number. For example,
+ * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11.
+ * Opera and Internet Explorer versions match the product release number.
+ * @see
+ * Webkit
+ * @see Gecko
+ *
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the user agent version is higher or the same as
+ * the given version.
*/
-goog.events.BrowserEvent.prototype.preventDefault = function() {
- goog.events.BrowserEvent.superClass_.preventDefault.call(this);
- var be = this.event_;
- if (!be.preventDefault) {
- be.returnValue = false;
- if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
- /** @preserveTry */
- try {
- // Most keys can be prevented using returnValue. Some special keys
- // require setting the keyCode to -1 as well:
- //
- // In IE7:
- // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
- //
- // In IE8:
- // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
- //
- // We therefore do this for all function keys as well as when Ctrl key
- // is pressed.
- var VK_F1 = 112;
- var VK_F12 = 123;
- if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
- be.keyCode = -1;
- }
- } catch (ex) {
- // IE throws an 'access denied' exception when trying to change
- // keyCode in some situations (e.g. srcElement is input[type=file],
- // or srcElement is an anchor tag rewritten by parent's innerHTML).
- // Do nothing in this case.
- }
- }
- } else {
- be.preventDefault();
- }
+goog.userAgent.isVersionOrHigher = function(version) {
+ return goog.userAgent.ASSUME_ANY_VERSION ||
+ goog.userAgent.isVersionOrHigherCache_[version] ||
+ (goog.userAgent.isVersionOrHigherCache_[version] =
+ goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0);
};
/**
- * @return {Event} The underlying browser event object.
+ * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}.
+ * @param {string|number} version The version to check.
+ * @return {boolean} Whether the user agent version is higher or the same as
+ * the given version.
+ * @deprecated Use goog.userAgent.isVersionOrHigher().
*/
-goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
- return this.event_;
-};
+goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher;
-/** @override */
-goog.events.BrowserEvent.prototype.disposeInternal = function() {
+/**
+ * Whether the IE effective document mode is higher or the same as the given
+ * document mode version.
+ * NOTE: Only for IE, return false for another browser.
+ *
+ * @param {number} documentMode The document mode version to check.
+ * @return {boolean} Whether the IE effective document mode is higher or the
+ * same as the given version.
+ */
+goog.userAgent.isDocumentModeOrHigher = function(documentMode) {
+ return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode;
};
-}
-if(!lt.util.load.provided_QMARK_('goog.object')) {
-// Copyright 2006 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 Utilities for manipulating objects/maps/hashes.
+ * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}.
+ * @param {number} version The version to check.
+ * @return {boolean} Whether the IE effective document mode is higher or the
+ * same as the given version.
+ * @deprecated Use goog.userAgent.isDocumentModeOrHigher().
*/
-
-goog.provide('goog.object');
+goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher;
/**
- * Calls a function for each element in an object/map/hash.
- *
- * @param {Object.} obj The object over which to iterate.
- * @param {function(this:T,V,?,Object.):?} f The function to call
- * for every element. This function takes 3 arguments (the element, the
- * index and the object) and the return value is ignored.
- * @param {T=} opt_obj This is used as the 'this' object within f.
- * @template T,K,V
+ * For IE version < 7, documentMode is undefined, so attempt to use the
+ * CSS1Compat property to see if we are in standards mode. If we are in
+ * standards mode, treat the browser version as the document mode. Otherwise,
+ * IE is emulating version 5.
+ * @type {number|undefined}
+ * @const
*/
-goog.object.forEach = function(obj, f, opt_obj) {
- for (var key in obj) {
- f.call(opt_obj, obj[key], key, obj);
+goog.userAgent.DOCUMENT_MODE = (function() {
+ var doc = goog.global['document'];
+ if (!doc || !goog.userAgent.IE) {
+ return undefined;
}
-};
-
+ var mode = goog.userAgent.getDocumentMode_();
+ return mode || (doc['compatMode'] == 'CSS1Compat' ?
+ parseInt(goog.userAgent.VERSION, 10) : 5);
+})();
+}
+if(!lt.util.load.provided_QMARK_('goog.events.BrowserFeature')) {
+// Copyright 2010 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.
/**
- * Calls a function for each element in an object/map/hash. If that call returns
- * true, adds the element to a new object.
+ * @fileoverview Browser capability checks for the events package.
*
- * @param {Object.} obj The object over which to iterate.
- * @param {function(this:T,V,?,Object.):boolean} f The function to call
- * for every element. This
- * function takes 3 arguments (the element, the index and the object)
- * and should return a boolean. If the return value is true the
- * element is added to the result object. If it is false the
- * element is not included.
- * @param {T=} opt_obj This is used as the 'this' object within f.
- * @return {!Object.} a new object in which only elements that passed the
- * test are present.
- * @template T,K,V
*/
-goog.object.filter = function(obj, f, opt_obj) {
- var res = {};
- for (var key in obj) {
- if (f.call(opt_obj, obj[key], key, obj)) {
- res[key] = obj[key];
- }
- }
- return res;
-};
-/**
- * For every element in an object/map/hash calls a function and inserts the
- * result into a new object.
- *
- * @param {Object.} obj The object over which to iterate.
- * @param {function(this:T,V,?,Object.):R} f The function to call
- * for every element. This function
- * takes 3 arguments (the element, the index and the object)
- * and should return something. The result will be inserted
- * into a new object.
- * @param {T=} opt_obj This is used as the 'this' object within f.
- * @return {!Object.} a new object with the results from f.
- * @template T,K,V,R
- */
-goog.object.map = function(obj, f, opt_obj) {
- var res = {};
- for (var key in obj) {
- res[key] = f.call(opt_obj, obj[key], key, obj);
- }
- return res;
-};
+goog.provide('goog.events.BrowserFeature');
+
+goog.require('goog.userAgent');
/**
- * Calls a function for each element in an object/map/hash. If any
- * call returns true, returns true (without checking the rest). If
- * all calls return false, returns false.
- *
- * @param {Object.} obj The object to check.
- * @param {function(this:T,V,?,Object.):boolean} f The function to
- * call for every element. This function
- * takes 3 arguments (the element, the index and the object) and should
- * return a boolean.
- * @param {T=} opt_obj This is used as the 'this' object within f.
- * @return {boolean} true if any element passes the test.
- * @template T,K,V
+ * Enum of browser capabilities.
+ * @enum {boolean}
*/
-goog.object.some = function(obj, f, opt_obj) {
- for (var key in obj) {
- if (f.call(opt_obj, obj[key], key, obj)) {
- return true;
- }
- }
- return false;
-};
+goog.events.BrowserFeature = {
+ /**
+ * Whether the button attribute of the event is W3C compliant. False in
+ * Internet Explorer prior to version 9; document-version dependent.
+ */
+ HAS_W3C_BUTTON: !goog.userAgent.IE ||
+ goog.userAgent.isDocumentModeOrHigher(9),
+ /**
+ * Whether the browser supports full W3C event model.
+ */
+ HAS_W3C_EVENT_SUPPORT: !goog.userAgent.IE ||
+ goog.userAgent.isDocumentModeOrHigher(9),
-/**
- * Calls a function for each element in an object/map/hash. If
- * all calls return true, returns true. If any call returns false, returns
- * false at this point and does not continue to check the remaining elements.
- *
- * @param {Object.} obj The object to check.
- * @param {?function(this:T,V,?,Object.):boolean} f The function to
- * call for every element. This function
- * takes 3 arguments (the element, the index and the object) and should
- * return a boolean.
- * @param {T=} opt_obj This is used as the 'this' object within f.
- * @return {boolean} false if any element fails the test.
- * @template T,K,V
- */
-goog.object.every = function(obj, f, opt_obj) {
- for (var key in obj) {
- if (!f.call(opt_obj, obj[key], key, obj)) {
- return false;
- }
- }
- return true;
-};
+ /**
+ * To prevent default in IE7-8 for certain keydown events we need set the
+ * keyCode to -1.
+ */
+ SET_KEY_CODE_TO_PREVENT_DEFAULT: goog.userAgent.IE &&
+ !goog.userAgent.isVersionOrHigher('9'),
+ /**
+ * Whether the {@code navigator.onLine} property is supported.
+ */
+ HAS_NAVIGATOR_ONLINE_PROPERTY: !goog.userAgent.WEBKIT ||
+ goog.userAgent.isVersionOrHigher('528'),
-/**
- * Returns the number of key-value pairs in the object map.
- *
- * @param {Object} obj The object for which to get the number of key-value
- * pairs.
- * @return {number} The number of key-value pairs in the object map.
- */
-goog.object.getCount = function(obj) {
- // JS1.5 has __count__ but it has been deprecated so it raises a warning...
- // in other words do not use. Also __count__ only includes the fields on the
- // actual object and not in the prototype chain.
- var rv = 0;
- for (var key in obj) {
- rv++;
- }
- return rv;
-};
+ /**
+ * Whether HTML5 network online/offline events are supported.
+ */
+ HAS_HTML5_NETWORK_EVENT_SUPPORT:
+ goog.userAgent.GECKO && goog.userAgent.isVersionOrHigher('1.9b') ||
+ goog.userAgent.IE && goog.userAgent.isVersionOrHigher('8') ||
+ goog.userAgent.OPERA && goog.userAgent.isVersionOrHigher('9.5') ||
+ goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('528'),
+ /**
+ * Whether HTML5 network events fire on document.body, or otherwise the
+ * window.
+ */
+ HTML5_NETWORK_EVENTS_FIRE_ON_BODY:
+ goog.userAgent.GECKO && !goog.userAgent.isVersionOrHigher('8') ||
+ goog.userAgent.IE && !goog.userAgent.isVersionOrHigher('9'),
-/**
- * Returns one key from the object map, if any exists.
- * For map literals the returned key will be the first one in most of the
- * browsers (a know exception is Konqueror).
- *
- * @param {Object} obj The object to pick a key from.
- * @return {string|undefined} The key or undefined if the object is empty.
- */
-goog.object.getAnyKey = function(obj) {
- for (var key in obj) {
- return key;
- }
+ /**
+ * Whether touch is enabled in the browser.
+ */
+ TOUCH_ENABLED:
+ ('ontouchstart' in goog.global ||
+ !!(goog.global['document'] &&
+ document.documentElement &&
+ 'ontouchstart' in document.documentElement) ||
+ // IE10 uses non-standard touch events, so it has a different check.
+ !!(goog.global['navigator'] &&
+ goog.global['navigator']['msMaxTouchPoints']))
};
-
+}
+if(!lt.util.load.provided_QMARK_('goog.events.EventType')) {
+// Copyright 2010 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.
/**
- * Returns one value from the object map, if any exists.
- * For map literals the returned value will be the first one in most of the
- * browsers (a know exception is Konqueror).
+ * @fileoverview Event Types.
*
- * @param {Object.} obj The object to pick a value from.
- * @return {V|undefined} The value or undefined if the object is empty.
- * @template K,V
+ * @author arv@google.com (Erik Arvidsson)
+ * @author mirkov@google.com (Mirko Visontai)
*/
-goog.object.getAnyValue = function(obj) {
- for (var key in obj) {
- return obj[key];
- }
-};
-/**
- * Whether the object/hash/map contains the given object as a value.
- * An alias for goog.object.containsValue(obj, val).
- *
- * @param {Object.} obj The object in which to look for val.
- * @param {V} val The object for which to check.
- * @return {boolean} true if val is present.
- * @template K,V
- */
-goog.object.contains = function(obj, val) {
- return goog.object.containsValue(obj, val);
-};
+goog.provide('goog.events.EventType');
+
+goog.require('goog.userAgent');
/**
- * Returns the values of the object/map/hash.
- *
- * @param {Object.} obj The object from which to get the values.
- * @return {!Array.} The values in the object/map/hash.
- * @template K,V
+ * Returns a prefixed event name for the current browser.
+ * @param {string} eventName The name of the event.
+ * @return {string} The prefixed event name.
+ * @suppress {missingRequire|missingProvide}
+ * @private
*/
-goog.object.getValues = function(obj) {
- var res = [];
- var i = 0;
- for (var key in obj) {
- res[i++] = obj[key];
- }
- return res;
+goog.events.getVendorPrefixedName_ = function(eventName) {
+ return goog.userAgent.WEBKIT ? 'webkit' + eventName :
+ (goog.userAgent.OPERA ? 'o' + eventName.toLowerCase() :
+ eventName.toLowerCase());
};
/**
- * Returns the keys of the object/map/hash.
- *
- * @param {Object} obj The object from which to get the keys.
- * @return {!Array.} Array of property keys.
+ * Constants for event names.
+ * @enum {string}
*/
-goog.object.getKeys = function(obj) {
- var res = [];
- var i = 0;
- for (var key in obj) {
- res[i++] = key;
- }
- return res;
-};
+goog.events.EventType = {
+ // Mouse events
+ CLICK: 'click',
+ DBLCLICK: 'dblclick',
+ MOUSEDOWN: 'mousedown',
+ MOUSEUP: 'mouseup',
+ MOUSEOVER: 'mouseover',
+ MOUSEOUT: 'mouseout',
+ MOUSEMOVE: 'mousemove',
+ MOUSEENTER: 'mouseenter',
+ MOUSELEAVE: 'mouseleave',
+ // Select start is non-standard.
+ // See http://msdn.microsoft.com/en-us/library/ie/ms536969(v=vs.85).aspx.
+ SELECTSTART: 'selectstart', // IE, Safari, Chrome
+ // Key events
+ KEYPRESS: 'keypress',
+ KEYDOWN: 'keydown',
+ KEYUP: 'keyup',
-/**
- * Get a value from an object multiple levels deep. This is useful for
- * pulling values from deeply nested objects, such as JSON responses.
- * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3)
- *
- * @param {!Object} obj An object to get the value from. Can be array-like.
- * @param {...(string|number|!Array.)} var_args A number of keys
- * (as strings, or numbers, for array-like objects). Can also be
- * specified as a single array of keys.
- * @return {*} The resulting value. If, at any point, the value for a key
- * is undefined, returns undefined.
- */
-goog.object.getValueByKeys = function(obj, var_args) {
- var isArrayLike = goog.isArrayLike(var_args);
- var keys = isArrayLike ? var_args : arguments;
+ // Focus
+ BLUR: 'blur',
+ FOCUS: 'focus',
+ DEACTIVATE: 'deactivate', // IE only
+ // NOTE: The following two events are not stable in cross-browser usage.
+ // WebKit and Opera implement DOMFocusIn/Out.
+ // IE implements focusin/out.
+ // Gecko implements neither see bug at
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=396927.
+ // The DOM Events Level 3 Draft deprecates DOMFocusIn in favor of focusin:
+ // http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
+ // You can use FOCUS in Capture phase until implementations converge.
+ FOCUSIN: goog.userAgent.IE ? 'focusin' : 'DOMFocusIn',
+ FOCUSOUT: goog.userAgent.IE ? 'focusout' : 'DOMFocusOut',
- // Start with the 2nd parameter for the variable parameters syntax.
- for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) {
- obj = obj[keys[i]];
- if (!goog.isDef(obj)) {
- break;
- }
- }
+ // Forms
+ CHANGE: 'change',
+ SELECT: 'select',
+ SUBMIT: 'submit',
+ INPUT: 'input',
+ PROPERTYCHANGE: 'propertychange', // IE only
- return obj;
-};
+ // Drag and drop
+ DRAGSTART: 'dragstart',
+ DRAG: 'drag',
+ DRAGENTER: 'dragenter',
+ DRAGOVER: 'dragover',
+ DRAGLEAVE: 'dragleave',
+ DROP: 'drop',
+ DRAGEND: 'dragend',
+ // WebKit touch events.
+ TOUCHSTART: 'touchstart',
+ TOUCHMOVE: 'touchmove',
+ TOUCHEND: 'touchend',
+ TOUCHCANCEL: 'touchcancel',
-/**
- * Whether the object/map/hash contains the given key.
- *
- * @param {Object} obj The object in which to look for key.
- * @param {*} key The key for which to check.
- * @return {boolean} true If the map contains the key.
- */
-goog.object.containsKey = function(obj, key) {
- return key in obj;
-};
+ // Misc
+ BEFOREUNLOAD: 'beforeunload',
+ CONSOLEMESSAGE: 'consolemessage',
+ CONTEXTMENU: 'contextmenu',
+ DOMCONTENTLOADED: 'DOMContentLoaded',
+ ERROR: 'error',
+ HELP: 'help',
+ LOAD: 'load',
+ LOSECAPTURE: 'losecapture',
+ ORIENTATIONCHANGE: 'orientationchange',
+ READYSTATECHANGE: 'readystatechange',
+ RESIZE: 'resize',
+ SCROLL: 'scroll',
+ UNLOAD: 'unload',
+ // HTML 5 History events
+ // See http://www.w3.org/TR/html5/history.html#event-definitions
+ HASHCHANGE: 'hashchange',
+ PAGEHIDE: 'pagehide',
+ PAGESHOW: 'pageshow',
+ POPSTATE: 'popstate',
-/**
- * Whether the object/map/hash contains the given value. This is O(n).
- *
- * @param {Object.} obj The object in which to look for val.
- * @param {V} val The value for which to check.
- * @return {boolean} true If the map contains the value.
- * @template K,V
- */
-goog.object.containsValue = function(obj, val) {
- for (var key in obj) {
- if (obj[key] == val) {
- return true;
- }
- }
- return false;
-};
+ // Copy and Paste
+ // Support is limited. Make sure it works on your favorite browser
+ // before using.
+ // http://www.quirksmode.org/dom/events/cutcopypaste.html
+ COPY: 'copy',
+ PASTE: 'paste',
+ CUT: 'cut',
+ BEFORECOPY: 'beforecopy',
+ BEFORECUT: 'beforecut',
+ BEFOREPASTE: 'beforepaste',
+ // HTML5 online/offline events.
+ // http://www.w3.org/TR/offline-webapps/#related
+ ONLINE: 'online',
+ OFFLINE: 'offline',
-/**
- * Searches an object for an element that satisfies the given condition and
- * returns its key.
- * @param {Object.} obj The object to search in.
- * @param {function(this:T,V,string,Object.):boolean} f The
- * function to call for every element. Takes 3 arguments (the value,
- * the key and the object) and should return a boolean.
- * @param {T=} opt_this An optional "this" context for the function.
- * @return {string|undefined} The key of an element for which the function
- * returns true or undefined if no such element is found.
- * @template T,K,V
- */
-goog.object.findKey = function(obj, f, opt_this) {
- for (var key in obj) {
- if (f.call(opt_this, obj[key], key, obj)) {
- return key;
- }
- }
- return undefined;
-};
+ // HTML 5 worker events
+ MESSAGE: 'message',
+ CONNECT: 'connect',
+ // CSS animation events.
+ /** @suppress {missingRequire} */
+ ANIMATIONSTART: goog.events.getVendorPrefixedName_('AnimationStart'),
+ /** @suppress {missingRequire} */
+ ANIMATIONEND: goog.events.getVendorPrefixedName_('AnimationEnd'),
+ /** @suppress {missingRequire} */
+ ANIMATIONITERATION: goog.events.getVendorPrefixedName_('AnimationIteration'),
-/**
- * Searches an object for an element that satisfies the given condition and
- * returns its value.
- * @param {Object.} obj The object to search in.
- * @param {function(this:T,V,string,Object.):boolean} f The function
- * to call for every element. Takes 3 arguments (the value, the key
- * and the object) and should return a boolean.
- * @param {T=} opt_this An optional "this" context for the function.
- * @return {V} The value of an element for which the function returns true or
- * undefined if no such element is found.
- * @template T,K,V
- */
-goog.object.findValue = function(obj, f, opt_this) {
- var key = goog.object.findKey(obj, f, opt_this);
- return key && obj[key];
-};
+ // CSS transition events. Based on the browser support described at:
+ // https://developer.mozilla.org/en/css/css_transitions#Browser_compatibility
+ /** @suppress {missingRequire} */
+ TRANSITIONEND: goog.events.getVendorPrefixedName_('TransitionEnd'),
+
+ // W3C Pointer Events
+ // http://www.w3.org/TR/pointerevents/
+ POINTERDOWN: 'pointerdown',
+ POINTERUP: 'pointerup',
+ POINTERCANCEL: 'pointercancel',
+ POINTERMOVE: 'pointermove',
+ POINTEROVER: 'pointerover',
+ POINTEROUT: 'pointerout',
+ POINTERENTER: 'pointerenter',
+ POINTERLEAVE: 'pointerleave',
+ GOTPOINTERCAPTURE: 'gotpointercapture',
+ LOSTPOINTERCAPTURE: 'lostpointercapture',
+ // IE specific events.
+ // See http://msdn.microsoft.com/en-us/library/ie/hh772103(v=vs.85).aspx
+ // Note: these events will be supplanted in IE11.
+ MSGESTURECHANGE: 'MSGestureChange',
+ MSGESTUREEND: 'MSGestureEnd',
+ MSGESTUREHOLD: 'MSGestureHold',
+ MSGESTURESTART: 'MSGestureStart',
+ MSGESTURETAP: 'MSGestureTap',
+ MSGOTPOINTERCAPTURE: 'MSGotPointerCapture',
+ MSINERTIASTART: 'MSInertiaStart',
+ MSLOSTPOINTERCAPTURE: 'MSLostPointerCapture',
+ MSPOINTERCANCEL: 'MSPointerCancel',
+ MSPOINTERDOWN: 'MSPointerDown',
+ MSPOINTERENTER: 'MSPointerEnter',
+ MSPOINTERHOVER: 'MSPointerHover',
+ MSPOINTERLEAVE: 'MSPointerLeave',
+ MSPOINTERMOVE: 'MSPointerMove',
+ MSPOINTEROUT: 'MSPointerOut',
+ MSPOINTEROVER: 'MSPointerOver',
+ MSPOINTERUP: 'MSPointerUp',
-/**
- * Whether the object/map/hash is empty.
- *
- * @param {Object} obj The object to test.
- * @return {boolean} true if obj is empty.
- */
-goog.object.isEmpty = function(obj) {
- for (var key in obj) {
- return false;
- }
- return true;
+ // Native IMEs/input tools events.
+ TEXTINPUT: 'textinput',
+ COMPOSITIONSTART: 'compositionstart',
+ COMPOSITIONUPDATE: 'compositionupdate',
+ COMPOSITIONEND: 'compositionend',
+
+ // Webview tag events
+ // See http://developer.chrome.com/dev/apps/webview_tag.html
+ EXIT: 'exit',
+ LOADABORT: 'loadabort',
+ LOADCOMMIT: 'loadcommit',
+ LOADREDIRECT: 'loadredirect',
+ LOADSTART: 'loadstart',
+ LOADSTOP: 'loadstop',
+ RESPONSIVE: 'responsive',
+ SIZECHANGED: 'sizechanged',
+ UNRESPONSIVE: 'unresponsive',
+
+ // HTML5 Page Visibility API. See details at
+ // {@code goog.labs.dom.PageVisibilityMonitor}.
+ VISIBILITYCHANGE: 'visibilitychange',
+
+ // LocalStorage event.
+ STORAGE: 'storage'
};
-
+}
+if(!lt.util.load.provided_QMARK_('goog.events.BrowserEvent')) {
+// 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.
/**
- * Removes all key value pairs from the object/map/hash.
+ * @fileoverview A patched, standardized event object for browser events.
+ *
+ *
+ * The patched event object contains the following members:
+ * - type {string} Event type, e.g. 'click'
+ * - timestamp {Date} A date object for when the event was fired
+ * - target {Object} The element that actually triggered the event
+ * - currentTarget {Object} The element the listener is attached to
+ * - relatedTarget {Object} For mouseover and mouseout, the previous object
+ * - offsetX {number} X-coordinate relative to target
+ * - offsetY {number} Y-coordinate relative to target
+ * - clientX {number} X-coordinate relative to viewport
+ * - clientY {number} Y-coordinate relative to viewport
+ * - screenX {number} X-coordinate relative to the edge of the screen
+ * - screenY {number} Y-coordinate relative to the edge of the screen
+ * - button {number} Mouse button. Use isButton() to test.
+ * - keyCode {number} Key-code
+ * - ctrlKey {boolean} Was ctrl key depressed
+ * - altKey {boolean} Was alt key depressed
+ * - shiftKey {boolean} Was shift key depressed
+ * - metaKey {boolean} Was meta key depressed
+ * - defaultPrevented {boolean} Whether the default action has been prevented
+ * - state {Object} History state object
+ *
+ * NOTE: The keyCode member contains the raw browser keyCode. For normalized
+ * key and character code use {@link goog.events.KeyHandler}.
+ *
*
- * @param {Object} obj The object to clear.
*/
-goog.object.clear = function(obj) {
- for (var i in obj) {
- delete obj[i];
- }
-};
+goog.provide('goog.events.BrowserEvent');
+goog.provide('goog.events.BrowserEvent.MouseButton');
+
+goog.require('goog.events.BrowserFeature');
+goog.require('goog.events.Event');
+goog.require('goog.events.EventType');
+goog.require('goog.reflect');
+goog.require('goog.userAgent');
-/**
- * Removes a key-value pair based on the key.
- *
- * @param {Object} obj The object from which to remove the key.
- * @param {*} key The key to remove.
- * @return {boolean} Whether an element was removed.
- */
-goog.object.remove = function(obj, key) {
- var rv;
- if ((rv = key in obj)) {
- delete obj[key];
- }
- return rv;
-};
/**
- * Adds a key-value pair to the object. Throws an exception if the key is
- * already in use. Use set if you want to change an existing pair.
- *
- * @param {Object.} obj The object to which to add the key-value pair.
- * @param {string} key The key to add.
- * @param {V} val The value to add.
- * @template K,V
+ * Accepts a browser event object and creates a patched, cross browser event
+ * object.
+ * The content of this object will not be initialized if no event object is
+ * provided. If this is the case, init() needs to be invoked separately.
+ * @param {Event=} opt_e Browser event object.
+ * @param {EventTarget=} opt_currentTarget Current target for event.
+ * @constructor
+ * @extends {goog.events.Event}
*/
-goog.object.add = function(obj, key, val) {
- if (key in obj) {
- throw Error('The object already contains the key "' + key + '"');
- }
- goog.object.set(obj, key, val);
-};
+goog.events.BrowserEvent = function(opt_e, opt_currentTarget) {
+ goog.events.BrowserEvent.base(this, 'constructor', opt_e ? opt_e.type : '');
+ /**
+ * Target that fired the event.
+ * @override
+ * @type {Node}
+ */
+ this.target = null;
-/**
- * Returns the value for the given key.
- *
- * @param {Object.} obj The object from which to get the value.
- * @param {string} key The key for which to get the value.
- * @param {R=} opt_val The value to return if no item is found for the given
- * key (default is undefined).
- * @return {V|R|undefined} The value for the given key.
- * @template K,V,R
- */
-goog.object.get = function(obj, key, opt_val) {
- if (key in obj) {
- return obj[key];
- }
- return opt_val;
-};
+ /**
+ * Node that had the listener attached.
+ * @override
+ * @type {Node|undefined}
+ */
+ this.currentTarget = null;
+ /**
+ * For mouseover and mouseout events, the related object for the event.
+ * @type {Node}
+ */
+ this.relatedTarget = null;
-/**
- * Adds a key-value pair to the object/map/hash.
- *
- * @param {Object.} obj The object to which to add the key-value pair.
- * @param {string} key The key to add.
- * @param {K} value The value to add.
- * @template K,V
- */
-goog.object.set = function(obj, key, value) {
- obj[key] = value;
-};
+ /**
+ * X-coordinate relative to target.
+ * @type {number}
+ */
+ this.offsetX = 0;
+ /**
+ * Y-coordinate relative to target.
+ * @type {number}
+ */
+ this.offsetY = 0;
-/**
- * Adds a key-value pair to the object/map/hash if it doesn't exist yet.
- *
- * @param {Object.} obj The object to which to add the key-value pair.
- * @param {string} key The key to add.
- * @param {V} value The value to add if the key wasn't present.
- * @return {V} The value of the entry at the end of the function.
- * @template K,V
- */
-goog.object.setIfUndefined = function(obj, key, value) {
- return key in obj ? obj[key] : (obj[key] = value);
-};
+ /**
+ * X-coordinate relative to the window.
+ * @type {number}
+ */
+ this.clientX = 0;
+ /**
+ * Y-coordinate relative to the window.
+ * @type {number}
+ */
+ this.clientY = 0;
-/**
- * Does a flat clone of the object.
- *
- * @param {Object.} obj Object to clone.
- * @return {!Object.} Clone of the input object.
- * @template K,V
- */
-goog.object.clone = function(obj) {
- // We cannot use the prototype trick because a lot of methods depend on where
- // the actual key is set.
+ /**
+ * X-coordinate relative to the monitor.
+ * @type {number}
+ */
+ this.screenX = 0;
- var res = {};
- for (var key in obj) {
- res[key] = obj[key];
- }
- return res;
- // We could also use goog.mixin but I wanted this to be independent from that.
-};
+ /**
+ * Y-coordinate relative to the monitor.
+ * @type {number}
+ */
+ this.screenY = 0;
+ /**
+ * Which mouse button was pressed.
+ * @type {number}
+ */
+ this.button = 0;
-/**
- * Clones a value. The input may be an Object, Array, or basic type. Objects and
- * arrays will be cloned recursively.
- *
- * WARNINGS:
- * goog.object.unsafeClone does not detect reference loops. Objects
- * that refer to themselves will cause infinite recursion.
- *
- * goog.object.unsafeClone is unaware of unique identifiers, and
- * copies UIDs created by getUid into cloned results.
- *
- * @param {*} obj The value to clone.
- * @return {*} A clone of the input value.
- */
-goog.object.unsafeClone = function(obj) {
- var type = goog.typeOf(obj);
- if (type == 'object' || type == 'array') {
- if (obj.clone) {
- return obj.clone();
- }
- var clone = type == 'array' ? [] : {};
- for (var key in obj) {
- clone[key] = goog.object.unsafeClone(obj[key]);
- }
- return clone;
- }
+ /**
+ * Keycode of key press.
+ * @type {number}
+ */
+ this.keyCode = 0;
- return obj;
-};
+ /**
+ * Keycode of key press.
+ * @type {number}
+ */
+ this.charCode = 0;
+ /**
+ * Whether control was pressed at time of event.
+ * @type {boolean}
+ */
+ this.ctrlKey = false;
-/**
- * Returns a new object in which all the keys and values are interchanged
- * (keys become values and values become keys). If multiple keys map to the
- * same value, the chosen transposed value is implementation-dependent.
- *
- * @param {Object} obj The object to transpose.
- * @return {!Object} The transposed object.
- */
-goog.object.transpose = function(obj) {
- var transposed = {};
- for (var key in obj) {
- transposed[obj[key]] = key;
+ /**
+ * Whether alt was pressed at time of event.
+ * @type {boolean}
+ */
+ this.altKey = false;
+
+ /**
+ * Whether shift was pressed at time of event.
+ * @type {boolean}
+ */
+ this.shiftKey = false;
+
+ /**
+ * Whether the meta key was pressed at time of event.
+ * @type {boolean}
+ */
+ this.metaKey = false;
+
+ /**
+ * History state object, only set for PopState events where it's a copy of the
+ * state object provided to pushState or replaceState.
+ * @type {Object}
+ */
+ this.state = null;
+
+ /**
+ * Whether the default platform modifier key was pressed at time of event.
+ * (This is control for all platforms except Mac, where it's Meta.)
+ * @type {boolean}
+ */
+ this.platformModifierKey = false;
+
+ /**
+ * The browser event object.
+ * @private {Event}
+ */
+ this.event_ = null;
+
+ if (opt_e) {
+ this.init(opt_e, opt_currentTarget);
}
- return transposed;
};
+goog.inherits(goog.events.BrowserEvent, goog.events.Event);
/**
- * The names of the fields that are defined on Object.prototype.
- * @type {Array.}
- * @private
+ * Normalized button constants for the mouse.
+ * @enum {number}
*/
-goog.object.PROTOTYPE_FIELDS_ = [
- 'constructor',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'toLocaleString',
- 'toString',
- 'valueOf'
+goog.events.BrowserEvent.MouseButton = {
+ LEFT: 0,
+ MIDDLE: 1,
+ RIGHT: 2
+};
+
+
+/**
+ * Static data for mapping mouse buttons.
+ * @type {!Array.}
+ */
+goog.events.BrowserEvent.IEButtonMap = [
+ 1, // LEFT
+ 4, // MIDDLE
+ 2 // RIGHT
];
/**
- * Extends an object with another object.
- * This operates 'in-place'; it does not create a new Object.
- *
- * Example:
- * var o = {};
- * goog.object.extend(o, {a: 0, b: 1});
- * o; // {a: 0, b: 1}
- * goog.object.extend(o, {c: 2});
- * o; // {a: 0, b: 1, c: 2}
- *
- * @param {Object} target The object to modify.
- * @param {...Object} var_args The objects from which values will be copied.
+ * Accepts a browser event object and creates a patched, cross browser event
+ * object.
+ * @param {Event} e Browser event object.
+ * @param {EventTarget=} opt_currentTarget Current target for event.
*/
-goog.object.extend = function(target, var_args) {
- var key, source;
- for (var i = 1; i < arguments.length; i++) {
- source = arguments[i];
- for (key in source) {
- target[key] = source[key];
- }
+goog.events.BrowserEvent.prototype.init = function(e, opt_currentTarget) {
+ var type = this.type = e.type;
- // For IE the for-in-loop does not contain any properties that are not
- // enumerable on the prototype object (for example isPrototypeOf from
- // Object.prototype) and it will also not include 'replace' on objects that
- // extend String and change 'replace' (not that it is common for anyone to
- // extend anything except Object).
+ // TODO(nicksantos): Change this.target to type EventTarget.
+ this.target = /** @type {Node} */ (e.target) || e.srcElement;
- for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) {
- key = goog.object.PROTOTYPE_FIELDS_[j];
- if (Object.prototype.hasOwnProperty.call(source, key)) {
- target[key] = source[key];
+ // TODO(nicksantos): Change this.currentTarget to type EventTarget.
+ this.currentTarget = /** @type {Node} */ (opt_currentTarget);
+
+ var relatedTarget = /** @type {Node} */ (e.relatedTarget);
+ if (relatedTarget) {
+ // There's a bug in FireFox where sometimes, relatedTarget will be a
+ // chrome element, and accessing any property of it will get a permission
+ // denied exception. See:
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=497780
+ if (goog.userAgent.GECKO) {
+ if (!goog.reflect.canAccessProperty(relatedTarget, 'nodeName')) {
+ relatedTarget = null;
}
}
- }
-};
-
-
-/**
- * Creates a new object built from the key-value pairs provided as arguments.
- * @param {...*} var_args If only one argument is provided and it is an array
- * then this is used as the arguments, otherwise even arguments are used as
- * the property names and odd arguments are used as the property values.
- * @return {!Object} The new object.
- * @throws {Error} If there are uneven number of arguments or there is only one
- * non array argument.
- */
-goog.object.create = function(var_args) {
- var argLength = arguments.length;
- if (argLength == 1 && goog.isArray(arguments[0])) {
- return goog.object.create.apply(null, arguments[0]);
+ // TODO(arv): Use goog.events.EventType when it has been refactored into its
+ // own file.
+ } else if (type == goog.events.EventType.MOUSEOVER) {
+ relatedTarget = e.fromElement;
+ } else if (type == goog.events.EventType.MOUSEOUT) {
+ relatedTarget = e.toElement;
}
- if (argLength % 2) {
- throw Error('Uneven number of arguments');
- }
+ this.relatedTarget = relatedTarget;
- var rv = {};
- for (var i = 0; i < argLength; i += 2) {
- rv[arguments[i]] = arguments[i + 1];
- }
- return rv;
-};
+ // Webkit emits a lame warning whenever layerX/layerY is accessed.
+ // http://code.google.com/p/chromium/issues/detail?id=101733
+ this.offsetX = (goog.userAgent.WEBKIT || e.offsetX !== undefined) ?
+ e.offsetX : e.layerX;
+ this.offsetY = (goog.userAgent.WEBKIT || e.offsetY !== undefined) ?
+ e.offsetY : e.layerY;
+ this.clientX = e.clientX !== undefined ? e.clientX : e.pageX;
+ this.clientY = e.clientY !== undefined ? e.clientY : e.pageY;
+ this.screenX = e.screenX || 0;
+ this.screenY = e.screenY || 0;
-/**
- * Creates a new object where the property names come from the arguments but
- * the value is always set to true
- * @param {...*} var_args If only one argument is provided and it is an array
- * then this is used as the arguments, otherwise the arguments are used
- * as the property names.
- * @return {!Object} The new object.
- */
-goog.object.createSet = function(var_args) {
- var argLength = arguments.length;
- if (argLength == 1 && goog.isArray(arguments[0])) {
- return goog.object.createSet.apply(null, arguments[0]);
- }
+ this.button = e.button;
- var rv = {};
- for (var i = 0; i < argLength; i++) {
- rv[arguments[i]] = true;
+ this.keyCode = e.keyCode || 0;
+ this.charCode = e.charCode || (type == 'keypress' ? e.keyCode : 0);
+ this.ctrlKey = e.ctrlKey;
+ this.altKey = e.altKey;
+ this.shiftKey = e.shiftKey;
+ this.metaKey = e.metaKey;
+ this.platformModifierKey = goog.userAgent.MAC ? e.metaKey : e.ctrlKey;
+ this.state = e.state;
+ this.event_ = e;
+ if (e.defaultPrevented) {
+ this.preventDefault();
}
- return rv;
};
/**
- * Creates an immutable view of the underlying object, if the browser
- * supports immutable objects.
+ * Tests to see which button was pressed during the event. This is really only
+ * useful in IE and Gecko browsers. And in IE, it's only useful for
+ * mousedown/mouseup events, because click only fires for the left mouse button.
*
- * In default mode, writes to this view will fail silently. In strict mode,
- * they will throw an error.
+ * Safari 2 only reports the left button being clicked, and uses the value '1'
+ * instead of 0. Opera only reports a mousedown event for the middle button, and
+ * no mouse events for the right button. Opera has default behavior for left and
+ * middle click that can only be overridden via a configuration setting.
*
- * @param {!Object.} obj An object.
- * @return {!Object.} An immutable view of that object, or the
- * original object if this browser does not support immutables.
- * @template K,V
+ * There's a nice table of this mess at http://www.unixpapa.com/js/mouse.html.
+ *
+ * @param {goog.events.BrowserEvent.MouseButton} button The button
+ * to test for.
+ * @return {boolean} True if button was pressed.
*/
-goog.object.createImmutableView = function(obj) {
- var result = obj;
- if (Object.isFrozen && !Object.isFrozen(obj)) {
- result = Object.create(obj);
- Object.freeze(result);
+goog.events.BrowserEvent.prototype.isButton = function(button) {
+ if (!goog.events.BrowserFeature.HAS_W3C_BUTTON) {
+ if (this.type == 'click') {
+ return button == goog.events.BrowserEvent.MouseButton.LEFT;
+ } else {
+ return !!(this.event_.button &
+ goog.events.BrowserEvent.IEButtonMap[button]);
+ }
+ } else {
+ return this.event_.button == button;
}
- return result;
};
/**
- * @param {!Object} obj An object.
- * @return {boolean} Whether this is an immutable view of the object.
- */
-goog.object.isImmutableView = function(obj) {
- return !!Object.isFrozen && Object.isFrozen(obj);
-};
-}
-if(!lt.util.load.provided_QMARK_('goog.events.EventWrapper')) {
-// Copyright 2009 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 Definition of the goog.events.EventWrapper interface.
+ * Whether this has an "action"-producing mouse button.
+ *
+ * By definition, this includes left-click on windows/linux, and left-click
+ * without the ctrl key on Macs.
*
- * @author eae@google.com (Emil A Eklund)
+ * @return {boolean} The result.
*/
-
-goog.provide('goog.events.EventWrapper');
-
+goog.events.BrowserEvent.prototype.isMouseActionButton = function() {
+ // Webkit does not ctrl+click to be a right-click, so we
+ // normalize it to behave like Gecko and Opera.
+ return this.isButton(goog.events.BrowserEvent.MouseButton.LEFT) &&
+ !(goog.userAgent.WEBKIT && goog.userAgent.MAC && this.ctrlKey);
+};
/**
- * Interface for event wrappers.
- * @interface
+ * @override
*/
-goog.events.EventWrapper = function() {
+goog.events.BrowserEvent.prototype.stopPropagation = function() {
+ goog.events.BrowserEvent.superClass_.stopPropagation.call(this);
+ if (this.event_.stopPropagation) {
+ this.event_.stopPropagation();
+ } else {
+ this.event_.cancelBubble = true;
+ }
};
/**
- * Adds an event listener using the 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 {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_scope Element in whose scope to call the listener.
- * @param {goog.events.EventHandler=} opt_eventHandler Event handler to add
- * listener to.
+ * @override
*/
-goog.events.EventWrapper.prototype.listen = function(src, listener, opt_capt,
- opt_scope, opt_eventHandler) {
+goog.events.BrowserEvent.prototype.preventDefault = function() {
+ goog.events.BrowserEvent.superClass_.preventDefault.call(this);
+ var be = this.event_;
+ if (!be.preventDefault) {
+ be.returnValue = false;
+ if (goog.events.BrowserFeature.SET_KEY_CODE_TO_PREVENT_DEFAULT) {
+ /** @preserveTry */
+ try {
+ // Most keys can be prevented using returnValue. Some special keys
+ // require setting the keyCode to -1 as well:
+ //
+ // In IE7:
+ // F3, F5, F10, F11, Ctrl+P, Crtl+O, Ctrl+F (these are taken from IE6)
+ //
+ // In IE8:
+ // Ctrl+P, Crtl+O, Ctrl+F (F1-F12 cannot be stopped through the event)
+ //
+ // We therefore do this for all function keys as well as when Ctrl key
+ // is pressed.
+ var VK_F1 = 112;
+ var VK_F12 = 123;
+ if (be.ctrlKey || be.keyCode >= VK_F1 && be.keyCode <= VK_F12) {
+ be.keyCode = -1;
+ }
+ } catch (ex) {
+ // IE throws an 'access denied' exception when trying to change
+ // keyCode in some situations (e.g. srcElement is input[type=file],
+ // or srcElement is an anchor tag rewritten by parent's innerHTML).
+ // Do nothing in this case.
+ }
+ }
+ } else {
+ be.preventDefault();
+ }
};
/**
- * Removes an event listener added using goog.events.EventWrapper.listen.
- *
- * @param {EventTarget|goog.events.EventTarget} src The node to remove listener
- * from.
- * @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_scope Element in whose scope to call the listener.
- * @param {goog.events.EventHandler=} opt_eventHandler Event handler to remove
- * listener from.
+ * @return {Event} The underlying browser event object.
*/
-goog.events.EventWrapper.prototype.unlisten = function(src, listener, opt_capt,
- opt_scope, opt_eventHandler) {
+goog.events.BrowserEvent.prototype.getBrowserEvent = function() {
+ return this.event_;
};
-}
-if(!lt.util.load.provided_QMARK_('goog.debug.errorHandlerWeakDep')) {
-// Copyright 2008 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 File which defines dummy object to work around undefined
- * properties compiler warning for weak dependencies on
- * {@link goog.debug.ErrorHandler#protectEntryPoint}.
- *
- */
-
-goog.provide('goog.debug.errorHandlerWeakDep');
-/**
- * Dummy object to work around undefined properties compiler warning.
- * @type {Object}
- */
-goog.debug.errorHandlerWeakDep = {
- /**
- * @param {Function} fn An entry point function to be protected.
- * @param {boolean=} opt_tracers Whether to install tracers around the
- * fn.
- * @return {Function} A protected wrapper function that calls the
- * entry point function.
- */
- protectEntryPoint: function(fn, opt_tracers) { return fn; }
+/** @override */
+goog.events.BrowserEvent.prototype.disposeInternal = function() {
};
}
if(!lt.util.load.provided_QMARK_('goog.events')) {
@@ -3542,17 +3954,21 @@ if(!lt.util.load.provided_QMARK_('goog.events')) {
// limitations under the License.
/**
- * @fileoverview Event Manager.
- *
- * Provides an abstracted interface to the browsers' event
- * systems. This uses an indirect lookup of listener functions to avoid circular
- * references between DOM (in IE) or XPCOM (in Mozilla) objects which leak
- * memory. This makes it easier to write OO Javascript/DOM code.
+ * @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 simulates capture & bubble in Internet Explorer.
+ * 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 also automagically have their event objects patched, so
- * your handlers don't need to worry about the browser.
+ * 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:
*
@@ -3560,42 +3976,33 @@ if(!lt.util.load.provided_QMARK_('goog.events')) {
* goog.events.listen(myNode, 'mouseover', mouseHandler, true);
* goog.events.unlisten(myNode, 'mouseover', mouseHandler, true);
* goog.events.removeAll(myNode);
- * goog.events.removeAll();
*
*
* in IE and event object patching]
*
- * @supported IE6+, FF1.5+, WebKit, Opera.
* @see ../demos/events.html
* @see ../demos/event-propagation.html
* @see ../demos/stopevent.html
*/
-
-// This uses 3 lookup tables/trees.
-// listenerTree_ is a tree of type -> capture -> src uid -> [Listener]
-// listeners_ is a map of key -> [Listener]
-//
-// The key is a field of the Listener. The Listener class also has the type,
-// capture and the src so one can always trace back in the tree
-//
-// sources_: src uid -> [Listener]
-
+// 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.array');
+goog.require('goog.asserts');
goog.require('goog.debug.entryPointRegistry');
-goog.require('goog.debug.errorHandlerWeakDep');
goog.require('goog.events.BrowserEvent');
goog.require('goog.events.BrowserFeature');
-goog.require('goog.events.Event');
-goog.require('goog.events.EventWrapper');
goog.require('goog.events.Listenable');
-goog.require('goog.events.Listener');
-goog.require('goog.object');
-goog.require('goog.userAgent');
+goog.require('goog.events.ListenerMap');
/**
@@ -3605,80 +4012,108 @@ goog.events.Key;
/**
- * @typedef {EventTarget|goog.events.Listenable|goog.events.EventTarget}
+ * @typedef {EventTarget|goog.events.Listenable}
*/
goog.events.ListenableType;
/**
* Container for storing event listeners and their proxies
- * @private
- * @type {Object.}
+ *
+ * TODO(user): Remove this when all external usage is
+ * purged. goog.events no longer use goog.events.listeners_ for
+ * anything meaningful.
+ *
+ * @private {!Object.}
*/
goog.events.listeners_ = {};
/**
- * The root of the listener tree
+ * Property name on a native event target for the listener map
+ * associated with the event target.
+ * @const
* @private
- * @type {Object}
*/
-goog.events.listenerTree_ = {};
+goog.events.LISTENER_MAP_PROP_ = 'closure_lm_' + ((Math.random() * 1e6) | 0);
/**
- * Lookup for mapping source UIDs to listeners.
+ * String used to prepend to IE event types.
+ * @const
* @private
- * @type {Object}
*/
-goog.events.sources_ = {};
+goog.events.onString_ = 'on';
/**
- * String used to prepend to IE event types. Not a constant so that it is not
- * inlined.
- * @type {string}
+ * Map of computed "on" 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.onString_ = 'on';
+goog.events.onStringMap_ = {};
/**
- * Map of computed on strings for IE event types. Caching this removes an extra
- * object allocation in goog.events.listen which improves IE6 performance.
- * @type {Object}
- * @private
+ * @enum {number} Different capture simulation mode for IE8-.
*/
-goog.events.onStringMap_ = {};
+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
+};
/**
- * Separator used to split up the various parts of an event key, to help avoid
- * the possibilities of collisions.
- * @type {string}
- * @private
+ * @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.keySeparator_ = '_';
+goog.events.listenerCountEstimate_ = 0;
/**
- * Adds an event listener for a specific event on a DOM Node or an
- * object that has implemented {@link goog.events.EventTarget}. 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().
+ * 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 {goog.events.ListenableType} src The node to listen to
- * events on.
+ * @param {EventTarget|goog.events.Listenable} src The node to listen
+ * to events on.
* @param {string|Array.} type Event type or array of event types.
- * @param {Function|Object} listener Callback method, or an object with a
- * handleEvent function.
+ * @param {function(this:T, ?):?|{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 {Object=} opt_handler Element in whose scope to call the listener.
+ * @param {T=} opt_handler Element in whose scope to call the listener.
* @return {goog.events.Key} Unique key for the listener.
+ * @template T
*/
goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
if (goog.isArray(type)) {
@@ -3688,39 +4123,30 @@ goog.events.listen = function(src, type, listener, opt_capt, opt_handler) {
return null;
}
- var listenableKey;
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(src)) {
- listenableKey = src.listen(
- /** @type {string} */ (type),
- goog.events.wrapListener_(listener), opt_capt, opt_handler);
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ return src.listen(
+ /** @type {string} */ (type), listener, opt_capt, opt_handler);
} else {
- listenableKey = goog.events.listen_(
- /** @type {EventTarget|goog.events.EventTarget} */ (src),
+ return goog.events.listen_(
+ /** @type {EventTarget} */ (src),
type, listener, /* callOnce */ false, opt_capt, opt_handler);
}
-
- var key = listenableKey.key;
- goog.events.listeners_[key] = listenableKey;
- return key;
};
/**
- * Adds an event listener for a specific event on a DOM Node or an object that
- * has implemented {@link goog.events.EventTarget}. A listener can only be
- * added once to an object and if it is added again the key for the listener
- * is returned.
+ * 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|goog.events.EventTarget} src The node to listen to
- * events on.
+ * @param {EventTarget} src The node to listen to events on.
* @param {?string} type Event type or array of event types.
- * @param {Function|Object} listener Callback method, or an object with a
- * handleEvent function.
+ * @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
@@ -3736,83 +4162,41 @@ goog.events.listen_ = function(
}
var capture = !!opt_capt;
- var map = goog.events.listenerTree_;
-
- if (!(type in map)) {
- map[type] = {count_: 0, remaining_: 0};
+ 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;
+ }
}
- map = map[type];
- if (!(capture in map)) {
- map[capture] = {count_: 0, remaining_: 0};
- map.count_++;
+ var listenerMap = goog.events.getListenerMap_(src);
+ if (!listenerMap) {
+ src[goog.events.LISTENER_MAP_PROP_] = listenerMap =
+ new goog.events.ListenerMap(src);
}
- map = map[capture];
-
- var srcUid = goog.getUid(src);
- var listenerArray, listenerObj;
-
- // The remaining_ property is used to be able to short circuit the iteration
- // of the event listeners.
- //
- // Increment the remaining event listeners to call even if this event might
- // already have been fired. At this point we do not know if the event has
- // been fired and it is too expensive to find out. By incrementing it we are
- // guaranteed that we will not skip any event listeners.
- map.remaining_++;
-
- // Do not use srcUid in map here since that will cast the number to a
- // string which will allocate one string object.
- if (!map[srcUid]) {
- listenerArray = map[srcUid] = [];
- map.count_++;
- } else {
- listenerArray = map[srcUid];
- // Ensure that the listeners do not already contain the current listener
- for (var i = 0; i < listenerArray.length; i++) {
- listenerObj = listenerArray[i];
- if (listenerObj.listener == listener &&
- listenerObj.handler == opt_handler) {
-
- // If this listener has been removed we should not return its key. It
- // is OK that we create new listenerObj below since the removed one
- // will be cleaned up later.
- if (listenerObj.removed) {
- break;
- }
- if (!callOnce) {
- // Ensure that, if there is an existing callOnce listener, it is no
- // longer a callOnce listener.
- listenerArray[i].callOnce = false;
- }
+ var listenerObj = listenerMap.add(
+ type, listener, callOnce, opt_capt, opt_handler);
- // We already have this listener. Return its key.
- return listenerArray[i];
- }
- }
+ // 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 = new goog.events.Listener();
- listenerObj.init(listener, proxy, src, type, capture, opt_handler);
- listenerObj.callOnce = callOnce;
+ listenerObj.proxy = proxy;
proxy.src = src;
proxy.listener = listenerObj;
- listenerArray.push(listenerObj);
-
- if (!goog.events.sources_[srcUid]) {
- goog.events.sources_[srcUid] = [];
- }
- goog.events.sources_[srcUid].push(listenerObj);
-
// Attach the proxy through the browser's API
if (src.addEventListener) {
- if (src == goog.global || !src.customEvent_) {
- src.addEventListener(type, proxy, capture);
- }
+ src.addEventListener(type, 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
@@ -3822,6 +4206,7 @@ goog.events.listen_ = function(
src.attachEvent(goog.events.getOnString_(type), proxy);
}
+ goog.events.listenerCountEstimate_++;
return listenerObj;
};
@@ -3852,9 +4237,10 @@ goog.events.getProxy = function() {
/**
- * Adds an event listener for a specific event on a DomNode or an object that
- * has implemented {@link goog.events.EventTarget}. After the event has fired
- * the event listener is removed from the target.
+ * 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
@@ -3863,13 +4249,15 @@ goog.events.getProxy = function() {
* one-off listener, listenOnce does not modify the listeners (it is
* still a once listener).
*
- * @param {goog.events.ListenableType} src The node to listen to
- * events on.
+ * @param {EventTarget|goog.events.Listenable} src The node to listen
+ * to events on.
* @param {string|Array.} type Event type or array of event types.
- * @param {Function|Object} listener Callback method.
+ * @param {function(this:T, ?):?|{handleEvent:function(?):?}|null} listener
+ * Callback method.
* @param {boolean=} opt_capt Fire in capture phase?.
- * @param {Object=} opt_handler Element in whose scope to call the listener.
+ * @param {T=} opt_handler Element in whose scope to call the listener.
* @return {goog.events.Key} Unique key for the listener.
+ * @template T
*/
goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
if (goog.isArray(type)) {
@@ -3879,37 +4267,32 @@ goog.events.listenOnce = function(src, type, listener, opt_capt, opt_handler) {
return null;
}
- var listenableKey;
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(src)) {
- listenableKey = src.listenOnce(
- /** @type {string} */ (type),
- goog.events.wrapListener_(listener), opt_capt, opt_handler);
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
+ return src.listenOnce(
+ /** @type {string} */ (type), listener, opt_capt, opt_handler);
} else {
- listenableKey = goog.events.listen_(
- /** @type {EventTarget|goog.events.EventTarget} */ (src),
+ return goog.events.listen_(
+ /** @type {EventTarget} */ (src),
type, listener, /* callOnce */ true, opt_capt, opt_handler);
}
-
- var key = listenableKey.key;
- goog.events.listeners_[key] = listenableKey;
- return key;
};
/**
* 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
+ * object that has implemented {@link goog.events.Listenable}. A listener can
* only be added once to an object.
*
- * @param {EventTarget|goog.events.EventTarget} src The node to listen to
- * events on.
+ * @param {EventTarget|goog.events.Listenable} src The target 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 {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 {Object=} opt_handler Element in whose scope to call the listener.
+ * @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) {
@@ -3920,11 +4303,12 @@ goog.events.listenWithWrapper = function(src, wrapper, listener, opt_capt,
/**
* Removes an event listener which was added with listen().
*
- * @param {goog.events.ListenableType} src The target to stop
+ * @param {EventTarget|goog.events.Listenable} src The target to stop
* listening to events on.
* @param {string|Array.} type The name of the event without the 'on'
* prefix.
- * @param {Function|Object} listener The listener function to remove.
+ * @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.
@@ -3939,25 +4323,26 @@ goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
return null;
}
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(src)) {
+ listener = goog.events.wrapListener(listener);
+ if (goog.events.Listenable.isImplementedBy(src)) {
return src.unlisten(
- /** @type {string} */ (type),
- goog.events.wrapListener_(listener), opt_capt, opt_handler);
+ /** @type {string} */ (type), listener, opt_capt, opt_handler);
}
- var capture = !!opt_capt;
-
- var listenerArray = goog.events.getListeners_(src, type, capture);
- if (!listenerArray) {
+ if (!src) {
+ // TODO(user): We should tighten the API to only accept
+ // non-null objects, or add an assertion here.
return false;
}
- for (var i = 0; i < listenerArray.length; i++) {
- if (listenerArray[i].listener == listener &&
- listenerArray[i].capture == capture &&
- listenerArray[i].handler == opt_handler) {
- return goog.events.unlistenByKey(listenerArray[i].key);
+ var capture = !!opt_capt;
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (src));
+ if (listenerMap) {
+ var listenerObj = listenerMap.getListener(
+ /** @type {string} */ (type), listener, capture, opt_handler);
+ if (listenerObj) {
+ return goog.events.unlistenByKey(listenerObj);
}
}
@@ -3974,72 +4359,49 @@ goog.events.unlisten = function(src, type, listener, opt_capt, opt_handler) {
* @return {boolean} indicating whether the listener was there to remove.
*/
goog.events.unlistenByKey = function(key) {
- // TODO(user): When we flip goog.events.Key to be ListenableKey,
- // we need to change this.
- var listener = goog.events.listeners_[key];
- if (!listener) {
+ // TODO(user): Remove this check when tests that rely on this
+ // are fixed.
+ if (goog.isNumber(key)) {
return false;
}
- if (listener.removed) {
+
+ var listener = /** @type {goog.events.ListenableKey} */ (key);
+ if (!listener || listener.removed) {
return false;
}
var src = listener.src;
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(src)) {
+ if (goog.events.Listenable.isImplementedBy(src)) {
return src.unlistenByKey(listener);
}
var type = listener.type;
var proxy = listener.proxy;
- var capture = listener.capture;
-
if (src.removeEventListener) {
- // EventTarget calls unlisten so we need to ensure that the source is not
- // an event target to prevent re-entry.
- // TODO(arv): What is this goog.global for? Why would anyone listen to
- // events on the [[Global]] object? Is it supposed to be window? Why would
- // we not want to allow removing event listeners on the window?
- if (src == goog.global || !src.customEvent_) {
- src.removeEventListener(type, proxy, capture);
- }
+ src.removeEventListener(type, proxy, listener.capture);
} else if (src.detachEvent) {
src.detachEvent(goog.events.getOnString_(type), proxy);
}
-
- var srcUid = goog.getUid(src);
-
- // In a perfect implementation we would decrement the remaining_ field here
- // but then we would need to know if the listener has already been fired or
- // not. We therefore skip doing this and in this uncommon case the entire
- // ancestor chain will need to be traversed as before.
-
- // Remove from sources_
- if (goog.events.sources_[srcUid]) {
- var sourcesArray = goog.events.sources_[srcUid];
- goog.array.remove(sourcesArray, listener);
- if (sourcesArray.length == 0) {
- delete goog.events.sources_[srcUid];
+ 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();
}
- listener.removed = true;
-
- // There are some esoteric situations where the hash code of an object
- // can change, and we won't be able to find the listenerArray anymore.
- // For example, if you're listening on a window, and the user navigates to
- // a different window, the UID will disappear.
- //
- // It should be impossible to ever find the original listenerArray, so it
- // doesn't really matter if we can't clean it up in this case.
- var listenerArray = goog.events.listenerTree_[type][capture][srcUid];
- if (listenerArray) {
- listenerArray.needsCleanup_ = true;
- goog.events.cleanUp_(type, capture, srcUid, listenerArray);
- }
-
- delete goog.events.listeners_[key];
-
return true;
};
@@ -4047,10 +4409,11 @@ goog.events.unlistenByKey = function(key) {
/**
* Removes an event listener which was added with listenWithWrapper().
*
- * @param {EventTarget|goog.events.EventTarget} src The target to stop
+ * @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|Object} listener The listener function to remove.
+ * @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.
@@ -4063,123 +4426,62 @@ goog.events.unlistenWithWrapper = function(src, wrapper, listener, opt_capt,
/**
- * Cleans up goog.events internal data structure. This should be
- * called by all implementations of goog.events.Listenable when
- * removing listeners.
+ * Removes all listeners from an object. You can also optionally
+ * remove listeners of a particular type.
*
- * TODO(user): Once we remove numeric key support from
- * goog.events.listen and friend, we will be able to remove this
- * requirement.
- *
- * @param {goog.events.ListenableKey} listenableKey The key to clean up.
- */
-goog.events.cleanUp = function(listenableKey) {
- delete goog.events.listeners_[listenableKey.key];
-};
-
-
-/**
- * Cleans up the listener array as well as the listener tree
- * @param {string} type The type of the event.
- * @param {boolean} capture Whether to clean up capture phase listeners instead
- * bubble phase listeners.
- * @param {number} srcUid The unique ID of the source.
- * @param {Array.} listenerArray The array being cleaned.
- * @private
+ * @param {Object=} opt_obj Object to remove listeners from. Not
+ * specifying opt_obj is now DEPRECATED (it used to remove all
+ * registered listeners).
+ * @param {string=} opt_type Type of event to, default is all types.
+ * @return {number} Number of listeners removed.
*/
-goog.events.cleanUp_ = function(type, capture, srcUid, listenerArray) {
- // The listener array gets locked during the dispatch phase so that removals
- // of listeners during this phase does not screw up the indeces. This method
- // is called after we have removed a listener as well as after the dispatch
- // phase in case any listeners were removed.
- if (!listenerArray.locked_) { // catches both 0 and not set
- if (listenerArray.needsCleanup_) {
- // Loop over the listener array and remove listeners that have removed set
- // to true. This could have been done with filter or something similar but
- // we want to change the array in place and we want to minimize
- // allocations. Adding a listener during this phase adds to the end of the
- // array so that works fine as long as the length is rechecked every in
- // iteration.
- for (var oldIndex = 0, newIndex = 0;
- oldIndex < listenerArray.length;
- oldIndex++) {
- if (listenerArray[oldIndex].removed) {
- var proxy = listenerArray[oldIndex].proxy;
- proxy.src = null;
- continue;
- }
- if (oldIndex != newIndex) {
- listenerArray[newIndex] = listenerArray[oldIndex];
- }
- newIndex++;
- }
- listenerArray.length = newIndex;
-
- listenerArray.needsCleanup_ = false;
+goog.events.removeAll = function(opt_obj, opt_type) {
+ // TODO(user): Change the type of opt_obj from Object= to
+ // !EventTarget|goog.events.Listenable). And replace this with an
+ // assertion.
+ if (!opt_obj) {
+ return 0;
+ }
- // In case the length is now zero we release the object.
- if (newIndex == 0) {
- delete goog.events.listenerTree_[type][capture][srcUid];
- goog.events.listenerTree_[type][capture].count_--;
+ if (goog.events.Listenable.isImplementedBy(opt_obj)) {
+ return opt_obj.removeAllListeners(opt_type);
+ }
- if (goog.events.listenerTree_[type][capture].count_ == 0) {
- delete goog.events.listenerTree_[type][capture];
- goog.events.listenerTree_[type].count_--;
- }
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (opt_obj));
+ if (!listenerMap) {
+ return 0;
+ }
- if (goog.events.listenerTree_[type].count_ == 0) {
- delete goog.events.listenerTree_[type];
+ var count = 0;
+ for (var type in listenerMap.listeners) {
+ if (!opt_type || type == opt_type) {
+ // Clone so that we don't need to worry about unlistenByKey
+ // changing the content of the ListenerMap.
+ var listeners = goog.array.clone(listenerMap.listeners[type]);
+ for (var i = 0; i < listeners.length; ++i) {
+ if (goog.events.unlistenByKey(listeners[i])) {
+ ++count;
}
}
-
}
}
+ return count;
};
/**
- * Removes all listeners from an object, if no object is specified it will
- * remove all listeners that have been registered. You can also optionally
- * remove listeners of a particular type or capture phase.
- *
- * removeAll() will not remove listeners registered directly on a
- * goog.events.Listenable and listeners registered via add(Once)Listener.
- *
- * @param {Object=} opt_obj Object to remove listeners from.
- * @param {string=} opt_type Type of event to, default is all types.
+ * 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.removeAll = function(opt_obj, opt_type) {
- var count = 0;
-
- var noObj = opt_obj == null;
- var noType = opt_type == null;
-
- if (!noObj) {
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- opt_obj && goog.events.Listenable.isImplementedBy(opt_obj)) {
- return opt_obj.removeAllListeners(opt_type);
- }
-
- var srcUid = goog.getUid(/** @type {Object} */ (opt_obj));
- if (goog.events.sources_[srcUid]) {
- var sourcesArray = goog.events.sources_[srcUid];
- for (var i = sourcesArray.length - 1; i >= 0; i--) {
- var listener = sourcesArray[i];
- if (noType || opt_type == listener.type) {
- goog.events.unlistenByKey(listener.key);
- count++;
- }
- }
- }
- } else {
- goog.object.forEach(goog.events.listeners_, function(listener, key) {
- goog.events.unlistenByKey(key);
- count++;
- });
- }
-
- return count;
+goog.events.removeAllNativeListeners = function() {
+ goog.events.listenerCountEstimate_ = 0;
+ return 0;
};
@@ -4192,39 +4494,19 @@ goog.events.removeAll = function(opt_obj, opt_type) {
* @return {Array.} Array of listener objects.
*/
goog.events.getListeners = function(obj, type, capture) {
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(obj)) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
return obj.getListeners(type, capture);
} else {
- return goog.events.getListeners_(obj, type, capture) || [];
- }
-};
-
-
-/**
- * Gets the listeners for a given object, type and capture phase.
- *
- * @param {Object} obj Object to get listeners for.
- * @param {?string} type Event type.
- * @param {boolean} capture Capture phase?.
- * @return {Array.?} Array of listener objects.
- * Returns null if object has no listeners of that type.
- * @private
- */
-goog.events.getListeners_ = function(obj, type, capture) {
- var map = goog.events.listenerTree_;
- if (type in map) {
- map = map[type];
- if (capture in map) {
- map = map[capture];
- var objUid = goog.getUid(obj);
- if (map[objUid]) {
- return map[objUid];
- }
+ if (!obj) {
+ // TODO(user): We should tighten the API to accept
+ // !EventTarget|goog.events.Listenable, and add an assertion here.
+ return [];
}
- }
- return null;
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (obj));
+ return listenerMap ? listenerMap.getListeners(type, capture) : [];
+ }
};
@@ -4232,10 +4514,11 @@ goog.events.getListeners_ = function(obj, type, capture) {
* Gets the goog.events.Listener for the event or null if no such listener is
* in use.
*
- * @param {EventTarget|goog.events.EventTarget} src The node from which to get
- * listeners.
+ * @param {EventTarget|goog.events.Listenable} src The target from
+ * which to get listeners.
* @param {?string} type The name of the event without the 'on' prefix.
- * @param {Function|Object} listener The listener function to get.
+ * @param {function(?):?|{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.
@@ -4243,29 +4526,24 @@ goog.events.getListeners_ = function(obj, type, capture) {
* @return {goog.events.ListenableKey} the found listener or null if not found.
*/
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 (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(src)) {
- return src.getListener(
- /** @type {string} */ (type),
- goog.events.wrapListener_(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 listenerArray = goog.events.getListeners_(src, type, capture);
- if (listenerArray) {
- for (var i = 0; i < listenerArray.length; i++) {
- // If goog.events.unlistenByKey is called during an event dispatch
- // then the listener array won't get cleaned up and there might be
- // 'removed' listeners in the list. Ignore those.
- if (!listenerArray[i].removed &&
- listenerArray[i].listener == listener &&
- listenerArray[i].capture == capture &&
- listenerArray[i].handler == opt_handler) {
- // We already have this listener. Return its key.
- return listenerArray[i];
- }
- }
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (src));
+ if (listenerMap) {
+ return listenerMap.getListener(type, listener, capture, opt_handler);
}
return null;
};
@@ -4276,7 +4554,8 @@ goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
* specified signature. If either the type or capture parameters are
* unspecified, the function will match on the remaining criteria.
*
- * @param {EventTarget|goog.events.EventTarget} obj Target to get listeners for.
+ * @param {EventTarget|goog.events.Listenable} obj Target to get
+ * listeners for.
* @param {string=} opt_type Event type.
* @param {boolean=} opt_capture Whether to check for capture or bubble-phase
* listeners.
@@ -4284,37 +4563,13 @@ goog.events.getListener = function(src, type, listener, opt_capt, opt_handler) {
* the requested type and/or capture phase.
*/
goog.events.hasListener = function(obj, opt_type, opt_capture) {
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(obj)) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
return obj.hasListener(opt_type, opt_capture);
}
- var objUid = goog.getUid(obj);
- var listeners = goog.events.sources_[objUid];
-
- if (listeners) {
- var hasType = goog.isDef(opt_type);
- var hasCapture = goog.isDef(opt_capture);
-
- if (hasType && hasCapture) {
- // Lookup in the listener tree whether the specified listener exists.
- var map = goog.events.listenerTree_[opt_type];
- return !!map && !!map[opt_capture] && objUid in map[opt_capture];
-
- } else if (!(hasType || hasCapture)) {
- // Simple check for whether the event target has any listeners at all.
- return true;
-
- } else {
- // Iterate through the listeners for the event target to find a match.
- return goog.array.some(listeners, function(listener) {
- return (hasType && listener.type == opt_type) ||
- (hasCapture && listener.capture == opt_capture);
- });
- }
- }
-
- return false;
+ var listenerMap = goog.events.getListenerMap_(
+ /** @type {EventTarget} */ (obj));
+ return !!listenerMap && listenerMap.hasListener(opt_type, opt_capture);
};
@@ -4337,10 +4592,10 @@ goog.events.expose = function(e) {
/**
- * Returns a string wth on prepended to the specified type. This is used for IE
+ * 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 strng.
+ * @param {string} type Event type.
* @return {string} The type string with 'on' prepended.
* @private
*/
@@ -4362,27 +4617,16 @@ goog.events.getOnString_ = function(type) {
* @return {boolean} True if all listeners returned true else false.
*/
goog.events.fireListeners = function(obj, type, capture, eventObject) {
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE &&
- goog.events.Listenable.isImplementedBy(obj)) {
+ if (goog.events.Listenable.isImplementedBy(obj)) {
return obj.fireListeners(type, capture, eventObject);
}
- var map = goog.events.listenerTree_;
- if (type in map) {
- map = map[type];
- if (capture in map) {
- return goog.events.fireListeners_(map[capture], obj, type,
- capture, eventObject);
- }
- }
- return true;
+ return goog.events.fireListeners_(obj, type, capture, eventObject);
};
/**
* Fires an object's listeners of a particular type and phase.
- *
- * @param {Object} map Object with listeners in it.
* @param {Object} obj Object whose listeners to call.
* @param {string} type Event type.
* @param {boolean} capture Which event phase.
@@ -4390,45 +4634,29 @@ goog.events.fireListeners = function(obj, type, capture, eventObject) {
* @return {boolean} True if all listeners returned true else false.
* @private
*/
-goog.events.fireListeners_ = function(map, obj, type, capture, eventObject) {
+goog.events.fireListeners_ = function(obj, type, capture, eventObject) {
var retval = 1;
- var objUid = goog.getUid(obj);
- if (map[objUid]) {
- var remaining = --map.remaining_;
- var listenerArray = map[objUid];
-
- // If locked_ is not set (and if already 0) initialize it to 1.
- if (!listenerArray.locked_) {
- listenerArray.locked_ = 1;
- } else {
- listenerArray.locked_++;
- }
-
- try {
- // Events added in the dispatch phase should not be dispatched in
- // the current dispatch phase. They will be included in the next
- // dispatch phase though.
- var length = listenerArray.length;
- for (var i = 0; i < length; i++) {
+ 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];
+ if (listenerArray) {
+ listenerArray = goog.array.clone(listenerArray);
+ 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.removed) {
+ if (listener && listener.capture == capture && !listener.removed) {
retval &=
goog.events.fireListener(listener, eventObject) !== false;
}
}
- } finally {
- // Allow the count of targets remaining to increase (if perhaps we have
- // added listeners) but do not allow it to decrease if we have reentered
- // this method through a listener dispatching the same event type,
- // resetting and exhausted the remaining count.
- map.remaining_ = Math.max(remaining, map.remaining_);
- listenerArray.locked_--;
- goog.events.cleanUp_(type, capture, objUid, listenerArray);
}
}
-
return Boolean(retval);
};
@@ -4441,19 +4669,26 @@ goog.events.fireListeners_ = function(map, obj, type, capture, eventObject) {
* @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.key);
+ goog.events.unlistenByKey(listener);
}
- return listener.handleEvent(eventObject);
+ 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.object.getCount(goog.events.listeners_);
+ return goog.events.listenerCountEstimate_;
};
@@ -4466,8 +4701,7 @@ goog.events.getTotalListenerCount = function() {
* function will return false. If one of the capture listeners calls
* stopPropagation, then the bubble listeners won't fire.
*
- * @param {goog.events.Listenable|goog.events.EventTarget} src The
- * event target.
+ * @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.
@@ -4475,87 +4709,11 @@ goog.events.getTotalListenerCount = function() {
* true.
*/
goog.events.dispatchEvent = function(src, e) {
- if (goog.events.Listenable.USE_LISTENABLE_INTERFACE) {
- return src.dispatchEvent(e);
- }
-
- var type = e.type || e;
- var map = goog.events.listenerTree_;
- if (!(type in map)) {
- return true;
- }
-
- // If accepting a string or object, create a custom event object so that
- // preventDefault and stopPropagation work with the event.
- if (goog.isString(e)) {
- e = new goog.events.Event(e, src);
- } else if (!(e instanceof goog.events.Event)) {
- var oldEvent = e;
- e = new goog.events.Event(/** @type {string} */ (type), src);
- goog.object.extend(e, oldEvent);
- } else {
- e.target = e.target || src;
- }
-
- var rv = 1, ancestors;
-
- map = map[type];
- var hasCapture = true in map;
- var targetsMap;
-
- if (hasCapture) {
- // Build ancestors now
- ancestors = [];
- for (var parent = src; parent; parent = parent.getParentEventTarget()) {
- ancestors.push(parent);
- }
-
- targetsMap = map[true];
- targetsMap.remaining_ = targetsMap.count_;
-
- // Call capture listeners
- for (var i = ancestors.length - 1;
- !e.propagationStopped_ && i >= 0 && targetsMap.remaining_;
- i--) {
- e.currentTarget = ancestors[i];
- rv &= goog.events.fireListeners_(targetsMap, ancestors[i], e.type,
- true, e) &&
- e.returnValue_ != false;
- }
- }
-
- var hasBubble = false in map;
- if (hasBubble) {
- targetsMap = map[false];
- targetsMap.remaining_ = targetsMap.count_;
-
- if (hasCapture) { // We have the ancestors.
-
- // Call bubble listeners
- for (var i = 0; !e.propagationStopped_ && i < ancestors.length &&
- targetsMap.remaining_;
- i++) {
- e.currentTarget = ancestors[i];
- rv &= goog.events.fireListeners_(targetsMap, ancestors[i], e.type,
- false, e) &&
- e.returnValue_ != false;
- }
- } else {
- // In case we don't have capture we don't have to build up the
- // ancestors array.
-
- for (var current = src;
- !e.propagationStopped_ && current && targetsMap.remaining_;
- current = current.getParentEventTarget()) {
- e.currentTarget = current;
- rv &= goog.events.fireListeners_(targetsMap, current, e.type,
- false, e) &&
- e.returnValue_ != false;
- }
- }
- }
-
- return Boolean(rv);
+ goog.asserts.assert(
+ goog.events.Listenable.isImplementedBy(src),
+ 'Can not use goog.events.dispatchEvent with ' +
+ 'non-goog.events.Listenable instance.');
+ return src.dispatchEvent(e);
};
@@ -4580,8 +4738,7 @@ goog.events.protectBrowserEventEntryPoint = function(errorHandler) {
* @param {Event=} opt_evt Optional event object that gets passed in via the
* native event handlers.
* @return {boolean} Result of the event handler.
- * @this {goog.events.EventTarget|Object} The object or Element that
- * fired the event.
+ * @this {EventTarget} The object or Element that fired the event.
* @private
*/
goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
@@ -4589,92 +4746,58 @@ goog.events.handleBrowserEvent_ = function(listener, opt_evt) {
return true;
}
- var type = listener.type;
- var map = goog.events.listenerTree_;
-
- if (!(type in map)) {
- return true;
- }
- map = map[type];
- var retval, targetsMap;
// 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;
- // Check if we have any capturing event listeners for this type.
- var hasCapture = true in map;
- var hasBubble = false in map;
-
- if (hasCapture) {
- if (goog.events.isMarkedIeEvent_(ieEvent)) {
- return true;
- }
-
- goog.events.markIeEvent_(ieEvent);
- }
-
- var evt = new goog.events.BrowserEvent();
- // TODO(user): update @this for this function
- evt.init(ieEvent, /** @type {EventTarget} */ (this));
+ 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);
- retval = true;
- try {
- if (hasCapture) {
var ancestors = [];
-
- for (var parent = evt.currentTarget;
- parent;
+ for (var parent = evt.currentTarget; parent;
parent = parent.parentNode) {
ancestors.push(parent);
}
- targetsMap = map[true];
- targetsMap.remaining_ = targetsMap.count_;
-
- // Call capture listeners
- for (var i = ancestors.length - 1;
- !evt.propagationStopped_ && i >= 0 && targetsMap.remaining_;
+ // 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_(targetsMap, ancestors[i], type,
- true, evt);
+ retval &= goog.events.fireListeners_(ancestors[i], type, true, evt);
}
- if (hasBubble) {
- targetsMap = map[false];
- targetsMap.remaining_ = targetsMap.count_;
-
- // Call bubble listeners
- for (var i = 0;
- !evt.propagationStopped_ && i < ancestors.length &&
- targetsMap.remaining_;
- i++) {
- evt.currentTarget = ancestors[i];
- retval &= goog.events.fireListeners_(targetsMap, ancestors[i], type,
- false, 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 {
- // Bubbling, let IE handle the propagation.
- retval = goog.events.fireListener(listener, evt);
- }
-
- } finally {
- if (ancestors) {
- ancestors.length = 0;
}
+ } else {
+ retval = goog.events.fireListener(listener, evt);
}
return retval;
- } // IE
+ }
- // Caught a non-IE DOM event. 1 additional argument which is the event object
- var be = new goog.events.BrowserEvent(
- opt_evt, /** @type {EventTarget} */ (this));
- retval = goog.events.fireListener(listener, be);
- return retval;
+ // Otherwise, simply fire the listener.
+ return goog.events.fireListener(
+ listener, new goog.events.BrowserEvent(opt_evt, this));
};
@@ -4730,8 +4853,7 @@ goog.events.isMarkedIeEvent_ = function(e) {
/**
* Counter to create unique event ids.
- * @type {number}
- * @private
+ * @private {number}
*/
goog.events.uniqueIdCounter_ = 0;
@@ -4741,16 +4863,31 @@ goog.events.uniqueIdCounter_ = 0;
*
* @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.
- * @type {string}
+ * @const
* @private
*/
goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
@@ -4764,13 +4901,16 @@ goog.events.LISTENER_WRAPPER_PROP_ = '__closure_events_fn_' +
* calls obj.handleEvent. If the same listener is passed to this
* function more than once, the same function is guaranteed to be
* returned.
- * @private
*/
-goog.events.wrapListener_ = function(listener) {
+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.');
return listener[goog.events.LISTENER_WRAPPER_PROP_] ||
(listener[goog.events.LISTENER_WRAPPER_PROP_] = function(e) {
return listener.handleEvent(e);
@@ -4868,7 +5008,7 @@ lt.plugins.haskell.hoogle__GT_parse = (function hoogle__GT_parse(response){retur
lt.plugins.haskell.hoogle__GT_convert_doc = (function hoogle__GT_convert_doc(hoogle_doc){if((hoogle_doc == null))
{return null;
} else
-{var location = hoogle_doc.location;var vec__8274 = /http:\/\/hackage.haskell.org\/packages\/archive\/(.+)\/latest\/doc\/html\/(.+).html/.exec(location);var with_mod = cljs.core.nth.call(null,vec__8274,0,null);var mod_package = cljs.core.nth.call(null,vec__8274,1,null);var module_name = cljs.core.nth.call(null,vec__8274,2,null);var explanation = (((with_mod == null))?"":[cljs.core.str(" ("),cljs.core.str(mod_package),cljs.core.str(": "),cljs.core.str(clojure.string.replace.call(null,module_name,"-",".")),cljs.core.str(")")].join(''));return new cljs.core.PersistentArrayMap(null, 3, [new cljs.core.Keyword(null,"name","name",1017277949),hoogle_doc.self,new cljs.core.Keyword(null,"ns","ns",1013907767),new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"a","a",1013904339),new cljs.core.PersistentArrayMap(null, 1, [new cljs.core.Keyword(null,"href","href",1017115293),location], null),[cljs.core.str("Hoogle"),cljs.core.str(explanation)].join('')], null),new cljs.core.Keyword(null,"doc","doc",1014003882),hoogle_doc.docs], null);
+{var location = hoogle_doc.location;var vec__7953 = /http:\/\/hackage.haskell.org\/packages\/archive\/(.+)\/latest\/doc\/html\/(.+).html/.exec(location);var with_mod = cljs.core.nth.call(null,vec__7953,0,null);var mod_package = cljs.core.nth.call(null,vec__7953,1,null);var module_name = cljs.core.nth.call(null,vec__7953,2,null);var explanation = (((with_mod == null))?"":[cljs.core.str(" ("),cljs.core.str(mod_package),cljs.core.str(": "),cljs.core.str(clojure.string.replace.call(null,module_name,"-",".")),cljs.core.str(")")].join(''));return new cljs.core.PersistentArrayMap(null, 3, [new cljs.core.Keyword(null,"name","name",1017277949),hoogle_doc.self,new cljs.core.Keyword(null,"ns","ns",1013907767),new cljs.core.PersistentVector(null, 3, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"a","a",1013904339),new cljs.core.PersistentArrayMap(null, 1, [new cljs.core.Keyword(null,"href","href",1017115293),location], null),[cljs.core.str("Hoogle"),cljs.core.str(explanation)].join('')], null),new cljs.core.Keyword(null,"doc","doc",1014003882),hoogle_doc.docs], null);
}
});
lt.plugins.haskell.convert_hoogle_results = (function convert_hoogle_results(results){var parsed_results = lt.plugins.haskell.hoogle__GT_parse.call(null,results);return cljs.core.map.call(null,lt.plugins.haskell.hoogle__GT_convert_doc,parsed_results);
@@ -4888,8 +5028,8 @@ lt.plugins.haskell.haskell_doc_search = (function haskell_doc_search(this$,cur){
lt.object.behavior_STAR_.call(null,new cljs.core.Keyword("lt.plugins.haskell","haskell-doc-search","lt.plugins.haskell/haskell-doc-search",2214663896),new cljs.core.Keyword(null,"reaction","reaction",4441361819),lt.plugins.haskell.haskell_doc_search,new cljs.core.Keyword(null,"triggers","triggers",2516997421),new cljs.core.PersistentHashSet(null, new cljs.core.PersistentArrayMap(null, 1, [new cljs.core.Keyword(null,"types+","types+",4450069060),null], null), null));
lt.plugins.haskell.symbol_token_QMARK_ = (function symbol_token_QMARK_(s){return cljs.core.re_seq.call(null,/[\w\$_\-\.\*\+\\/\?\>convert-doc", "hayoo-doc", "location",
"func-name", "lt.plugins.haskell/hoogle->url",
"lt.plugins.haskell/hoogle", "lt.plugins.haskell/hoogle->parse",
- "lt.plugins.haskell/hoogle->convert-doc", "hoogle-doc", "vec__8274",
+ "lt.plugins.haskell/hoogle->convert-doc", "hoogle-doc", "vec__7953",
"cljs.core/nth", "with-mod", "mod-package", "module-name",
"explanation", "clojure.string/replace",
"lt.plugins.haskell/convert-hoogle-results", "results",
@@ -35,18 +35,18 @@
"lt.plugins.haskell/find-symbol-at-cursor", "editor", "loc",
"lt.objs.editor/->cursor", "token-left", "lt.objs.editor/->token",
"token-right", "cljs.core/update-in", "cljs.core/inc",
- "or__6813__auto__", "cljs.core/assoc",
+ "or__6364__auto__", "cljs.core/assoc",
"lt.plugins.haskell/with-editor", "func",
"lt.plugins.haskell/inline-hoogle-doc", "doc", "cljs.core/first",
"lt.objs.notifos/set-msg!", "lt.plugins.haskell/haskell-inline-doc",
"token", "lt.plugins.haskell/format-inline-error", "error",
- "split-error", "message-only", "cljs.core/drop",
+ "error-unified", "split-error", "message-only", "cljs.core/drop",
"clojure.string/join", "clojure.string/trim", "message",
"js/parseInt", "lt.plugins.haskell/print-inline-error",
"formatted-error", "lt.plugins.haskell/print-inline-errors", "data",
- "seq__8279", "cljs.core/seq", "chunk__8280", "count__8281",
- "i__8282", "cljs.core/-nth", "temp__4092__auto__",
- "cljs.core/chunked-seq?", "c__7561__auto__", "cljs.core/chunk-first",
+ "seq__7958", "cljs.core/seq", "chunk__7959", "count__7960",
+ "i__7961", "cljs.core/-nth", "temp__4092__auto__",
+ "cljs.core/chunked-seq?", "c__7112__auto__", "cljs.core/chunk-first",
"cljs.core/chunk-rest", "cljs.core/count", "cljs.core/next",
"lt.plugins.haskell/handle-inline-errors", "result",
"cljs.core/empty?", "lt.plugins.haskell/__BEH__haskell-syntax",
@@ -68,7 +68,6 @@
"lt.plugins.haskell/selection-info", "pos", "info",
"cljs.core/deref", "lt.objs.editor/selection?",
"lt.objs.editor/selection", "lt.objs.editor/line",
- "lt.plugins.haskell/prepare-code", "code",
"lt.plugins.haskell/clear-result", "line", "cljs.core/get",
"lt.objs.editor/line-handle",
"lt.plugins.haskell/__BEH__on-eval-one", "clojure.string/blank?",
@@ -81,8 +80,8 @@
"lt.plugins.haskell/__BEH__on-exit", "lt.object/object*",
"lt.plugins.haskell/find-project-dir", "file", "roots",
"lt.objs.files/get-roots", "lt.objs.files/parent", "prev",
- "cljs.core/=", "p1__8283#", "cljs.core/some",
- "lt.objs.files/ls-sync", "p__8284", "map__8286", "cljs.core/seq?",
+ "cljs.core/=", "p1__7962#", "cljs.core/some",
+ "lt.objs.files/ls-sync", "p__7963", "map__7965", "cljs.core/seq?",
"cljs.core/apply", "cljs.core/hash-map",
"lt.plugins.haskell/run-haskell", "path", "name", "client", "obj",
"lt.object/create", "client-id", "lt.objs.clients/->id",
@@ -91,11 +90,11 @@
"lt.plugins.haskell/check-client", "lt.objs.files/exists?",
"lt.plugins.haskell/handle-no-haskell", "lt.objs.clients/rem!",
"lt.objs.popup/popup!", "lt.objs.platform/open",
- "lt.plugins.haskell/notify", "map__8288", "haskell", "cljs.core/not",
- "lt.plugins.haskell/check-all", "p__8289", "map__8291",
+ "lt.plugins.haskell/notify", "map__7967", "haskell", "cljs.core/not",
+ "lt.plugins.haskell/check-all", "p__7968", "map__7970",
"lt.plugins.haskell/try-connect", "lt.objs.clients/client!",
"lt.plugins.haskell/__BEH__connect",
"lt.objs.sidebar.clients/add-connector", "lt.objs.dialogs/dir",
- "command", "map__8293", "origin", "cljs.core/map?",
- "lt.objs.clients/send", "lt.objs.eval/get-client!", "map__8295",
+ "command", "map__7972", "origin", "cljs.core/map?",
+ "lt.objs.clients/send", "lt.objs.eval/get-client!", "map__7974",
"lt.objs.tabs/->path", "cm", "lt.objs.editor/->cm-ed", "string"]}
\ No newline at end of file
diff --git a/light-haskell.cabal b/light-haskell.cabal
index 2dd7bc2..88b9c70 100644
--- a/light-haskell.cabal
+++ b/light-haskell.cabal
@@ -60,7 +60,8 @@ executable light-haskell
aeson ==0.7.*,
process >=1.1,
ghc-mod >=5.1,
- stylish-haskell ==0.5.*
+ stylish-haskell ==0.5.*,
+ haskell-src-exts ==1.14.*
-- Directories containing source files.
hs-source-dirs: haskell
diff --git a/src/lt/plugins/haskell.cljs b/src/lt/plugins/haskell.cljs
index b35bb63..ff96e77 100644
--- a/src/lt/plugins/haskell.cljs
+++ b/src/lt/plugins/haskell.cljs
@@ -296,9 +296,6 @@
:code (ed/line editor (:line pos))))]
info))
-(defn prepare-code [code]
- (clj-string/replace code #"^(\w+)(\s+)?=" "let $1 ="))
-
(defn clear-result [editor line]
(when-let [result (get (@editor :widgets) [(ed/line-handle editor line) :inline])]
(object/raise result :clear!)))
@@ -307,7 +304,7 @@
:triggers #{:eval.one}
:reaction (fn [editor]
(let [info (selection-info editor)
- data {:data (prepare-code (:code info))
+ data {:data (:code info)
:line (:line info)}]
(when-not (clj-string/blank? (:code info))
(clear-result editor (:line info))