Class bot.Error
code »Error
+ └ bot.ErrorError extension that includes error status codes from the WebDriver wire + protocol: + http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes
Constructor
| Parameters |
|---|
|
Enumerations
|
diff --git a/node_modules/selenium-webdriver/CHANGES.md b/node_modules/selenium-webdriver/CHANGES.md new file mode 100644 index 0000000..2d7c98d --- /dev/null +++ b/node_modules/selenium-webdriver/CHANGES.md @@ -0,0 +1,169 @@ +## v2.42.1 + +* FIXED: 7465: Fixed `net.getLoopbackAddress` on Windows +* FIXED: 7277: Support `done` callback in Mocha's BDD interface +* FIXED: 7156: `Promise#thenFinally` should not suppress original error + +## v2.42.0 + +* Removed deprecated functions `Promise#addCallback()`, + `Promise#addCallbacks()`, `Promise#addErrback()`, and `Promise#addBoth()`. +* Fail with a more descriptive error if the server returns a malformed redirect +* FIXED: 7300: Connect to ChromeDriver using the loopback address since + ChromeDriver 2.10.267517 binds to localhost by default. +* FIXED: 7339: Preserve wrapped test function's string representation for + Mocha's BDD interface. + +## v2.41.0 + +* FIXED: 7138: export logging API from webdriver module. +* FIXED: 7105: beforeEach/it/afterEach properly bind `this` for Mocha tests. + +## v2.40.0 + +* API documentation is now included in the docs directory. +* Added utility functions for working with an array of promises: + `promise.all`, `promise.map`, and `promise.filter` +* Introduced `Promise#thenCatch()` and `Promise#thenFinally()`. +* Deprecated `Promise#addCallback()`, `Promise#addCallbacks()`, + `Promise#addErrback()`, and `Promise#addBoth()`. +* Removed deprecated function `webdriver.WebDriver#getCapability`. +* FIXED: 6826: Added support for custom locators. + +## v2.39.0 + +* Version bump to stay in sync with the Selenium project. + +## v2.38.1 + +* FIXED: 6686: Changed `webdriver.promise.Deferred#cancel()` to silently no-op + if the deferred has already been resolved. + +## v2.38.0 + +* When a promise is rejected, always annotate the stacktrace with the parent + flow state so users can identify the source of an error. +* Updated tests to reflect features not working correctly in the SafariDriver + (cookie management and proxy support; see issues 5051, 5212, and 5503) +* FIXED: 6284: For mouse moves, correctly omit the x/y offsets if not + specified as a function argument (instead of passing (0,0)). +* FIXED: 6471: Updated documentation on `webdriver.WebElement#getAttribute` +* FIXED: 6612: On Unix, use the default IANA ephemeral port range if unable to + retrieve the current system's port range. +* FIXED: 6617: Avoid triggering the node debugger when initializing the + stacktrace module. +* FIXED: 6627: Safely rebuild chrome.Options from a partial JSON spec. + +## v2.37.0 + +* FIXED: 6346: The remote.SeleniumServer class now accepts JVM arguments using + the `jvmArgs` option. + +## v2.36.0 + +* _Release skipped to stay in sync with main Selenium project._ + +## v2.35.2 + +* FIXED: 6200: Pass arguments to the Selenium server instead of to the JVM. + +## v2.35.1 + +* FIXED: 6090: Changed example scripts to use chromedriver. + +## v2.35.0 + +* Version bump to stay in sync with the Selenium project. + +## v2.34.1 + +* FIXED: 6079: The parent process should not wait for spawn driver service + processes (chromedriver, phantomjs, etc.) + +## v2.34.0 + +* Added the `selenium-webdriver/testing/assert` module. This module + simplifies writing assertions against promised values (see + example in module documentation). +* Added the `webdriver.Capabilities` class. +* Added native support for the ChromeDriver. When using the `Builder`, + requesting chrome without specifying a remote server URL will default to + the native ChromeDriver implementation. The + [ChromeDriver server](https://code.google.com/p/chromedriver/downloads/list) + must be downloaded separately. + + // Will start ChromeDriver locally. + var driver = new webdriver.Builder(). + withCapabilities(webdriver.Capabilities.chrome()). + build(); + + // Will start ChromeDriver using the remote server. + var driver = new webdriver.Builder(). + withCapabilities(webdriver.Capabilities.chrome()). + usingServer('http://server:1234/wd/hub'). + build(); + +* Added support for configuring proxies through the builder. For examples, see + `selenium-webdriver/test/proxy_test`. +* Added native support for PhantomJS. +* Changed signature of `SeleniumServer` to `SeleniumServer(jar, options)`. +* Tests are now included in the npm published package. See `README.md` for + execution instructions +* Removed the deprecated `webdriver.Deferred#resolve` and + `webdriver.promise.resolved` functions. +* Removed the ability to connect to an existing session from the Builder. This + feature is intended for use with the browser-based client. + +## v2.33.0 + +* Added support for WebDriver's logging API +* FIXED: 5511: Added webdriver.manage().timeouts().pageLoadTimeout(ms) + +## v2.32.1 + +* FIXED: 5541: Added missing return statement for windows in + `portprober.findFreePort()` + +## v2.32.0 + +* Added the `selenium-webdriver/testing` package, which provides a basic + framework for writing tests using Mocha. See + `selenium-webdriver/example/google_search_test.js` for usage. +* For Promises/A+ compatibility, backing out the change in 2.30.0 that ensured + rejections were always Error objects. Rejection reasons are now left as is. +* Removed deprecated functions originally scheduled for removal in 2.31.0 + * promise.Application.getInstance() + * promise.ControlFlow#schedule() + * promise.ControlFlow#scheduleTimeout() + * promise.ControlFlow#scheduleWait() +* Renamed some functions for consistency with Promises/A+ terminology. The + original functions have been deprecated and will be removed in 2.34.0: + * promise.resolved() -> promise.fulfilled() + * promise.Deferred#resolve() -> promise.Deferred#fulfill() +* FIXED: remote.SeleniumServer#stop now shuts down within the active control + flow, allowing scripts to finish. Use #kill to shutdown immediately. +* FIXED: 5321: cookie deletion commands + +## v2.31.0 + +* Added an example script. +* Added a class for controlling the standalone Selenium server (server +available separately) +* Added a portprober for finding free ports +* FIXED: WebElements now belong to the same flow as their parent driver. + +## v2.30.0 + +* Ensures promise rejections are always Error values. +* Version bump to keep in sync with the Selenium project. + +## v2.29.1 + +* Fixed a bug that could lead to an infinite loop. +* Added a README.md + +## v2.29.0 + +* Initial release for npm: + + npm install selenium-webdriver diff --git a/node_modules/selenium-webdriver/COPYING b/node_modules/selenium-webdriver/COPYING new file mode 100644 index 0000000..80a4762 --- /dev/null +++ b/node_modules/selenium-webdriver/COPYING @@ -0,0 +1,204 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2007-2009 Google Inc. + Copyright 2007-2009 WebDriver committers + + 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. + diff --git a/node_modules/selenium-webdriver/README.md b/node_modules/selenium-webdriver/README.md new file mode 100644 index 0000000..1f954bf --- /dev/null +++ b/node_modules/selenium-webdriver/README.md @@ -0,0 +1,77 @@ +# selenium-webdriver + +## Installation + +Install the latest published version using `npm`: + + npm install selenium-webdriver + +In addition to the npm package, you will to download the WebDriver +implementations you wish to utilize. As of 2.34.0, `selenium-webdriver` +natively supports the [ChromeDriver](http://chromedriver.storage.googleapis.com/index.html). +Simply download a copy and make sure it can be found on your `PATH`. The other +drivers (e.g. Firefox, Internet Explorer, and Safari), still require the +[standalone Selenium server](http://selenium-release.storage.googleapis.com/index.html). + +### Running the tests + +To run the tests, you will need to download a copy of the +[ChromeDriver](http://chromedriver.storage.googleapis.com/index.html) and make +sure it can be found on your `PATH`. + + npm test selenium-webdriver + +To run the tests against multiple browsers, download the +[Selenium server](http://selenium-release.storage.googleapis.com/index.html) and +specify its location through the `SELENIUM_SERVER_JAR` environment variable. +You can use the `SELENIUM_BROWSER` environment variable to define a +comma-separated list of browsers you wish to test against. For example: + + export SELENIUM_SERVER_JAR=path/to/selenium-server-standalone-2.33.0.jar + SELENIUM_BROWSER=chrome,firefox npm test selenium-webdriver + +## Usage + + + var webdriver = require('selenium-webdriver'); + + var driver = new webdriver.Builder(). + withCapabilities(webdriver.Capabilities.chrome()). + build(); + + driver.get('http://www.google.com'); + driver.findElement(webdriver.By.name('q')).sendKeys('webdriver'); + driver.findElement(webdriver.By.name('btnG')).click(); + driver.wait(function() { + return driver.getTitle().then(function(title) { + return title === 'webdriver - Google Search'; + }); + }, 1000); + + driver.quit(); + +## Documentation + +API documentation is included in the docs module. The API documentation for the +current release are also available online from the [Selenium project](http://selenium.googlecode.com/git/docs/api/javascript/index.html "API docs"). A full user guide is available on the +[Selenium project wiki](http://code.google.com/p/selenium/wiki/WebDriverJs "User guide"). + +## Issues + +Please report any issues using the [Selenium issue tracker](https://code.google.com/p/selenium/issues/list). + +## License + +Copyright 2009-2014 Software Freedom Conservancy + +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. diff --git a/node_modules/selenium-webdriver/_base.js b/node_modules/selenium-webdriver/_base.js new file mode 100644 index 0000000..6048179 --- /dev/null +++ b/node_modules/selenium-webdriver/_base.js @@ -0,0 +1,165 @@ +// Copyright 2012 Selenium committers +// Copyright 2012 Software Freedom Conservancy +// +// 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 The base module responsible for bootstrapping the Closure + * library and providing a means of loading Closure-based modules. + * + *
Each script loaded by this module will be granted access to this module's + * {@code require} function; all required non-native modules must be specified + * relative to this module. + * + *
This module will load all scripts from the "lib" subdirectory, unless the
+ * SELENIUM_DEV_MODE environment variable has been set to 1, in which case all
+ * scripts will be loaded from the Selenium client containing this script.
+ */
+
+'use strict';
+
+var fs = require('fs'),
+ path = require('path'),
+ vm = require('vm');
+
+
+/**
+ * If this script was loaded from the Selenium project repo, it will operate in
+ * development mode, adjusting how it loads Closure-based dependencies.
+ * @type {boolean}
+ */
+var devMode = (function() {
+ var buildDescFile = path.join(__dirname, '..', 'build.desc');
+ return fs.existsSync(buildDescFile);
+})();
+
+
+/** @return {boolean} Whether this script was loaded in dev mode. */
+function isDevMode() {
+ return devMode;
+}
+
+
+/**
+ * @type {string} Path to Closure's base file, relative to this module.
+ * @const
+ */
+var CLOSURE_BASE_FILE_PATH = (function() {
+ var relativePath = isDevMode() ?
+ '../../../third_party/closure/goog/base.js' :
+ './lib/goog/base.js';
+ return path.join(__dirname, relativePath);
+})();
+
+
+/**
+ * @type {string} Path to Closure's base file, relative to this module.
+ * @const
+ */
+var DEPS_FILE_PATH = (function() {
+ var relativePath = isDevMode() ?
+ '../../../javascript/deps.js' :
+ './lib/goog/deps.js';
+ return path.join(__dirname, relativePath);
+})();
+
+
+
+/**
+ * Synchronously loads a script into the protected Closure context.
+ * @param {string} src Path to the file to load.
+ */
+function loadScript(src) {
+ src = path.normalize(src);
+ var contents = fs.readFileSync(src, 'utf8');
+ vm.runInContext(contents, closure, src);
+}
+
+
+/**
+ * The protected context to host the Closure library.
+ * @type {!Object}
+ * @const
+ */
+var closure = vm.createContext({
+ console: console,
+ setTimeout: setTimeout,
+ setInterval: setInterval,
+ clearTimeout: clearTimeout,
+ clearInterval: clearInterval,
+ process: process,
+ require: require,
+ Buffer: Buffer,
+ Error: Error,
+ CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/',
+ CLOSURE_IMPORT_SCRIPT: function(src) {
+ loadScript(src);
+ return true;
+ },
+ CLOSURE_NO_DEPS: !isDevMode(),
+ goog: {}
+});
+closure.window = closure;
+
+
+loadScript(CLOSURE_BASE_FILE_PATH);
+loadScript(DEPS_FILE_PATH);
+
+
+/**
+ * Loads a symbol by name from the protected Closure context.
+ * @param {string} symbol The symbol to load.
+ * @return {?} The loaded symbol, or {@code null} if not found.
+ * @throws {Error} If the symbol has not been defined.
+ */
+function closureRequire(symbol) {
+ closure.goog.require(symbol);
+ return closure.goog.getObjectByName(symbol);
+}
+
+
+// PUBLIC API
+
+
+/**
+ * Loads a symbol by name from the protected Closure context and exports its
+ * public API to the provided object. This function relies on Closure code
+ * conventions to define the public API of an object as those properties whose
+ * name does not end with "_".
+ * @param {string} symbol The symbol to load. This must resolve to an object.
+ * @return {!Object} An object with the exported API.
+ * @throws {Error} If the symbol has not been defined or does not resolve to
+ * an object.
+ */
+exports.exportPublicApi = function(symbol) {
+ var src = closureRequire(symbol);
+ if (typeof src != 'object' || src === null) {
+ throw Error('"' + symbol + '" must resolve to an object');
+ }
+
+ var dest = {};
+ Object.keys(src).forEach(function(key) {
+ if (key[key.length - 1] != '_') {
+ dest[key] = src[key];
+ }
+ });
+
+ return dest;
+};
+
+
+if (isDevMode()) {
+ exports.closure = closure;
+}
+exports.isDevMode = isDevMode;
+exports.require = closureRequire;
diff --git a/node_modules/selenium-webdriver/builder.js b/node_modules/selenium-webdriver/builder.js
new file mode 100644
index 0000000..fc2f40b
--- /dev/null
+++ b/node_modules/selenium-webdriver/builder.js
@@ -0,0 +1,113 @@
+// Copyright 2011 Software Freedom Conservancy. 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.
+
+var base = require('./_base'),
+ executors = require('./executors');
+
+var goog = base.require('goog'),
+ AbstractBuilder = base.require('webdriver.AbstractBuilder'),
+ Browser = base.require('webdriver.Browser'),
+ Capability = base.require('webdriver.Capability'),
+ WebDriver = base.require('webdriver.WebDriver'),
+ promise = base.require('webdriver.promise');
+
+
+/**
+ * @param {!webdriver.Capabilities} capabilities The desired capabilities.
+ * @return {webdriver.WebDriver} A new WebDriver instance or {@code null}
+ * if the requested browser is not natively supported in Node.
+ */
+function createNativeDriver(capabilities) {
+ switch (capabilities.get(Capability.BROWSER_NAME)) {
+ case Browser.CHROME:
+ // Requiring 'chrome' above would create a cycle:
+ // index -> builder -> chrome -> index
+ var chrome = require('./chrome');
+ return chrome.createDriver(capabilities);
+
+ case Browser.PHANTOM_JS:
+ // Requiring 'phantomjs' would create a cycle:
+ // index -> builder -> phantomjs -> index
+ var phantomjs = require('./phantomjs');
+ return phantomjs.createDriver(capabilities);
+
+ default:
+ return null;
+ }
+}
+
+
+
+/**
+ * Creates new {@link webdriver.WebDriver WebDriver} instances.
+ * @constructor
+ * @extends {webdriver.AbstractBuilder}
+ */
+var Builder = function() {
+ goog.base(this);
+};
+goog.inherits(Builder, AbstractBuilder);
+
+
+/**
+ * Sets the proxy configuration to use for WebDriver clients created by this
+ * builder. Any calls to {@link #withCapabilities} after this function will
+ * overwrite these settings.
+ * @param {!proxy.ProxyConfig} config The configuration to use.
+ * @return {!Builder} A self reference.
+ */
+Builder.prototype.setProxy = function(config) {
+ this.getCapabilities().set(Capability.PROXY, config);
+ return this;
+};
+
+
+/**
+ * Sets Chrome-specific options for drivers created by this builder.
+ * @param {!chrome.Options} options The ChromeDriver options to use.
+ * @return {!Builder} A self reference.
+ */
+Builder.prototype.setChromeOptions = function(options) {
+ var newCapabilities = options.toCapabilities(this.getCapabilities());
+ return /** @type {!Builder} */(this.withCapabilities(newCapabilities));
+};
+
+
+/**
+ * @override
+ */
+Builder.prototype.build = function() {
+ var url = this.getServerUrl();
+
+ // If a remote server wasn't specified, check for browsers we support
+ // natively in node before falling back to using the java Selenium server.
+ if (!url) {
+ var driver = createNativeDriver(this.getCapabilities());
+ if (driver) {
+ return driver;
+ }
+
+ // Nope, fall-back to using the default java server.
+ url = AbstractBuilder.DEFAULT_SERVER_URL;
+ }
+
+ var executor = executors.createExecutor(url);
+ return WebDriver.createSession(executor, this.getCapabilities());
+};
+
+
+// PUBLIC API
+
+
+exports.Builder = Builder;
diff --git a/node_modules/selenium-webdriver/chrome.js b/node_modules/selenium-webdriver/chrome.js
new file mode 100644
index 0000000..7e32873
--- /dev/null
+++ b/node_modules/selenium-webdriver/chrome.js
@@ -0,0 +1,473 @@
+// Copyright 2013 Selenium committers
+// Copyright 2013 Software Freedom Conservancy
+//
+// 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.
+
+'use strict';
+
+var fs = require('fs'),
+ util = require('util');
+
+var webdriver = require('./index'),
+ executors = require('./executors'),
+ io = require('./io'),
+ portprober = require('./net/portprober'),
+ remote = require('./remote');
+
+
+/**
+ * Name of the ChromeDriver executable.
+ * @type {string}
+ * @const
+ */
+var CHROMEDRIVER_EXE =
+ process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver';
+
+
+/**
+ * Creates {@link remote.DriverService} instances that manage a ChromeDriver
+ * server.
+ * @param {string=} opt_exe Path to the server executable to use. If omitted,
+ * the builder will attempt to locate the chromedriver on the current
+ * PATH.
+ * @throws {Error} If provided executable does not exist, or the chromedriver
+ * cannot be found on the PATH.
+ * @constructor
+ */
+var ServiceBuilder = function(opt_exe) {
+ /** @private {string} */
+ this.exe_ = opt_exe || io.findInPath(CHROMEDRIVER_EXE, true);
+ if (!this.exe_) {
+ throw Error(
+ 'The ChromeDriver could not be found on the current PATH. Please ' +
+ 'download the latest version of the ChromeDriver from ' +
+ 'http://chromedriver.storage.googleapis.com/index.html and ensure ' +
+ 'it can be found on your PATH.');
+ }
+
+ if (!fs.existsSync(this.exe_)) {
+ throw Error('File does not exist: ' + this.exe_);
+ }
+
+ /** @private {!Array. Error extension that includes error status codes from the WebDriver wire
+ protocol:
+ http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes Flag used for duck-typing when this code is embedded in a Firefox extension.
+ This is required since an Error thrown in one component and then reported
+ to another will fail instanceof checks in the second component. A map of error codes to state string. This class contains setters and getters for the parts of the URI.
+ The Checks if this Uri has been marked as read only, and if so, throws an error.
+ This should be called whenever any modifying function is called. Returns the first value for a given cgi parameter or undefined if the given
+ parameter name does not appear in the query string. Returns the values for a given cgi parameter as a list of decoded
+ query parameter values. Returns the query data. Returns true if this has the same domain as that of uri2. Adds a random parameter to the Uri. Removes the named query parameter. Resolves the given relative URI (a goog.Uri object), using the URI
+ represented by this instance as the base URI.
+
+ There are several kinds of relative URIs: Sets the URI fragment. Sets whether to ignore case.
+ NOTE: If there are already key/value pairs in the QueryData, and
+ ignoreCase_ is set to false, the keys will all be lower-cased. Sets the value of the named query parameters, clearing previous values for
+ that key. Sets the values of the named query parameters, clearing previous values for
+ that key. Not new values will currently be moved to the end of the query
+ string.
+
+ So, Sets the query data. Sets whether Uri is read only. If this goog.Uri is read-only,
+ enforceReadOnly_ will be called at the start of any function that may modify
+ this Uri. Sets the userInfo. Whether or not to ignore case when comparing query params. Whether or not this Uri should be treated as Read Only. Object representing query data. Creates a new goog.Uri object from unencoded parts. Decodes a value or returns the empty string if it isn't defined or empty. Converts a character in [\01-\177] to its unicode character equivalent. If unescapedPart is non null, then escapes any characters in it that aren't
+ valid characters in a url and also escapes any special characters that
+ appear in extra. Checks whether two URIs have the same domain. Creates a uri from the string form. Basically an alias of new goog.Uri().
+ If a Uri object is passed to parse then it will return a clone of the object. Removes dot segments in given path component, as described in
+ RFC 3986, section 5.2.4. Resolves a relative Uri against a base Uri, accepting both strings and
+ Uri objects. Parameter name added to stop caching. If true, we preserve the type of query parameters set programmatically.
+
+ This means that if you set a parameter to a boolean, and then call
+ getParameterValue, you will get a boolean back.
+
+ If false, we will coerce parameters to strings, just as they would
+ appear in real URIs.
+
+ TODO(nicksantos): Remove this once people have time to fix all tests. Regular expression for characters that are disallowed in an absolute path. Regular expression for characters that are disallowed in the fragment. Regular expression for characters that are disallowed in the query. Regular expression for characters that are disallowed in a relative path. Regular expression for characters that are disallowed in the scheme or
+ userInfo part of the URI. Class used to represent URI query parameters. It is essentially a hash of
+ name-value pairs, though a name can be present more than once.
+
+ Has the same interface as the collections in goog.structs. Adds a key value pair. Clone the query data instance. Whether there is a parameter with the given name Whether there is a parameter with the given value. If the underlying key map is not yet initialized, it parses the
+ query string and fills the map with parsed data. Extends a query data object with another query data or map like object. This
+ operates 'in-place', it does not create a new QueryData object. Removes all keys that are not in the provided list. (Modifies this object.) Returns the first value associated with the key. If the query data has no
+ such key this will return undefined or the optional default. Helper function to get the key name from a JavaScript object. Converts
+ the object to a string, and to lower case if necessary. Returns all the values of the parameters with the given name. If the query
+ data has no such key this will return an empty array. If no key is given
+ all values wil be returned. Invalidate the cache. Sets a key value pair and removes all other keys with the same value. Ignore case in parameter names.
+ NOTE: If there are already key/value pairs in the QueryData, and
+ ignoreCase_ is set to false, the keys will all be lower-cased. Encoded query string, or null if it requires computing from the key map. If true, ignore the case of the parameter name in #get. The map containing name/value or name/array-of-values pairs.
+ May be null if it requires parsing from the query string.
+
+ We need to use a Map because we cannot guarantee that the key names will
+ not be problematic for IE. Creates a new query data instance from parallel arrays of parameter names
+ and values. Allows for duplicate parameter names. Throws an error if the
+ lengths of the arrays differ. Creates a new query data instance from a map of names and values. Error object for failed assertions. The message pattern used to format the error message. Error handlers can
+ use this to uniquely identify the assertion. Base class for custom error objects. Implements the Performs the grouping of objects using the given key. Returns the The current key visited during iteration. The current value being added to the group. The iterable to group, coerced to an iterator. Class/interface for iterators. An iterator needs to implement a Returns the Class that is used to serialize JSON objects to a string. Serializes an array to a JSON string Serializes a generic value to a JSON string Serializes a number to a JSON string Serializes an object to a JSON string Serializes a string to a JSON string Character mappings used internally for goog.string.quote Regular expression used to match characters that need to be replaced.
+ The S60 browser has a bug where unicode characters are not matched by
+ regular expressions. The condition below detects such behaviour and
+ adjusts the regular expression accordingly. The AllOf matcher. The AnyOf matcher. The CloseTo matcher. The ContainsString matcher. The EndsWith matcher. The EqualToIgnoringWhitespace matcher. The EqualTo matcher. The Equals matcher. The GreaterThanEqualTo matcher. The GreaterThan matcher. The HasProperty matcher. The InstanceOf matcher. The IsNot matcher. The IsNull matcher. The IsNullOrUndefined matcher. The IsUndefined matcher. The LessThanEqualTo matcher. The lessThan matcher. Error thrown when a Matcher fails to match the input value. The Equals matcher. The MatchesRegex matcher. The StartsWith matcher. The StringContainsInOrdermatcher. Default factory to use when creating xhr objects. You probably shouldn't be
+ instantiating this directly, but rather using it via goog.net.XmlHttp. Initialize the private state used by other functions. Cache of options - we only actually call internalGetOptions once. An xhr factory subclass which can be constructed using two factory methods.
+ This exists partly to allow the preservation of goog.net.XmlHttp.setFactory()
+ with an unchanged signature. Options factory method. XHR factory method. Override this method in subclasses to preserve the caching offered by
+ getOptions(). Cache of options - we only actually call internalGetOptions once. Abstract base class for an XmlHttpRequest factory. Override this method in subclasses to preserve the caching offered by
+ getOptions(). Cache of options - we only actually call internalGetOptions once. Class for Hash Map datastructure. Returns an iterator that iterates over the values or the keys in the map.
+ This throws an exception if the map was mutated since the iterator was
+ created. Cleans up the temp keys array by removing entries that are no longer in the
+ map. Clones a map and returns a new map. Whether the map contains the given key. Whether the map contains the given value. This is O(n). Whether this map is equal to the argument map. Returns the value for the given key. If the key is not found and the default
+ value is not given this will return Returns an iterator that iterates over the keys in the map. Removal of keys
+ while iterating might have undesired side effects. Returns an iterator that iterates over the values in the map. Removal of
+ keys while iterating might have undesired side effects. Returns a new map 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.
+
+ It acts very similarly to {goog.object.transpose(Object)}. An array of keys. This is necessary for two reasons:
+ 1. Iterating the keys using for (var key in this.map_) allocates an
+ object for every key in IE which is really bad for IE6 GC perf.
+ 2. Without a side data structure, we would need to escape all the keys
+ as that would be the only way we could tell during iteration if the
+ key was an internal key or a property of the object.
+
+ This array can contain deleted keys so it's necessary to check the map
+ as well to see if the key is still in the map (this doesn't require a
+ memory allocation in IE). Default equality test for values. Safe way to test for hasOwnProperty. It even allows testing for
+ 'hasOwnProperty'. Creates new Builds a new Configures which WebDriver server should be used for new sessions. Overrides
+ the value loaded from the Sets the desired capabilities when requesting a new session. This will
+ overwrite any previously set desired capabilities. The desired capabilities to use when creating a new session. URL of the remote server to use for new clients; initialized from the
+ value of the The default URL of the WebDriver server to use if
+ Environment variable that defines the URL of the WebDriver server that
+ should be used for all new WebDriver clients. This setting may be overridden
+ using Class for defining sequences of complex user interactions. Each sequence
+ will not be executed until Example: Clicks a mouse button.
+
+ If an element is provided, the mouse will first be moved to the center
+ of that element. This is equivalent to:
+ Double-clicks a mouse button.
+
+ If an element is provided, the mouse will first be moved to the center of
+ that element. This is equivalent to:
+ Warning: this method currently only supports the left mouse button. See
+ http://code.google.com/p/selenium/issues/detail?id=4047 Convenience function for performing a "drag and drop" manuever. The target
+ element may be moved to the location of another element, or by an offset (in
+ pixels). Performs a modifier key press. The modifier key is not released
+ until Performs a modifier key release. The release is targetted at the currently
+ focused element. Presses a mouse button. The mouse button will not be released until
+ If an element is provided, the mouse will first be moved to the center
+ of that element. This is equivalent to:
+ Warning: this method currently only supports the left mouse button. See
+ http://code.google.com/p/selenium/issues/detail?id=4047 Moves the mouse. The location to move to may be specified in terms of the
+ mouse's current location, an offset relative to the top-left corner of an
+ element, or an element (in which case the middle of the element is used). Releases a mouse button. Behavior is undefined for calling this function
+ without a previous call to If an element is provided, the mouse will first be moved to the center
+ of that element. This is equivalent to:
+ Warning: this method currently only supports the left mouse button. See
+ http://code.google.com/p/selenium/issues/detail?id=4047 Executes this action sequence. Schedules a keyboard action. Schedules a mouse action. Schedules an action to be executed each time Simulates typing multiple keys. Each modifier key encountered in the
+ sequence will not be released until it is encountered again. All key events
+ will be targetted at the currently focused element. Checks that a key is a modifier key. Represents a modal dialog such as Accepts this alert. Dismisses this alert. Retrieves the message text displayed with this alert. For instance, if the
+ alert were opened with alert("hello"), then this would return "hello". Sets the response text on this alert. This command will return an error if
+ the underlying alert does not support response text (e.g. window.alert and
+ window.confirm). Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ Configures the builder to create a client that will use an existing WebDriver
+ session. Configures which WebDriver server should be used for new sessions. Overrides
+ the value loaded from the Sets the desired capabilities when requesting a new session. This will
+ overwrite any previously set desired capabilities. ID of an existing WebDriver session that new clients should use.
+ Initialized from the value of the
+ The desired capabilities to use when creating a new session. URL of the remote server to use for new clients; initialized from the
+ value of the Environment variable that defines the session ID of an existing WebDriver
+ session to use when creating clients. If set, all new Builder instances will
+ default to creating clients that use this session. To create a new session,
+ use Merges another set of capabilities into this instance. Any duplicates in
+ the provided set will override those already set on this instance. Describes a command to be executed by the WebDriverJS framework. Returns a named command parameter. Sets a parameter to send with this command. Sets the parameters for this command. The parameters to this command. Object that can emit events for others to listen for. This is used instead
+ of Closure's event system because it is much more light weight. The API is
+ based on Node's EventEmitters. Registers a listener. Registers a listener. An alias for Registers a one-time listener which will be called only the first time an
+ event is emitted, after which it will be removed. Removes all listeners for a specific type of event. If no event is
+ specified, all listeners across all types will be removed. Removes a previously registered event listener. The pending command, if any. An element locator. Verifies that a Creates a factory function for a Contains information about a WebDriver session. Retrieves the value of a specific capability. An error returned to indicate that there is an unhandled modal dialog on the
+ current page. Flag used for duck-typing when this code is embedded in a Firefox extension.
+ This is required since an Error thrown in one component and then reported
+ to another will fail instanceof checks in the second component. Creates a new WebDriver client, which provides control over a browser.
+
+ Every WebDriver command returns a Creates a new action sequence using this driver. The sequence will not be
+ scheduled for execution until Schedules a command to execute a custom function. Schedules a command to close the current window. Schedules a command to execute asynchronous JavaScript in the context of the
+ currently selected frame or window. The script fragment will be executed as
+ the body of an anonymous function. If the script is provided as a function
+ object, that function will be converted to a string for injection into the
+ target window.
+
+ Any arguments provided in addition to the script will be included as script
+ arguments and may be referenced using the Schedules a command to execute JavaScript in the context of the currently
+ selected frame or window. The script fragment will be executed as the body
+ of an anonymous function. If the script is provided as a function object,
+ that function will be converted to a string for injection into the target
+ window.
+
+ Any arguments provided in addition to the script will be included as script
+ arguments and may be referenced using the Locates a DOM element so that commands may be issued against it using the
+ Schedule a command to find an element on the page. If the element cannot be
+ found, a The search criteria for an element may be defined using one of the
+ factories in the You may also provide a custom locator function, which takes as input
+ this WebDriver instance and returns a When running in the browser, a WebDriver cannot manipulate DOM elements
+ directly; it may do so only through a Schedule a command to search for multiple elements on the page. Schedules a command to navigate to the given URL. Schedules a command to retrieve the current list of available window handles. Schedules a command to retrieve the URL of the current page. Schedules a command to retrieve the current page's source. The page source
+ returned is a representation of the underlying DOM: do not expect it to be
+ formatted or escaped in the same way as the response sent from the web
+ server. Schedules a command to retrieve the current page's title. Schedules a command to retrieve they current window handle. Schedules a command to test if an element is present on the page.
+
+ If given a DOM element, this function will check if it belongs to the
+ document the driver is currently focused on. Otherwise, the function will
+ test if at least one element can be found with the given search criteria. Schedules a command to quit the current session. After calling quit, this
+ instance will be invalidated and may no longer be used to issue commands
+ against the browser. Schedules a Schedules a command to make the driver sleep for the given amount of time. Schedule a command to take a screenshot. The driver makes a best effort to
+ return a screenshot of the following, in order of preference:
+ Schedules a command to wait for a condition to hold, as defined by some
+ user supplied function. If any errors occur while evaluating the wait, they
+ will be allowed to propagate.
+
+ In the event a condition returns a Sends a command to the server that is expected to return the details for a
+ Creates a new WebDriver client for an existing session. Creates a new WebDriver session. Translates a command to its wire-protocol representation before passing it
+ to the given Converts a value from its JSON representation according to the WebDriver wire
+ protocol. Any JSON object containing a
+ Converts an object to its JSON representation in the WebDriver wire protocol.
+ When converting values of type object, the following steps will be taken:
+ Interface for managing WebDriver log records. Fetches available log entries for the given type.
+
+ Retrieves the log types available to this driver. Interface for navigating back and forth in the browser history. Schedules a command to move backwards in the browser history. Schedules a command to move forwards in the browser history. Schedules a command to refresh the current page. Schedules a command to navigate to a new URL. Provides methods for managing browser and driver state. Schedules a command to add a cookie. Schedules a command to delete all cookies visible to the current page. Schedules a command to delete the cookie with the given name. This command is
+ a no-op if there is no cookie with the given name visible to the current
+ page. Schedules a command to retrieve the cookie with the given name. Returns null
+ if there is no such cookie. The cookie will be returned as a JSON object as
+ described by the WebDriver wire protocol. Schedules a command to retrieve all cookies visible to the current page.
+ Each cookie will be returned as a JSON object as described by the WebDriver
+ wire protocol. An interface for changing the focus of the driver to another frame or window. Schedules a command retrieve the Schedules a command to change focus to the active alert dialog. This command
+ will return a Schedules a command to switch focus of all future commands to the first frame
+ on the page. Schedules a command to switch the focus of all future commands to another
+ frame on the page.
+ Schedules a command to switch the focus of all future commands to another
+ window. Windows may be specified by their An interface for managing timeout behavior for WebDriver instances. Specifies the amount of time the driver should wait when searching for an
+ element if it is not immediately present.
+ Sets the amount of time to wait for a page load to complete before returning
+ an error. If the timeout is negative, page loads may be indefinite. Sets the amount of time to wait, in milliseconds, for an asynchronous script
+ to finish execution before returning an error. If the timeout is less than or
+ equal to 0, the script will be allowed to run indefinitely. An interface for managing the current window. Retrieves the window's current position, relative to the top left corner of
+ the screen. Retrieves the window's current size. Maximizes the current window. Repositions the current window. Resizes the current window. Represents a DOM element. WebElements can be found by searching from the
+ document root using a Schedules a command to clear the Schedules a command to click on this element. Schedule a command to find a descendant of this element. If the element
+ cannot be found, a The search criteria for an element may be defined using one of the
+ factories in the You may also provide a custom locator function, which takes as input
+ this WebDriver instance and returns a Schedules a command to find all of the descendants of this element that
+ match the given search criteria. Schedules a command to query for the value of the given attribute of the
+ element. Will return the current value, even if it has been modified after
+ the page has been loaded. More exactly, this method will return the value of
+ the given attribute, unless that attribute is not present, in which case the
+ value of the property with the same name is returned. If neither value is
+ set, null is returned (for example, the "value" property of a textarea
+ element). The "style" attribute is converted as best can be to a
+ text representation with a trailing semi-colon. The following are deemed to
+ be "boolean" attributes and will return either "true" or null:
+
+ async, autofocus, autoplay, checked, compact, complete, controls, declare,
+ defaultchecked, defaultselected, defer, disabled, draggable, ended,
+ formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope,
+ loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open,
+ paused, pubdate, readonly, required, reversed, scoped, seamless, seeking,
+ selected, spellcheck, truespeed, willvalidate
+
+ Finally, the following commonly mis-capitalized attribute/property names
+ are evaluated as expected:
+ Schedules a command to query for the computed style of the element
+ represented by this instance. If the element inherits the named style from
+ its parent, the parent will be queried for its value. Where possible, color
+ values will be converted to their hex representation (e.g. #00ff00 instead of
+ rgb(0, 255, 0)).
+ Schedules a command to retrieve the inner HTML of this element. Schedules a command to compute the location of this element in page space. Schedules a command to retrieve the outer HTML of this element. Schedules a command to compute the size of this element's bounding box, in
+ pixels. Schedules a command to query for the tag/node name of this element. Get the visible (i.e. not hidden by CSS) innerText of this element, including
+ sub-elements, without any leading or trailing whitespace. Schedules a command to test whether this element is currently displayed. Schedules a command to test if there is at least one descendant of this
+ element that matches the given search criteria. Schedules a command to query whether the DOM element represented by this
+ instance is enabled, as dicted by the Schedules a command to query whether this element is selected. Schedules a command that targets this element with the parent WebDriver
+ instance. Will ensure this element's ID is included in the command parameters
+ under the "id" key. Schedules a command to type a sequence on the DOM element represented by this
+ instance.
+ Schedules a command to submit the form containing this element (or this
+ element if it is a FORM element). This command is a no-op if the element is
+ not contained in a form. Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the The parent WebDriver instance for this element. A promise that resolves to the JSON representation of this WebElement's
+ ID, as defined by the WebDriver wire protocol. Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ Compares to WebElements for equality. The property key used in the wire protocol to indicate that a JSON object
+ contains the ID of a WebElement. Communicates with a WebDriver server, which may be on a different domain,
+ using the cross-origin resource sharing
+ (CORS) extension to WebDriver's JSON wire protocol.
+
+ Each command from the standard JSON protocol will be encoded in a
+ JSON object with the following form:
+ {method:string, path:string, data:!Object}
+
+ The encoded command is then sent as a POST request to the server's /xdrpc
+ endpoint. The server will decode the command, re-route it to the appropriate
+ handler, and then return the command's response as a standard JSON response
+ object. The JSON responses will always be returned with a 200
+ response from the server; clients must rely on the response's "status" field
+ to determine whether the command succeeded.
+
+ This client cannot be used with the standard wire protocol due to
+ limitations in the various browser implementations of the CORS specification:
+ Tests whether the current environment supports cross-origin resource sharing. Resource URL to send commands to on the server. A command executor that communicates with a server using the WebDriver
+ command protocol. Client used to communicate with the server. Builds a fully qualified path using the given set of command parameters. Each
+ path segment prefixed with ':' will be replaced by the value of the
+ corresponding parameter. All parameters spliced into the path will be
+ removed from the parameter map. Callback used to parse Maps command names to resource locator. Describes a partial HTTP request. This class is a "partial" request and only
+ defines the path on the server to send a request to. It is each
+ Represents a HTTP response. Builds a A HTTP client that sends requests using XMLHttpRequests. A single log entry. Converts a Special error used to signal when a task is canceled because a previous
+ task in the same frame failed. Handles the execution of scheduled tasks, each of which may be an
+ asynchronous operation. The control flow will ensure tasks are executed in
+ the ordered scheduled, starting each task only once those before it have
+ completed.
+
+ Each task scheduled within this flow may return a
+ Tasks and each callback registered on a Each time a ControlFlow empties its task queue, it will fire an
+ Aborts the current frame. The frame, and all of the tasks scheduled within it
+ will be discarded. If this instance does not have an active frame, it will
+ immediately terminate all execution. Aborts this flow, abandoning all remaining tasks. If there are
+ listeners registered, an Appends a summary of this instance's recent task history to the given
+ error's stack trace. This function will also ensure the error's stack trace
+ is in canonical form. Schedules a task that will wait for another promise to resolve. The resolved
+ promise's value will be returned as the task result. Cancels the event loop, if necessary. Cancels the shutdown sequence if it is currently scheduled. Clears this instance's task history. Commences the shutdown sequence for this instance. After one turn of the
+ event loop, this object will emit the
+ Schedules a task for execution. If there is nothing currently in the
+ queue, the task will be executed in the next turn of the event loop. Returns a summary of the recent task activity for this instance. This
+ includes the most recently completed task, as well as any parent tasks. In
+ the returned summary, the task at index N is considered a sub-task of the
+ task at index N+1. Executes the next task for the current frame. If the current frame has no
+ more tasks, the frame's result will be resolved, returning control to the
+ frame's creator. This will terminate the flow if the completed frame was at
+ the top of the stack. Executes a function in a new frame. If the function does not schedule any new
+ tasks, the frame will be discarded and the function's result returned
+ immediately. Otherwise, a promise will be returned. This promise will be
+ resolved with the function's result once all of the tasks scheduled within
+ the function have been completed. If the function's frame is aborted, the
+ returned promise will be rejected. Schedules the interval for this instance's event loop, if necessary. Inserts a Removes a completed task from this instance's history record. If any
+ tasks remain from aborted frames, those will be removed as well. Schedules a task that shall wait for a condition to hold. Each condition
+ function may return any value, but it will always be evaluated as a boolean.
+
+ Condition functions may schedule sub-tasks with this instance, however,
+ their execution time will be factored into whether a wait has timed out.
+
+ In the event a condition returns a Promise, the polling loop will wait for
+ it to be resolved before evaluating whether the condition has been satisfied.
+ The resolution time for a promise is factored into whether a wait has timed
+ out.
+
+ If the condition function throws, or returns a rejected promise, the
+ wait task will fail. Registers a listener. Registers a listener. An alias for Registers a one-time listener which will be called only the first time an
+ event is emitted, after which it will be removed. Removes all listeners for a specific type of event. If no event is
+ specified, all listeners across all types will be removed. Removes a previously registered event listener. Tracks the active execution frame for this instance. Lazily initialized
+ when the first task is scheduled. Interval ID for this instance's event loop. The number of aborted frames since the last time a task was executed or a
+ frame completed successfully. The number of "pending" promise rejections.
+
+ Each time a promise is rejected and is not handled by a listener, it will
+ schedule a 0-based timeout to check if it is still unrejected in the next
+ turn of the JS-event loop. This allows listeners to attach to, and handle,
+ the rejected promise at any point in same turn of the event loop that the
+ promise was rejected.
+
+ When this flow's own event loop triggers, it will not run if there
+ are any outstanding promise rejections. This allows unhandled promises to
+ be reported before a new task is started, ensuring the error is reported to
+ the current task queue. A reference to the frame in which new tasks should be scheduled. If
+ Timeout ID set when the flow is about to shutdown without any errors
+ being detected. Upon shutting down, the flow will emit an
+ The timer used by this instance. Property used to track whether an error has been annotated by
+ How often, in milliseconds, the event loop should run. The default timer object, which uses the global timer functions. Represents a value that will be resolved at some point in the future. This
+ class represents the protected "producer" half of a Promise - each Deferred
+ has a If this Deferred is rejected and there are no listeners registered before
+ the next turn of the event loop, the rejection will be passed to the
+ If this Deferred is cancelled, the cancellation reason will be forward to
+ the Deferred's canceller function (if provided). The canceller may return a
+ truth-y value to override the reason provided for rejection. Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ An execution frame within a Each frame may contain sub-frames. If child N is a sub-frame, then the
+ items queued within it are given priority over child N+1. Adds a new node to this frame. Marks all of the tasks that are descendants of this frame in the execution
+ tree as cancelled. This is necessary for callbacks scheduled asynchronous.
+ For example:
+
+ var someResult;
+ webdriver.promise.createFlow(function(flow) {
+ someResult = flow.execute(function() {});
+ throw Error();
+ }).addErrback(function(err) {
+ console.log('flow failed: ' + err);
+ someResult.then(function() {
+ console.log('task succeeded!');
+ }, function(err) {
+ console.log('task failed! ' + err);
+ });
+ });
+ // flow failed: Error: boom
+ // task failed! CanceledTaskError: Task discarded due to a previous
+ // task failure: Error: boom Removes a child from this frame. Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the Whether this frame is active. A frame is considered active once one of its
+ descendants has been removed for execution.
+
+ Adding a sub-frame as a child to an active frame is an indication that
+ a callback to a Whether this frame is currently locked. A locked frame represents a callback
+ or task function which has run to completion and scheduled all of its tasks.
+
+ Once a frame becomes A reference to the last node inserted in this frame. The task currently being executed within this frame. This node's parent. Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ A single node in an Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the This node's parent. Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the A task to be executed by a Registers listeners for when this instance is resolved. This function most
+ overridden by subtypes. Registers a listener for when this promise is rejected. This is synonymous
+ with the Registers a listener to invoke when this promise is resolved, regardless
+ of whether the promise's value was successfully computed. This function
+ is synonymous with the This node's parent. Represents the eventual value of a completed operation. Each promise may be
+ in one of three states: pending, resolved, or rejected. Each promise starts
+ in the pending state and may make a single transition to either a
+ fulfilled or failed state.
+
+ Class representing one stack frame. Stores a snapshot of the stack trace at the time this instance was created.
+ The stack trace will always be adjusted to exclude this function call. The parsed stack trace. This list is lazily generated the first time it is
+ accessed. Utility for performing assertions against a given Asserts that the given Asserts that the wrapped value is a number within a given distance of an
+ expected value. Asserts that the wrapped value is a string or array-like structure
+ containing the given value. Asserts that the wrapped value is a string ending with the given suffix. Asserts that the value managed by this assertion is strictly equal to the
+ given Asserts that the value managed by this assertion is a number strictly
+ greater than Asserts that the value managed by this assertion is a number >= the given
+ value. Asserts that the wrapped value is an instance of the given class. Asserts that the value managed by this assertion is strictly false. Asserts that the wrapped value is null. Asserts that the wrapped value is null or undefined. Asserts that the value managed by this assertion is strictly true. Asserts that the wrapped value is undefined. Asserts that the value managed by this assertion is a number strictly less
+ than the given value. Asserts that the value managed by this assertion is a number <= the given
+ value. Asserts that the wrapped value is a string that matches the given RegExp. Asserts that the wrapped value is a string starting with the given prefix. A self reference provided for writing fluent assertions:
+ webdriver.testing.assert(x).is.equalTo(y); Negates any matchers applied to this instance's value:
+ webdriver.testing.assert(x).not.equalTo(y); Wraps an object literal implementing the Matcher interface. This is used
+ to appease the Closure compiler, which will not treat an object literal as
+ implementing an interface. Accepts strins or array-like structures that contain An assertion that negates any applied matchers. Asserts that the wrapped value is a number within a given distance of an
+ expected value. Asserts that the wrapped value is a string or array-like structure
+ containing the given value. Asserts that the wrapped value is a string ending with the given suffix. Asserts that the value managed by this assertion is strictly equal to the
+ given Asserts that the value managed by this assertion is a number strictly
+ greater than Asserts that the value managed by this assertion is a number >= the given
+ value. Asserts that the wrapped value is an instance of the given class. Asserts that the value managed by this assertion is strictly false. Asserts that the wrapped value is null. Asserts that the wrapped value is null or undefined. Asserts that the value managed by this assertion is strictly true. Asserts that the wrapped value is undefined. Asserts that the value managed by this assertion is a number strictly less
+ than the given value. Asserts that the value managed by this assertion is a number <= the given
+ value. Asserts that the wrapped value is a string that matches the given RegExp. Asserts that the wrapped value is a string starting with the given prefix. A self reference provided for writing fluent assertions:
+ webdriver.testing.assert(x).is.equalTo(y); Negates any matchers applied to this instance's value:
+ webdriver.testing.assert(x).not.equalTo(y); Error codes from the WebDriver wire protocol:
+ http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes Status strings enumerated in the W3C WebDriver working draft. 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 Type of options that an XmlHttp object can have. Status constants for XMLHTTP, matches:
+ http://msdn.microsoft.com/library/default.asp?url=/library/
+ en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp Common Unicode string characters. Character codes inlined to avoid object allocations due to charCode. The index of each URI component in the return value of goog.uri.utils.split. Standard supported query parameters. Enumeration of the buttons used in the advanced interactions API. Common webdriver capability keys. Enumeration of predefined names command names that all command processors
+ will support. Attributes used to communicate with the FirefoxDriver extension. Events used to communicate with the FirefoxDriver extension. Representations of pressable keys that aren't text. These are stored in
+ the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to
+ http://www.google.com.au/search?&q=unicode+pua&btnG=Search Simulate pressing many keys at once in a "chord". Takes a sequence of
+ Logging levels. Log level names from WebDriver's JSON wire protocol. Common log types. Events that may be emitted by an The three states a Install the latest published version using In addition to the npm package, you will to download the WebDriver
+implementations you wish to utilize. As of 2.34.0, To run the tests, you will need to download a copy of the
+ChromeDriver and make
+sure it can be found on your To run the tests against multiple browsers, download the
+Selenium server and
+specify its location through the API documentation is included in the docs module. The API documentation for the
+current release are also available online from the Selenium project. A full user guide is available on the
+Selenium project wiki. Please report any issues using the Selenium issue tracker. Copyright 2009-2014 Software Freedom Conservancy 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 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. A matcher object to be used in assertThat statements. Generates a Matcher from the ‘matches’ and ‘describe’ functions passed in. Interface for the common parts of XMLHttpRequest.
+
+ Mostly copied from externs/w3c_xml.js. Handles the execution of Executes the given Interface used for sending individual HTTP requests to the server. Sends a request to the server. If an error occurs while sending the request,
+ such as a failure to connect to the server, the provided callback will be
+ invoked with a non-null The main user facing module. Exports WebDriver's primary
+ public API and provides convenience assessors to certain sub-modules. A collection of factory functions for creating The base module responsible for bootstrapping the Closure
+ library and providing a means of loading Closure-based modules.
+
+ Each script loaded by this module will be granted access to this module's
+ This module will load all scripts from the "lib" subdirectory, unless the
+ SELENIUM_DEV_MODE environment variable has been set to 1, in which case all
+ scripts will be loaded from the Selenium client containing this script. Loads a symbol by name from the protected Closure context and exports its
+ public API to the provided object. This function relies on Closure code
+ conventions to define the public API of an object as those properties whose
+ name does not end with "_". Creates new Sets Chrome-specific options for drivers created by this builder. Sets the proxy configuration to use for WebDriver clients created by this
+ builder. Any calls to Configures which WebDriver server should be used for new sessions. Overrides
+ the value loaded from the Sets the desired capabilities when requesting a new session. This will
+ overwrite any previously set desired capabilities. The desired capabilities to use when creating a new session. URL of the remote server to use for new clients; initialized from the
+ value of the Creates a new ChromeDriver session. Returns the default ChromeDriver service. If such a service has not been
+ configured, one will be constructed using the default configuration for
+ a ChromeDriver executable found on the system PATH. Sets the default service to use for new ChromeDriver instances. Class for managing ChromeDriver specific options. Add additional command line arguments to use when launching the Chrome
+ browser. Each argument may be specified with or without the "--" prefix
+ (e.g. "--foo" and "foo"). Arguments with an associated value should be
+ delimited by an "=": "foo=bar". Add additional extensions to install when launching Chrome. Each extension
+ should be specified as the path to the packed CRX file, or a Buffer for an
+ extension. Sets whether to leave the started Chrome browser running if the controlling
+ ChromeDriver service is killed before Sets the path to the Chrome binary to use. On Mac OS X, this path should
+ reference the actual Chrome executable, not just the application binary
+ (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome").
+
+ The binary path be absolute or relative to the chromedriver server
+ executable, but it must exist on the machine that will launch Chrome. Sets the path to Chrome's log file. This path should exist on the machine
+ that will launch Chrome. Sets preferences for the "Local State" file in Chrome's user data
+ directory. Sets the logging preferences for the new session. Sets the user preferences for Chrome's user profile. See the "Preferences"
+ file in Chrome's user data directory for examples. Converts this options instance to a Converts this instance to its JSON wire protocol representation. Note this
+ function is an implementation not intended for general use. Extracts the ChromeDriver specific options from the given capabilities
+ object. Creates Enables verbose logging. Sets the number of threads the driver should use to manage HTTP requests.
+ By default, the driver will use 4 threads. Sets the base path for WebDriver REST commands (e.g. "/wd/hub").
+ By default, the driver will accept commands relative to "/". Defines the environment to start the server under. This settings will be
+ inherited by every browser session started by the server. Various utilities for working with
+ Creates a command executor that uses WebDriver's JSON wire protocol. Defines a the A Various HTTP utilities. Queries a WebDriver server for its current status. Waits for a WebDriver server to be healthy and accepting requests. Polls a URL with GET requests until it returns a 2xx response or the
+ timeout expires. Searches the Retrieves the external IP address for this host. Retrieves a loopback address for this machine. Creates a new PhantomJS WebDriver client. Defines functions for configuring a webdriver proxy:
+ Manually configures the browser proxy. The following options are
+ supported:
+ Manages the life and death of a native executable WebDriver server.
+
+ It is expected that the driver server implements the
+ WebDriver
+ Wire Protocol. Furthermore, the managed server should support multiple
+ concurrent sessions, so that this class may be reused for multiple clients. Stops the service if it is not currently running. This function will kill
+ the server immediately. To synchronize with the active control flow, use
+ Starts the server if it is not already running. Schedules a task in the current control flow to stop the server if it is
+ currently running. Promise that resolves to the server's address or null if the server has not
+ been started. Promise that tracks the status of shutting down the server, or null if the
+ server is not currently shutting down. The default amount of time, in milliseconds, to wait for the server to
+ start. Manages the life and death of the Selenium standalone server. The server
+ may be obtained from http://selenium-release.storage.googleapis.com/index.html. Stops the service if it is not currently running. This function will kill
+ the server immediately. To synchronize with the active control flow, use
+ Starts the server if it is not already running. Schedules a task in the current control flow to stop the server if it is
+ currently running. Promise that resolves to the server's address or null if the server has not
+ been started. Promise that tracks the status of shutting down the server, or null if the
+ server is not currently shutting down. Provides wrappers around the following global functions from
+ Mocha's BDD interface:
+ The provided wrappers leverage the You may conditionally suppress a test function using the exported
+ "ignore" function. If the provided predicate returns true, the attached
+ test case will be skipped:
+ Register a function to call before each test in a suite. Ignores the test chained to this function if the provided predicate returns
+ true. Defines a library that simplifies writing assertions against
+ promised values.
+
+ Registers a new assertion to expose from the
+ Parses a JSON string and returns the result. Converts a JSON object to its string representation. Whether the current browser supports the native JSON interface. Checks that a response object does not specify an error as defined by the
+ WebDriver wire protocol. If the response object defines an error, it will
+ be thrown. Otherwise, the response will be returned as is. Converts an error value into its JSON representation as defined by the
+ WebDriver wire protocol. Creates a new success response object with the provided value. Whether the rendering engine version of the current browser is equal to or
+ greater than the given version. This implementation differs from
+ goog.userAgent.isVersion in the following ways:
+ Whether the product version of the current browser is equal to or greater
+ than the given version. This implementation differs from
+ goog.userAgent.product.isVersion in the following ways:
+ Whether the current browser is Android pre-gingerbread. Whether the current browser is Android pre-icecreamsandwich Android Operating System Version. Whether we are in a Firefox extension. When we are in a Firefox extension, this is a function that accepts a version
+ and returns whether the version of Gecko we are on is the same or higher
+ than the given version. When we are not in a Firefox extension, this is null. When we are in a Firefox extension, this is a function that accepts a version
+ and returns whether the version of Firefox we are on is the same or higher
+ than the given version. When we are not in a Firefox extension, this is null. Whether the current document is IE in IE10 (or newer) standards mode. Whether the current document is IE in IE9 (or newer) standards mode. Whether the current document is IE in a documentMode older than 10. Whether the current document is IE in a documentMode older than 8. Whether the current document is IE in a documentMode older than 9. Whether we are on IOS. Whether we are on a mobile browser. Whether the current browser is Safari 6. Whether the current browser is Windows Phone. Base namespace for the Closure library. Checks to see goog is already
+ defined in the current scope before assigning to prevent clobbering if
+ base.js is loaded more than once. When defining a class Foo with an abstract method bar(), you can do:
+ Foo.prototype.bar = goog.abstractMethod
+
+ Now if a subclass of Foo fails to override bar(), an error will be thrown
+ when bar() is invoked.
+
+ Note: This does not take the name of the function to override as an argument
+ because that would make it more difficult to obfuscate our JavaScript code. Adds a dependency from a file to the files it requires. Adds a Call up to the superclass.
+
+ If this is called from a constructor, then this calls the superclass
+ constructor with arguments 1-N.
+
+ If this is called from a prototype method, then you must pass the name of the
+ method as the second argument to this function. If you do not, you will get a
+ runtime error. This calls the superclass' method with arguments 2-N.
+
+ This function only works if you use goog.inherits to express inheritance
+ relationships between your classes.
+
+ This function is a compiler primitive. At compile-time, the compiler will do
+ macro expansion to remove a lot of the extra overhead that this function
+ introduces. The compiler will also enforce a lot of the assumptions that this
+ function makes, and treat it as a compiler error if you break them. Partially applies this function to a particular 'this object' and zero or
+ more arguments. The result is a new function with some arguments of the first
+ function pre-filled and the value of this 'pre-specified'.
+
+ Remaining arguments specified at call-time are appended to the pre-specified
+ ones.
+
+ Also see: A pure-JS implementation of goog.bind. A native implementation of goog.bind. Clones a value. The input may be an Object, Array, or basic type. Objects and
+ arrays will be cloned recursively.
+
+ WARNINGS:
+ Defines a named value. In uncompiled mode, the value is retreived from
+ CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and
+ has the property specified, and otherwise used the defined defaultValue.
+ When compiled, the default can be overridden using compiler command-line
+ options. Builds an object structure for the provided namespace path, ensuring that
+ names that already exist are not overwritten. For example:
+ "a.b.c" -> a = {};a.b={};a.b.c={};
+ Used by goog.provide and goog.exportSymbol. Exports a property unobfuscated into the object's namespace.
+ ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction);
+ ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); Exposes an unobfuscated global namespace path for the given object.
+ Note that fields of the exported object *will* be obfuscated, unless they are
+ exported in turn via this function or goog.exportProperty.
+
+ Also handy for making public items that are defined in anonymous closures.
+
+ ex. goog.exportSymbol('public.path.Foo', Foo);
+
+ ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction);
+ public.path.Foo.staticFunction();
+
+ ex. goog.exportSymbol('public.path.Foo.prototype.myMethod',
+ Foo.prototype.myMethod);
+ new public.path.Foo().myMethod(); Tries to detect the base path of base.js script that bootstraps Closure. Forward declares a symbol. This is an indication to the compiler that the
+ symbol may be used in the source yet is not required and may not be provided
+ in compilation.
+
+ The most common usage of forward declaration is code that takes a type as a
+ function parameter but does not need to require it. By forward declaring
+ instead of requiring, no hard dependency is made, and (if not required
+ elsewhere) the namespace may never be required and thus, not be pulled
+ into the JavaScript binary. If it is required elsewhere, it will be type
+ checked as normal. Handles strings that are intended to be used as CSS class names.
+
+ This function works in tandem with @see goog.setCssNameMapping.
+
+ Without any mapping set, the arguments are simple joined with a hyphen and
+ passed through unaltered.
+
+ When there is a mapping, there are two possible styles in which these
+ mappings are used. In the BY_PART style, each part (i.e. in between hyphens)
+ of the passed in css name is rewritten according to the map. In the BY_WHOLE
+ style, the full css name is looked up in the map directly. If a rewrite is
+ not specified by the map, the compiler will output a warning.
+
+ When the mapping is passed to the compiler, it will replace calls to
+ goog.getCssName with the strings from the mapping, e.g.
+ var x = goog.getCssName('foo');
+ var y = goog.getCssName(this.baseClass, 'active');
+ becomes:
+ var x= 'foo';
+ var y = this.baseClass + '-active';
+
+ If one argument is passed it will be processed, if two are passed only the
+ modifier will be processed, as it is assumed the first argument was generated
+ as a result of calling goog.getCssName. Adds a hash code field to an object. The hash code is unique for the
+ given object. Gets a localized message.
+
+ This function is a compiler primitive. If you give the compiler a localized
+ message bundle, it will replace the string at compile-time with a localized
+ version, and expand goog.getMsg call to a concatenated string.
+
+ Messages must be initialized in the form:
+ Gets a localized message. If the message does not have a translation, gives a
+ fallback message.
+
+ This is useful when introducing a new message that has not yet been
+ translated into all languages.
+
+ This function is a compiler primitive. Must be used in the form:
+ Returns an object based on its fully qualified external name. The object
+ is not found if null or undefined. If you are using a compilation pass that
+ renames property names beware that using this function will not find renamed
+ properties. Looks at the dependency rules and tries to determine the script file that
+ fulfills a particular rule. Gets a unique ID for an object. This mutates the object so that further calls
+ with the same object as a parameter returns the same value. The unique ID is
+ guaranteed to be unique across the current session amongst objects that are
+ passed into Evals JavaScript in the global scope. In IE this uses execScript, other
+ browsers use goog.global.eval. If goog.global.eval does not evaluate in the
+ global scope (for example, in Safari), appends a script tag instead.
+ Throws an exception if neither execScript or eval is defined. Globalizes a whole namespace, such as goog or goog.lang. Whether the given object is alreay assigned a unique ID.
+
+ This does not modify the object. The identity function. Returns its first argument. Imports a script if, and only if, that script hasn't already been imported.
+ (Must be called at execution time) Tries to detect whether is in the context of an HTML document. Inherit the prototype methods from one constructor into another.
+
+ Usage:
+ Returns true if the specified value is an array. Returns true if the object looks like an array. To qualify as array like
+ the value needs to be either a NodeList or an object with a Number length
+ property. Returns true if the specified value is a boolean. Returns true if the object looks like a Date. To qualify as Date-like the
+ value needs to be an object and have a getFullYear() function. Returns true if the specified value is not undefined.
+ WARNING: Do not use this to test if an object has a property. Use the in
+ operator instead. Returns true if the specified value is defined and not null. Returns true if the specified value is a function. Returns true if the specified value is null. Returns true if the specified value is a number. Returns true if the specified value is an object. This includes arrays and
+ functions. Check if the given name has been goog.provided. This will return false for
+ names that are available only as implicit namespaces. Returns true if the specified value is a string. Copies all the members of a source object to a target object. This method
+ does not work on all browsers for all objects that contain keys such as
+ toString or hasOwnProperty. Use goog.object.extend for this purpose. Null function used for default values of callbacks, etc. Like bind(), except that a 'this object' is not required. Useful when the
+ target function is already bound.
+
+ Usage:
+ var g = partial(f, arg1, arg2);
+ g(arg3, arg4); Creates object stubs for a namespace. The presence of one or more
+ goog.provide() calls indicate that the file defines the given
+ objects/namespaces. Provided objects must not be null or undefined.
+ Build tools also scan for provide/require statements
+ to discern dependencies, build dependency files (see deps.js), etc. Removes the hash code field from an object. Removes the unique ID from an object. This is useful if the object was
+ previously mutated using Implements a system for the dynamic resolution of dependencies that works in
+ parallel with the BUILD system. Note that all calls to goog.require will be
+ stripped by the JSCompiler when the --closure_pass option is used. Allow for aliasing within scope functions. This function exists for
+ uncompiled code - in compiled code the calls will be inlined and the aliases
+ applied. In uncompiled code the function is simply run since the aliases as
+ written are valid JavaScript. Sets the map to check when returning a value from goog.getCssName(). Example:
+ Marks that the current file should only be used for testing, and never for
+ live code in production.
+
+ In the case of unit tests, the message may optionally be an exact namespace
+ for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra
+ provide (if not explicitly defined in the code). This is a "fixed" version of the typeof operator. It differs from the typeof
+ operator in such a way that null returns 'null' and arrays return 'array'. The default implementation of the import function. Writes a script tag to
+ import the script. Resolves dependencies based on the dependencies added using addDependency
+ and calls importScript_ in the correct order. True if goog.dependencies_ is available. Name for unique ID property. Initialized in a way to help avoid collisions
+ with other closure JavaScript on the same page. Path for included scripts. Optional obfuscation style for CSS class names. Should be set to either
+ 'BY_WHOLE' or 'BY_PART' if defined. Optional map of CSS class names to obfuscated names used with
+ goog.getCssName(). This object is used to keep track of dependencies and other data that is
+ used for loading scripts. Indicates whether or not we can call 'eval' directly to eval code in the
+ global scope. Set to a Boolean by the first call to goog.globalEval (which
+ empirically tests whether eval works for globals). @see goog.globalEval Reference to the global context. In most cases this will be 'window'. Namespaces implicitly defined by goog.provide. For example,
+ goog.provide('goog.events.Event') implicitly declares that 'goog' and
+ 'goog.events' must be namespaces. Object used to keep track of urls that have already been added. This record
+ allows the prevention of circular dependencies. All singleton classes that have been instantiated, for testing. Don't read
+ it directly, use the Counter for UID. Inserts a value into a sorted array. The array is not modified if the
+ value is already present. Removes a value from a sorted array. Searches the specified array for the specified target using the binary
+ search algorithm. If no opt_compareFn is specified, elements are compared
+ using Implementation of a binary search algorithm which knows how to use both
+ comparison functions and evaluators. If an evaluator is provided, will call
+ the evaluator with the given optional data object, conforming to the
+ interface defined in binarySelect. Otherwise, if a comparison function is
+ provided, will call the comparison function against the given data object.
+
+ This implementation purposefully does not use goog.bind or goog.partial for
+ performance reasons.
+
+ Runtime: O(log n) Selects an index in the specified array using the binary search algorithm.
+ The evaluator receives an element and determines whether the desired index
+ is before, at, or after it. The evaluator must be consistent (formally,
+ goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign)
+ must be monotonically non-increasing).
+
+ Runtime: O(log n) Splits an array into disjoint buckets according to a splitting function. Clears the array. Does a shallow copy of an array. 3-way array compare function. Returns a new array that is the result of joining the arguments. If arrays
+ are passed then their items are added, however, if non-arrays are passed they
+ will be added to the return array as is.
+
+ Note that ArrayLike objects will be added as is, rather than having their
+ items added.
+
+ goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4]
+ goog.array.concat(0, [1, 2]) -> [0, 1, 2]
+ goog.array.concat([1, 2], null) -> [1, 2, null]
+
+ There is bug in all current versions of IE (6, 7 and 8) where arrays created
+ in an iframe become corrupted soon (not immediately) after the iframe is
+ destroyed. This is common if loading data via goog.net.IframeIo, for example.
+ This corruption only affects the concat method which will start throwing
+ Catastrophic Errors (#-2147418113).
+
+ See http://endoflow.com/scratch/corrupted-arrays.html for a test case.
+
+ Internally goog.array should use this, so that all methods will continue to
+ work on these broken array objects. Whether the array contains the given object. Counts the array elements that fulfill the predicate, i.e. for which the
+ callback function returns true. Skips holes in the array. Compares its two arguments for order, using the built in < and >
+ operators. Compares its two arguments for equality, using the built in === operator. Compares two arrays for equality. Two arrays are considered equal if they
+ have the same length and their corresponding elements are equal according to
+ the comparison function. Call f for each element of an array. If all calls return true, every()
+ returns true. If any call returns false, every() returns false and
+ does not continue to check the remaining elements.
+
+ See Extends an array with another array, element, or "array like" object.
+ This function operates 'in-place', it does not create a new Array.
+
+ Example:
+ var a = [];
+ goog.array.extend(a, [0, 1]);
+ a; // [0, 1]
+ goog.array.extend(a, 2);
+ a; // [0, 1, 2] Calls a function for each element in an array, and if the function returns
+ true adds the element to a new array.
+
+ See Search an array for the first element that satisfies a given condition and
+ return that element. Search an array for the first element that satisfies a given condition and
+ return its index. Search an array (in reverse order) for the last element that satisfies a
+ given condition and return its index. Search an array (in reverse order) for the last element that satisfies a
+ given condition and return that element. Returns an array consisting of every argument with all arrays
+ expanded in-place recursively. Calls a function for each element in an array. Skips holes in the array.
+ See Calls a function for each element in an array, starting from the last
+ element rather than the first. Returns the index of the first element of an array with a specified value, or
+ -1 if the element is not present in the array.
+
+ See Pushes an item into an array, if it's not already in the array. Inserts at the given index of the array, all elements of another array. Inserts an object at the given index of the array. Inserts an object into an array before a specified object. Whether the array is empty. Tells if the array is sorted. Returns a new array that contains the contents of all the arrays passed. Returns the last element in an array without removing it.
+ Same as goog.array.peek. Returns the index of the last element of an array with a specified value, or
+ -1 if the element is not present in the array.
+
+ See Calls a function for each element in an array and inserts the result into a
+ new array.
+
+ See Moves one item of an array to a new position keeping the order of the rest
+ of the items. Example use case: keeping a list of JavaScript objects
+ synchronized with the corresponding list of DOM elements after one of the
+ elements has been dragged to a new position. Returns the last element in an array without removing it.
+ Same as goog.array.last. Creates a range of numbers in an arithmetic progression.
+
+ Range takes 1, 2, or 3 arguments:
+ Passes every element of an array into a function and accumulates the result.
+
+ See Passes every element of an array into a function and accumulates the result,
+ starting from the last element and working towards the first.
+
+ See Removes the first occurrence of a particular value from an array. Removes from an array the element at index i Removes all duplicates from an array (retaining only the first
+ occurrence of each array element). This function modifies the
+ array in place and doesn't change the order of the non-duplicate items.
+
+ For objects, duplicates are identified as having the same unique ID as
+ defined by Removes the first value that satisfies the given condition. Returns an array consisting of the given value repeated N times. Rotates an array in-place. After calling this method, the element at
+ index i will be the element previously at index (i - n) %
+ array.length, for all values of i between 0 and array.length - 1,
+ inclusive.
+
+ For example, suppose list comprises [t, a, n, k, s]. After invoking
+ rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k]. Shuffles the values in the specified array using the Fisher-Yates in-place
+ shuffle (also known as the Knuth Shuffle). By default, calls Math.random()
+ and so resets the state of that random number generator. Similarly, may reset
+ the state of the any other specified random number generator.
+
+ Runtime: O(n) Returns a new array from a segment of an array. This is a generic version of
+ Array slice. This means that it might work on other objects similar to
+ arrays, such as the arguments object. Calls f for each element of an array. If any call returns true, some()
+ returns true (without checking the remaining elements). If all calls
+ return false, some() returns false.
+
+ See Sorts the specified array into ascending order. If no opt_compareFn is
+ specified, elements are compared using
+ Sorts an array of objects by the specified object key and compare
+ function. If no compare function is provided, the key values are
+ compared in ascending order using Adds or removes elements from an array. This is a generic version of Array
+ splice. This means that it might work on other objects similar to arrays,
+ such as the arguments object. Sorts the specified array into ascending order in a stable way. If no
+ opt_compareFn is specified, elements are compared using
+ Converts an object to an array. Creates a new object built from the provided array and the key-generation
+ function. Creates a new array for which the element at position i is an array of the
+ ith element of the provided arrays. The returned array will only be as long
+ as the shortest array provided; additional values are ignored. For example,
+ the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]].
+
+ This is similar to the zip() function in Python. See Reference to the original Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is
+ true. Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true. Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true. Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true. 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. Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true. Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true. Checks that no enumerable keys are present in Object.prototype. Such keys
+ would break most code that use Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true. Throws an exception with the given message and "Assertion failed" prefixed
+ onto it. 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:
+
+ Always returns false. Always returns NULL. Always returns true. Creates a function that returns true if each of its components evaluates
+ to true. The components are evaluated in order, and the evaluation will be
+ short-circuited as soon as a function returns false.
+ For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x). Gives a wrapper function that caches the return value of a parameterless
+ function when first called.
+
+ When called for the first time, the given function is called and its
+ return value is cached (thus this is only appropriate for idempotent
+ functions). Subsequent calls will return the cached return value. This
+ allows the evaluation of expensive functions to be delayed until first used.
+
+ To cache the return values of functions with parameters, see goog.memoize. Creates the composition of the functions passed in.
+ For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)). Creates a function that always returns the same value. Generic factory function to construct an object given the constructor
+ and the arguments. Intended to be bound to create object factories.
+
+ Callers should cast the result to the appropriate type for proper type
+ checking by the compiler. Creates a function that always throws an error with the given message. Creates a function that throws the given object. A simple function that returns the first argument of whatever is passed
+ into it. Given a function, create a function that keeps opt_numArgs arguments and
+ silently discards all additional arguments. Creates a function that returns the Boolean opposite of a provided function.
+ For example, (goog.functions.not(f))(x) is equivalent to !f(x). Creates a function that returns its nth argument. Creates a function that returns true if any of its components evaluates
+ to true. The components are evaluated in order, and the evaluation will be
+ short-circuited as soon as a function returns true.
+ For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x). Creates a function that calls the functions passed in in sequence, and
+ returns the value of the last function. For example,
+ (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x). Given a function, create a new function that swallows its return value
+ and replaces it with a new one. Creates an iterator that returns running totals from the numbers in
+ Takes zero or more iterables and returns one iterator that will iterate over
+ them in the order chained. Takes a single iterable containing zero or more iterables and returns one
+ iterator that will iterate over each one in the order given. Creates an iterator that returns combinations of elements from
+ Creates an iterator that returns combinations of elements from
+ Creates an iterator that filters Creates an iterator that is advanced Creates an iterator that counts indefinitely from a starting value. Create an iterator to cycle over the iterable's elements indefinitely.
+ For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ... Builds a new iterator that iterates over the original, but skips elements as
+ long as a supplied function returns true. Creates an iterator that returns arrays containing a count and an element
+ obtained from the given Iterates over two iterables and returns true if they contain the same
+ sequence of elements and have the same length. Goes through the values in the iterator. Calls f for each of these and if any
+ of them returns false this returns false (without checking the rest). If all
+ return true this will return true. Calls a function for every element in the iterator, and if the function
+ returns true adds the element to a new iterator. Calls a function for every element in the iterator, and if the function
+ returns false adds the element to a new iterator. Calls a function for each element in the iterator with the element of the
+ iterator passed as argument. Creates an iterator that returns arrays containing elements from the
+ Checks an array for duplicate elements. Joins the values in a iterator with a delimiter. Creates an iterator that returns the first For every element in the iterator call a function and return a new iterator
+ with that value. Advances the iterator to the next position, returning the given default value
+ instead of throwing an exception if the iterator has no more entries. Creates an iterator that returns permutations of elements in
+ Cartesian product of zero or more sets. Gives an iterator that gives every
+ combination of one element chosen from each set. For example,
+ ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]). Creates a new iterator that returns the values in a range. This function
+ can take 1, 2 or 3 arguments:
+ Passes every element of an iterator into a function and accumulates the
+ result. Creates an iterator that returns the same object or value repeatedly. Creates an iterator that returns a range of elements from an iterable.
+ Similar to goog.array#slice but does not support negative indexes. Goes through the values in the iterator. Calls f for each of these, and if
+ any of them returns true, this returns true (without checking the rest). If
+ all return false this will return false. Gives an iterator that gives the result of calling the given function
+ Builds a new iterator that iterates over the original, but only as long as a
+ supplied function returns true. Returns an array of iterators each of which can iterate over the values in
+ Converts the iterator to an array Returns an iterator that knows how to iterate over the values in the object. Creates an iterator that returns arrays containing the ith elements from the
+ provided iterables. The returned arrays will be the same size as the number
+ of iterables given in Creates an iterator that returns arrays containing the ith elements from the
+ provided iterables. The returned arrays will be the same size as the number
+ of iterables given in Singleton Error object that is used to terminate iterations. Tests if a string is an invalid JSON string. This only ensures that we are
+ not using any invalid characters Parses a JSON string and returns the result. This throws an exception if
+ the string is an invalid JSON string.
+
+ Note that this is very slow on large strings. If you trust the source of
+ the string then you should use unsafeParse instead. Serializes an object or a value to a JSON string. Parses a JSON string and returns the result. This uses eval so it is open
+ to security issues and it should only be used if you trust the source. Asserts that the actual value evaluated by the matcher is true. Determines IE version. More information:
+ http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString
+ http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
+ http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx
+ http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx Determines Opera version. More information:
+ http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond Nearly all User-Agents start with Mozilla/N.0. This looks at the second tuple
+ for the actual browser version number. For more information, see:
+ http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html Parses the user agent into tuples for each section. Gets the native userAgent string from navigator if it exists.
+ If navigator or navigator.userAgent string is missing, returns an empty
+ string. Getter for the native navigator.
+ This is a separate function so it can be stubbed out in testing. Applications may override browser detection on the built in
+ navigator.userAgent object by setting this string. Set to null to use the
+ browser object instead. A possible override for applications which wish to not check
+ navigator.userAgent but use a specified value for detection instead. Computes the angle between two points (x1,y1) and (x2,y2).
+ Angle zero points in the +X direction, 90 degrees points in the +Y
+ direction (down) and from there we grow clockwise towards 360 degrees. Computes the difference between startAngle and endAngle (angles in degrees). For a given angle and radius, finds the X portion of the offset. For a given angle and radius, finds the Y portion of the offset. Returns the arithmetic mean of the arguments. Takes a number and clamps it to within the provided bounds. Returns whether the supplied number is finite and not NaN. Returns whether the supplied number represents an integer, i.e. that is has
+ no fractional component. No range-checking is performed on the number. Performs linear interpolation between values a and b. Returns the value
+ between a and b proportional to x (when x is between 0 and 1. When x is
+ outside this range, the return value is a linear extrapolation). Returns the precise value of floor(log10(num)).
+ Simpler implementations didn't work because of floating point rounding
+ errors. For example
+ JavaScript implementation of Longest Common Subsequence problem.
+ http://en.wikipedia.org/wiki/Longest_common_subsequence
+
+ Returns the longest possible array that is subarray of both of given arrays. The % operator in JavaScript returns the remainder of a / b, but differs from
+ some other languages in that the result will have the same sign as the
+ dividend. For example, -1 % 8 == -1, whereas in some other languages
+ (such as Python) the result would be 7. This function emulates the more
+ correct modulo behavior, which is useful for certain applications such as
+ calculating an offset index in a circular list. Tests whether the two values are equal to each other, within a certain
+ tolerance to adjust for floating point errors. Returns a random integer greater than or equal to 0 and less than A tweaked variant of A tweaked variant of Returns the unbiased sample variance of the arguments. For a definition,
+ see e.g. http://en.wikipedia.org/wiki/Variance Returns the sign of a number as per the "sign" or "signum" function. Normalizes an angle to be in range [0-360). Angles outside this range will
+ be normalized to be the equivalent angle with that range. Normalizes an angle to be in range [0-2*PI). Angles outside this range will
+ be normalized to be the equivalent angle with that range. Returns the sample standard deviation of the arguments. For a definition of
+ sample standard deviation, see e.g.
+ http://en.wikipedia.org/wiki/Standard_deviation Returns the sum of the arguments. Converts radians to degrees. Converts degrees to radians. Returns a random number greater than or equal to Static class for creating XMLHttpRequest objects. Static class for creating XMLHttpRequest objects. Gets the options to use with the XMLHttpRequest objects obtained using
+ the static methods. Sets the factories for creating XMLHttpRequest objects and their options. Sets the global factory object. The global factory instance for creating XMLHttpRequest objects. 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. Removes all key value pairs from the object/map/hash. Does a flat clone of the object. Whether the object/hash/map contains the given object as a value.
+ An alias for goog.object.containsValue(obj, val). Whether the object/map/hash contains the given key. Whether the object/map/hash contains the given value. This is O(n). Creates a new object built from the key-value pairs provided as arguments. 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. Creates a new object where the property names come from the arguments but
+ the value is always set to true 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. 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, {b: 2, c: 3});
+ o; // {a: 0, b: 2, c: 3} Calls a function for each element in an object/map/hash. If that call returns
+ true, adds the element to a new object. Searches an object for an element that satisfies the given condition and
+ returns its key. Searches an object for an element that satisfies the given condition and
+ returns its value. Calls a function for each element in an object/map/hash. Returns the value for the given key. 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). 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). Returns the number of key-value pairs in the object map. Returns the keys of the object/map/hash. 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) Returns the values of the object/map/hash. Whether the object/map/hash is empty. For every element in an object/map/hash calls a function and inserts the
+ result into a new object. Removes a key-value pair based on the key. Adds a key-value pair to the object/map/hash. Adds a key-value pair to the object/map/hash if it doesn't exist yet. 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. 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. Clones a value. The input may be an Object, Array, or basic type. Objects and
+ arrays will be cloned recursively.
+
+ WARNINGS:
+ The names of the fields that are defined on Object.prototype. Concatenates string expressions. This is useful
+ since some browsers are very inefficient when it comes to using plus to
+ concat strings. Be careful when using null and undefined here since
+ these will not be included in the result. If you need to represent these
+ be sure to cast the argument to a String first.
+ For example:
+ Replaces Windows and Mac new lines with unix style: \r or \r\n with \n. A string comparator that ignores case.
+ -1 = str1 less than str2
+ 0 = str1 equals str2
+ 1 = str1 greater than str2 Determines whether a string contains a substring, ignoring case. Case-insensitive suffix-checker. Case-insensitive equality checker. Case-insensitive prefix-checker. Removes the breaking spaces from the left and right of the string and
+ collapses the sequences of breaking spaces in the middle into single spaces.
+ The original and the result strings render the same way in HTML. Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines
+ and tabs) to a single space, and strips leading and trailing whitespace. Compares elements of a version number. Determines whether a string contains a substring. Returns the non-overlapping occurrences of ss in s.
+ If either s or ss evalutes to false, then returns zero. Generates and returns a string which is unique in the current document.
+ This is useful, for example, to create unique IDs for DOM elements. Fast suffix-checker. Takes a character and returns the escaped string for that character. For
+ example escapeChar(String.fromCharCode(15)) -> "\\x0E". Takes a string and returns the escaped string for that character. Returns a string with at least 64-bits of randomness.
+
+ Doesn't trust Javascript's random function entirely. Uses a combination of
+ random and current timestamp, and then encodes the string in base-36 to
+ make it shorter. String hash function similar to java.lang.String.hashCode().
+ The hash code for a string is computed as
+ s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1],
+ where s[i] is the ith character of the string and n is the length of
+ the string. We mod the result to make it between 0 (inclusive) and 2^32
+ (exclusive). Escapes double quote '"' and single quote '\'' characters in addition to
+ '&', '<', and '>' so that a string can be included in an HTML tag attribute
+ value within double or single quotes.
+
+ It should be noted that > doesn't need to be escaped for the HTML or XML to
+ be valid, but it has been decided to escape it for consistency with other
+ implementations.
+
+ With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the
+ lowercase letter "e".
+
+ NOTE(user):
+ HtmlEscape is often called during the generation of large blocks of HTML.
+ Using statics for the regular expressions and strings is an optimization
+ that can more than half the amount of time IE spends in this function for
+ large apps, since strings and regexes both contribute to GC allocations.
+
+ Testing for the presence of a character before escaping increases the number
+ of function calls, but actually provides a speed increase for the average
+ case -- since the average case often doesn't require the escaping of all 4
+ characters and indexOf() is much cheaper than replace().
+ The worst case does suffer slightly from the additional calls, therefore the
+ opt_isLikelyToContainHtmlChars option has been included for situations
+ where all 4 HTML entities are very likely to be present and need escaping.
+
+ Some benchmarks (times tended to fluctuate +-0.05ms):
+ FireFox IE6
+ (no chars / average (mix of cases) / all 4 chars)
+ no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80
+ indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84
+ indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85
+
+ An additional advantage of checking if replace actually needs to be called
+ is a reduction in the number of object allocations, so as the size of the
+ application grows the difference between the various methods would increase. Checks if a string contains all letters. Checks if a string contains only numbers or letters. Checks if a string is all breaking whitespace. Checks if a string is empty or contains only whitespaces. Checks if a string is null, undefined, empty or contains only whitespaces. Returns whether the given string is lower camel case (e.g. "isFooBar").
+
+ Note that this assumes the string is entirely letters. Checks if a string contains only numbers. Checks if a character is a space character. Checks if a character is a valid unicode character. Returns whether the given string is upper camel case (e.g. "FooBarBaz").
+
+ Note that this assumes the string is entirely letters. Returns a string representation of the given object, with
+ null and undefined being returned as the empty string. Converts \n to Normalizes spaces in a string, replacing all consecutive spaces and tabs
+ with a single space. Replaces non-breaking space with a space. Normalizes whitespace in a string, replacing all whitespace chars with
+ a space. String comparison function that handles numbers in a way humans might expect.
+ Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The
+ comparison is mostly case-insensitive, though strings that are identical
+ except for case are sorted with the upper-case strings before lower-case.
+
+ This comparison function is significantly slower (about 500x) than either
+ the default or the case-insensitive compare. It should not be used in
+ time-critical code, but should be fast enough to sort several hundred short
+ strings (like filenames) with a reasonable delay. Pads number to given length and optionally rounds it to a given precision.
+ For example:
+ Parse a string in decimal or hexidecimal ('0xFFFF') form.
+
+ To parse a particular radix, please use parseInt(string, radix) directly. See
+ https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
+
+ This is a wrapper for the built-in parseInt function that will only parse
+ numbers as base 10 or base 16. Some JS implementations assume strings
+ starting with "0" are intended to be octal. ES3 allowed but discouraged
+ this behavior. ES5 forbids it. This function emulates the ES5 behavior.
+
+ For more information, see Mozilla JS Reference: http://goo.gl/8RiFj Preserve spaces that would be otherwise collapsed in HTML by replacing them
+ with non-breaking space Unicode characters. Encloses a string in double quotes and escapes characters so that the
+ string is a valid JS string. Escapes characters in the string that are not safe to use in a RegExp. Removes the first occurrence of a substring from a string. Removes all occurrences of a substring from a string. Removes a substring of a specified length at a specific
+ index in a string. Repeats a string n times. Splits a string on a separator a limited number of times.
+
+ This implementation is more similar to Python or Java, where the limit
+ parameter specifies the maximum number of splits rather than truncating
+ the number of results.
+
+ See http://docs.python.org/2/library/stdtypes.html#str.split
+ See JavaDoc: http://goo.gl/F2AsY
+ See Mozilla reference: http://goo.gl/dZdZs Fast prefix-checker. Takes a string and replaces newlines with a space. Multiple lines are
+ replaced with a single space. Strip quote characters around a string. The second argument is a string of
+ characters to treat as quotes. This can be a single character or a string of
+ multiple character and in that case each of those are treated as possible
+ quote characters. For example:
+
+ Does simple python-style string substitution.
+ subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog". Converts a string from selector-case to camelCase (e.g. from
+ "multi-part-string" to "multiPartString"), useful for converting
+ CSS selectors and HTML dataset keys to their equivalent JS properties. Takes a string and creates a map (Object) in which the keys are the
+ characters in the string. The value for the key is set to true. You can
+ then use goog.object.map or goog.array.map to change the values. Converts the supplied string to a number, which may be Infinity or NaN.
+ This function strips whitespace: (toNumber(' 123') === 123)
+ This function accepts scientific notation: (toNumber('1e1') === 10)
+
+ This is better than Javascript's built-in conversions because, sadly:
+ (Number(' ') === 0) and (parseFloat('123a') === 123) Converts a string from camelCase to selector-case (e.g. from
+ "multiPartString" to "multi-part-string"), useful for converting JS
+ style and dataset properties to equivalent CSS selectors and HTML keys. Converts a string into TitleCase. First character of the string is always
+ capitalized in addition to the first letter of every subsequent word.
+ Words are delimited by one or more whitespaces by default. Custom delimiters
+ can optionally be specified to replace the default, which doesn't preserve
+ whitespace delimiters and instead must be explicitly included if needed.
+
+ Default delimiter => " ":
+ goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree'
+ goog.string.toTitleCase('one two three') => 'One Two Three'
+ goog.string.toTitleCase(' one two ') => ' One Two '
+ goog.string.toTitleCase('one_two_three') => 'One_two_three'
+ goog.string.toTitleCase('one-two-three') => 'One-two-three'
+
+ Custom delimiter => "_-.":
+ goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree'
+ goog.string.toTitleCase('one two three', '_-.') => 'One two three'
+ goog.string.toTitleCase(' one two ', '_-.') => ' one two '
+ goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three'
+ goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three'
+ goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three'
+ goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three'
+ goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three' Trims white spaces to the left and right of a string. Trims whitespaces at the left end of a string. Trims whitespaces at the right end of a string. Truncates a string to a certain length and adds '...' if necessary. The
+ length also accounts for the ellipsis, so a maximum length of 10 and a string
+ 'Hello World!' produces 'Hello W...'. Truncate a string in the middle, adding "..." if necessary,
+ and favoring the beginning of the string. Unescapes an HTML string. Unescapes an HTML string using a DOM to resolve non-XML, non-numeric
+ entities. This function is XSS-safe and whitespace-preserving. Unescapes a HTML string using the provided document. Unescapes XML entities. URL-decodes the string. We need to specially handle '+'s because
+ the javascript library doesn't convert them to spaces. URL-encodes a string Do escaping of whitespace to preserve spatial formatting. We use character
+ entity #160 to make it safer for xml. Regular expression that matches any character that needs to be escaped. Regular expression that matches an ampersand, for use in escaping. Regular expression that matches a lowercase letter "e", for use in escaping. Regular expression that matches a greater than sign, for use in escaping. Maximum value of #goog.string.hashCode, exclusive. 2^32. Regular expression that matches an HTML entity.
+ See also HTML5: Tokenization / Tokenizing character references. Regular expression that matches a less than sign, for use in escaping. Regular expression that matches null character, for use in escaping. Regular expression that matches a double quote, for use in escaping. Regular expression that matches a single quote, for use in escaping. Character mappings used internally for goog.string.escapeChar. Regular expression used for splitting a string into substrings of fractional
+ numbers, integers, and non-numeric characters. Special chars that need to be escaped for goog.string.quote. The most recent unique ID. |0 is equivalent to Math.floor in this case. Removes all the elements from the collection. Whether the collection contains the given value. This is O(n) and uses
+ equals (==) to test the existence. Calls f for each value in a collection. If all calls return true this return
+ true this returns true. If any returns false this returns false at this point
+ and does not continue to check the remaining values. Calls a function for every value in the collection. When a call returns true,
+ adds the value to a new collection (Array is returned by default). Calls a function for each value in a collection. The function takes
+ three arguments; the value, the key and the collection.
+
+ NOTE: This will be deprecated soon! Please use a more specific method if
+ possible, e.g. goog.array.forEach, goog.object.forEach, etc. Returns the number of values in the collection-like object. Returns the keys of the collection. Some collections have no notion of
+ keys/indexes and this function will return undefined in those cases. Returns the values of the collection-like object. Whether the collection is empty. Calls a function for every value in the collection and adds the result into a
+ new collection (defaults to creating a new Array). Calls f for each value in a collection. If any call returns true this returns
+ true (without checking the rest). If all returns false this returns false. Appends key=value pairs to an array, supporting multi-valued objects. Appends a single URI parameter.
+
+ Repeated calls to this can exhibit quadratic behavior in IE6 due to the
+ way string append works, though it should be limited given the 2kb limit. Appends URI parameters to an existing URI.
+
+ The variable arguments may contain alternating keys and values. Keys are
+ assumed to be already URI encoded. The values should not be URI-encoded,
+ and will instead be encoded by this function.
+ Appends query parameters from a map. Generates a URI path using a given URI and a path with checks to
+ prevent consecutive "//". The baseUri passed in must not contain
+ query or fragment identifiers. The path to append may not contain query or
+ fragment identifiers. Appends a URI and query data in a string buffer with special preconditions.
+
+ Internal implementation utility, performing very few object allocations. Asserts that there are no fragment or query identifiers, only in uncompiled
+ mode. Builds a URI string from already-encoded parts.
+
+ No encoding is performed. Any component may be omitted as either null or
+ undefined. Builds a query data string from a sequence of alternating keys and values.
+ Currently generates "&key&" for empty args. Builds a buffer of query data from a map. Builds a buffer of query data from a sequence of alternating keys and values. Builds a query data string from a map.
+ Currently generates "&key&" for empty args. Finds the next instance of a query parameter with the specified name.
+
+ Does not instantiate any objects. Gets a URI component by index.
+
+ It is preferred to use the getPathEncoded() variety of functions ahead,
+ since they are more readable. Gets the effective scheme for the URL. If the URL is relative then the
+ scheme is derived from the page's location. Extracts everything up to the port of the URI. Gets the first value of a query parameter. Gets all values of a query parameter. Extracts the path of the URL and everything after. Determines if the URI contains a specific key.
+
+ Performs no object instantiations. Ensures that two URI's have the exact same domain, scheme, and port.
+
+ Unlike the version in goog.Uri, this checks protocol, and therefore is
+ suitable for checking against the browser's same-origin policy. Sets the zx parameter of a URI to a random value. Check to see if the user is being phished. Gets the URI with the fragment identifier removed. Removes all instances of a query parameter. Replaces all existing definitions of a parameter with a single definition.
+
+ Repeated calls to this can exhibit quadratic behavior due to the need to
+ find existing instances and reconstruct the string, though it should be
+ limited given the 2kb limit. Consider using appendParams to append multiple
+ parameters in bulk. Replaces the path. Splits a URI into its component parts.
+
+ Each component can be accessed via the component indices; for example:
+ Regular expression for finding a hash mark or end of string. Safari has a nasty bug where if you have an http URL with a username, e.g.,
+ http://evil.com%2F@google.com/
+ Safari will report that window.location.href is
+ http://evil.com/google.com/
+ so that anyone who tries to parse the domain of that URL will get
+ the wrong domain. We've seen exploits where people use this to trick
+ Safari into loading resources from evil domains.
+
+ To work around this, we run a little "Safari phishing check", and throw
+ an exception if we see this happening.
+
+ There is no convenient place to put this check. We apply it to
+ anyone doing URI parsing on Webkit. We're not happy about this, but
+ it fixes the problem.
+
+ This should be removed once Safari fixes their bug.
+
+ Exploit reported by Masato Kinugawa. A regular expression for breaking a URI into its component parts.
+
+ Regexp to find trailing question marks and ampersands. Compares two version numbers. TODO(nnaze): Change type to "Navigator" and update compilation targets. Returns the userAgent string for the current browser. Initialize the goog.userAgent constants that define which platform the user
+ agent is running on. Deprecated alias to 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. Whether the user agent is running on a mobile device.
+
+ This is a separate function so that the logic can be tested.
+
+ TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile(). Deprecated alias to 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. Whether the user agent is running on Android. Whether we know the browser engine at compile-time. 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. Whether the user agent is Gecko. Gecko is the rendering engine used by
+ Mozilla, Firefox, and others. Whether the user agent is Internet Explorer. Whether the user agent is running on an iPad. Whether the user agent is running on an iPhone. Whether the user agent is running on a Linux operating system. Whether the user agent is running on a Macintosh operating system. Whether the user agent is running on a mobile device.
+
+ TODO(nnaze): Consider deprecating MOBILE when labs.userAgent
+ is promoted as the gecko/webkit logic is likely inaccurate. Whether the user agent is Opera. 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). Used while transitioning code to use WEBKIT instead. The version of the user agent. This is a string because it might contain
+ 'b' (as in beta) as well as multiple dots. Whether the user agent is WebKit. WebKit is the rendering engine that
+ Safari, Android and others use. Whether the user agent is running on a Windows operating system. Whether the user agent is running on a X11 windowing system. Whether the user agent is running on Android. Whether the user agent is running on an iPad. Whether the user agent is running on an iPhone. Whether the user agent is running on a Linux operating system. Whether the user agent is running on a Macintosh operating system. Whether the user agent is running on a Windows operating system. Whether the user agent is running on a X11 windowing system. Cache for Run regexp's exec() on the userAgent string. Return the first group of the given regex. Right now we just focus on Tier 1-3 browsers at:
+ http://wiki/Nonconf/ProductPlatformGuidelines
+ As well as the YUI grade A browsers at:
+ http://developer.yahoo.com/yui/articles/gbs/ Whether the user agent product version is higher or the same as the given
+ version. Whether the code is running on the default browser on an Android phone. Whether the code is running on the Camino web browser. Whether the code is running on the Chrome web browser. Whether the code is running on the Firefox web browser. Whether the code is running on an IE web browser. Whether the code is running on an iPad. Whether the code is running on an iPhone or iPod touch. Whether the code is running on the Opera web browser. Whether we know the product type at compile-time. Whether the code is running on the Safari web browser. The version of the user agent. This is a string because it might contain
+ 'b' (as in beta) as well as multiple dots. Whether the code is running on the default browser on an Android phone. Whether the code is running on the Camino web browser. Whether the code is running on the Chrome web browser. Whether the code is running on the Firefox web browser. Whether the code is running on an iPad Whether the code is running on an iPhone or iPod touch. Whether the code is running on the Safari web browser. A collection of factory functions for creating Care should be taken when using JavaScript minifiers (such as the
+ Closure compiler), as locator hashes will always be parsed using
+ the un-obfuscated properties listed below. Locates elements that have a specific class name. The returned locator
+ is equivalent to searching for elements with the CSS selector ".clazz". Locates elements using a CSS selector. For browsers that do not support
+ CSS selectors, WebDriver implementations may return an
+ Locates an element by its ID. Locates an elements by evaluating a
+ Locates link elements whose Locates elements whose Locates link elements whose Locates elements with a given tag name. The returned locator is
+ equivalent to using the Locates elements matching a XPath selector. Care should be taken when
+ using an XPath selector with a Converts a headers object to a HTTP header block string. Converts a level name or value to a Queries for a named environment variable. Initializes a process object for use in a browser window. Sets an environment value. If the new value is either null or undefined, the
+ environment variable will be cleared. Whether the current environment is using Node's native process object. The global process object to use. Will either be Node's global
+ Given an array of promises, will return a promise that will be fulfilled
+ with the fulfillment values of the input array's values. If any of the
+ input array's promises are rejected, the returned promise will be rejected
+ with the same reason. Invokes the appropriate callback function as soon as a promised
+ Wraps a function that is assumed to be a node-style callback as its final
+ argument. This callback takes two arguments: an error value (which will be
+ null if the call succeeded), and the success value as the second argument.
+ If the call fails, the returned promise will be rejected, otherwise it will
+ be resolved with the result. Creates a new control flow. The provided callback will be invoked as the
+ first task within the new flow, with the flow as its sole argument. Returns
+ a promise that resolves to the callback result. Creates a new deferred object. Creates a promise that will be resolved at a set time in the future. Calls a function for each element in an array, and if the function returns
+ true adds the element to a new array.
+
+ If the return value of the filter function is a promise, this function
+ will wait for it to be fulfilled before determining whether to insert the
+ element into the new array.
+
+ If the filter function throws or returns a rejected promise, the promise
+ returned by this function will be rejected with the same reason. Only the
+ first failure will be reported; all subsequent errors will be silently
+ ignored. Creates a promise that has been resolved with the given value. Returns a promise that will be resolved with the input value in a
+ fully-resolved state. If the value is an array, each element will be fully
+ resolved. Likewise, if the value is an object, all keys will be fully
+ resolved. In both cases, all nested arrays and objects will also be
+ fully resolved. All fields are resolved in place; the returned promise will
+ resolve on Tests if a value is an Error-like object. This is more than an straight
+ instanceof check since the value may originate from another context. Determines whether a Calls a function for each element in an array and inserts the result into a
+ new array, which is used as the fulfillment value of the promise returned
+ by this function.
+
+ If the return value of the mapping function is a promise, this function
+ will wait for it to be fulfilled before inserting it into the new array.
+
+ If the mapping function throws or returns a rejected promise, the
+ promise returned by this function will be rejected with the same reason.
+ Only the first failure will be reported; all subsequent errors will be
+ silently ignored. Creates a promise that has been rejected with the given reason. Changes the default flow to use when no others are active. Registers an observer on a promised A stack of active control flows, with the top of the stack used to schedule
+ commands. When there are multiple flows on the stack, the flow at index N
+ represents a callback triggered within a task owned by the flow at index
+ N-1. The default flow to use if no others are active. Formats an error's stack trace. Gets the native stack trace if available otherwise follows the call chain.
+ The generated trace will exclude all frames up to and including the call to
+ this function. Get an error's stack trace with the error string trimmed.
+ V8 prepends the string representation of an error to its stack trace.
+ This function trims the string so that the stack trace can be parsed
+ consistently with the other JS engines. Parses a long firefox stack frame. Parses one stack frame. Parses an Error object's stack trace. Representation of an anonymous frame in a stack trace generated by
+ Constant for an anonymous frame. Whether the current browser supports stack traces. Whether the current environment supports the Error.captureStackTrace
+ function (as of 10/17/2012, only V8). RegExp pattern for function call in a Chakra (IE) stack trace. This
+ expression allows for identifiers like 'Anonymous function', 'eval code',
+ and 'Global code'. Regular expression for parsing on stack frame in Chakra (IE). Pattern for a function call in a Closure stack trace. Creates three optional
+ submatches: the context, function name, and alias. Regular expression for parsing a stack frame generated by Closure's
+ Pattern for a matching the type on a fully-qualified name. Forms an
+ optional sub-match on the type. For example, in "foo.bar.baz", will match on
+ "foo.bar". RegExp pattern for function call in the Firefox stack trace.
+ Creates a submatch for the function name. RegExp pattern for function names in the Firefox stack trace.
+ Firefox has extended identifiers to deal with inner functions and anonymous
+ functions: https://bugzilla.mozilla.org/show_bug.cgi?id=433529#c9 Regular expression for parsing one stack frame in Firefox. RegExp pattern for JavaScript identifiers. We don't support Unicode
+ identifiers defined in ECMAScript v3. Maximum length of a string that can be matched with a RegExp on
+ Firefox 3x. Exceeding this approximate length will cause string.match
+ to exceed Firefox's stack quota. This situation can be encountered
+ when goog.globalEval is invoked with a long argument; such as
+ when loading a module. RegExp pattern for an anonymous function call in an Opera stack frame.
+ Creates 2 (optional) submatches: the context object and function name. RegExp pattern for a function call in an Opera stack frame.
+ Creates 3 (optional) submatches: the function name (if not anonymous),
+ the aliased context object and the function name (if anonymous). Regular expression for parsing on stack frame in Opera 11.68+ Pattern for matching a fully qualified name. Will create two sub-matches:
+ the type (optional), and the name. For example, in "foo.bar.baz", will
+ match on ["foo.bar", "baz"]. Placeholder for an unparsable frame in a stack trace generated by
+ RegExp pattern for an URL + position inside the file. RegExp pattern for function name alias in the V8 stack trace. RegExp pattern for the context of a function call in V8. Creates two
+ submatches, only one of which will ever match: either the namespace
+ identifier (with optional "new" keyword in the case of a constructor call),
+ or just the "new " phrase for a top level constructor call. RegExp pattern for function call in the V8 stack trace.
+ Creates 3 submatches with context object (optional), function name and
+ function alias (optional). RegExp pattern for function names and constructor calls in the V8 stack
+ trace. RegExp pattern for a location string in a V8 stack frame. Creates two
+ submatches for the location, one for enclosed in parentheticals and on
+ where the location appears alone (which will only occur if the location is
+ the only information in the frame). Regular expression for parsing one stack frame in V8. Creates a new assertion. Creates a new assertion. Registers a new assertion to expose from the
+ Asserts that a matcher accepts a given value. This function has two
+ signatures based on the number of arguments:
+
+ Two arguments:
+ assertThat(actualValue, matcher)
+ Three arguments:
+ assertThat(failureMessage, actualValue, matcher) Creates an equality matcher. The JSON functions can be defined by external libraries like Prototype
+ * and setting this flag to false forces the use of Closure's goog.json
+ * implementation.
+ *
+ * If your JavaScript can be loaded by a third_party site and you are wary
+ * about relying on the native functions, specify
+ * "--define bot.json.NATIVE_JSON=false" to the Closure compiler.
+ */
+bot.json.NATIVE_JSON = true;
+
+
+/**
+ * Whether the current browser supports the native JSON interface.
+ * @const
+ * @see http://caniuse.com/#search=JSON
+ * @private {boolean}
+ */
+bot.json.SUPPORTS_NATIVE_JSON_ =
+ // List WebKit and Opera first since every supported version of these
+ // browsers supports native JSON (and we can compile away large chunks of
+ // code for individual fragments by setting the appropriate compiler flags).
+ goog.userAgent.WEBKIT || goog.userAgent.OPERA ||
+ (goog.userAgent.GECKO && bot.userAgent.isEngineVersion(3.5)) ||
+ (goog.userAgent.IE && bot.userAgent.isEngineVersion(8));
+
+
+/**
+ * Converts a JSON object to its string representation.
+ * @param {*} jsonObj The input object.
+ * @param {?(function(string, *): *)=} opt_replacer A replacer function called
+ * for each (key, value) pair that determines how the value should be
+ * serialized. By default, this just returns the value and allows default
+ * serialization to kick in.
+ * @return {string} A JSON string representation of the input object.
+ */
+bot.json.stringify = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ?
+ JSON.stringify : goog.json.serialize;
+
+
+/**
+ * Parses a JSON string and returns the result.
+ * @param {string} jsonStr The string to parse.
+ * @return {*} The JSON object.
+ * @throws {Error} If the input string is an invalid JSON string.
+ */
+bot.json.parse = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ?
+ JSON.parse : goog.json.parse;
diff --git a/node_modules/selenium-webdriver/lib/atoms/response.js b/node_modules/selenium-webdriver/lib/atoms/response.js
new file mode 100644
index 0000000..d929a66
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/atoms/response.js
@@ -0,0 +1,107 @@
+// Copyright 2011 Software Freedom Conservancy. 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 working with WebDriver response objects.
+ * @see: http://code.google.com/p/selenium/wiki/JsonWireProtocol#Responses
+ */
+
+goog.provide('bot.response');
+goog.provide('bot.response.ResponseObject');
+
+goog.require('bot.Error');
+goog.require('bot.ErrorCode');
+
+
+/**
+ * Type definition for a response object, as defined by the JSON wire protocol.
+ * @typedef {{status: bot.ErrorCode, value: (*|{message: string})}}
+ * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Responses
+ */
+bot.response.ResponseObject;
+
+
+/**
+ * @param {*} value The value to test.
+ * @return {boolean} Whether the given value is a response object.
+ */
+bot.response.isResponseObject = function(value) {
+ return goog.isObject(value) && goog.isNumber(value['status']);
+};
+
+
+/**
+ * Creates a new success response object with the provided value.
+ * @param {*} value The response value.
+ * @return {!bot.response.ResponseObject} The new response object.
+ */
+bot.response.createResponse = function(value) {
+ if (bot.response.isResponseObject(value)) {
+ return /** @type {!bot.response.ResponseObject} */ (value);
+ }
+ return {
+ 'status': bot.ErrorCode.SUCCESS,
+ 'value': value
+ };
+};
+
+
+/**
+ * Converts an error value into its JSON representation as defined by the
+ * WebDriver wire protocol.
+ * @param {(bot.Error|Error|*)} error The error value to convert.
+ * @return {!bot.response.ResponseObject} The new response object.
+ */
+bot.response.createErrorResponse = function(error) {
+ if (bot.response.isResponseObject(error)) {
+ return /** @type {!bot.response.ResponseObject} */ (error);
+ }
+
+ var statusCode = error && goog.isNumber(error.code) ? error.code :
+ bot.ErrorCode.UNKNOWN_ERROR;
+ return {
+ 'status': /** @type {bot.ErrorCode} */ (statusCode),
+ 'value': {
+ 'message': (error && error.message || error) + ''
+ }
+ };
+};
+
+
+/**
+ * Checks that a response object does not specify an error as defined by the
+ * WebDriver wire protocol. If the response object defines an error, it will
+ * be thrown. Otherwise, the response will be returned as is.
+ * @param {!bot.response.ResponseObject} responseObj The response object to
+ * check.
+ * @return {!bot.response.ResponseObject} The checked response object.
+ * @throws {bot.Error} If the response describes an error.
+ * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Failed_Commands
+ */
+bot.response.checkResponse = function(responseObj) {
+ var status = responseObj['status'];
+ if (status == bot.ErrorCode.SUCCESS) {
+ return responseObj;
+ }
+
+ // If status is not defined, assume an unknown error.
+ status = status || bot.ErrorCode.UNKNOWN_ERROR;
+
+ var value = responseObj['value'];
+ if (!value || !goog.isObject(value)) {
+ throw new bot.Error(status, value + '');
+ }
+
+ throw new bot.Error(status, value['message'] + '');
+};
diff --git a/node_modules/selenium-webdriver/lib/atoms/userAgent.js b/node_modules/selenium-webdriver/lib/atoms/userAgent.js
new file mode 100644
index 0000000..60964d0
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/atoms/userAgent.js
@@ -0,0 +1,255 @@
+// Copyright 2011 WebDriver committers
+// Copyright 2011 Google Inc.
+//
+// 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 Similar to goog.userAgent.isVersion, but with support for
+ * getting the version information when running in a firefox extension.
+ */
+goog.provide('bot.userAgent');
+
+goog.require('goog.string');
+goog.require('goog.userAgent');
+goog.require('goog.userAgent.product');
+goog.require('goog.userAgent.product.isVersion');
+
+
+/**
+ * Whether the rendering engine version of the current browser is equal to or
+ * greater than the given version. This implementation differs from
+ * goog.userAgent.isVersion in the following ways:
+ * Class bot.Error
code »Error
+ └ bot.ErrorConstructor
Parameters !bot.ErrorCodestring=Enumerations
Instance Methods
Defined in
bot.ErrorInstance Properties
Defined in
bot.ErrorStatic Properties
Class goog.Uri
code »getXyz/setXyz methods return the decoded part
+ -- sogoog.Uri.parse('/foo%20bar').getPath() will return the
+ decoded path, /foo bar.
+
+ The constructor accepts an optional unparsed, raw URI string. The parser
+ is relaxed, so special characters that aren't escaped but don't cause
+ ambiguities will not cause parse failures.
+
+ All setters return this and so may be chained, a la
+ goog.Uri.parse('/foo').setFragment('part').toString().Constructor
Parameters *=boolean=Classes
Instance Methods
stringReturns The decoded URI query, not including the ?.
stringReturns The encoded URI query, not including the ?.
stringReturns The URI fragment, not including the #.
booleanReturns Whether to ignore case.
(string|undefined)Parameters stringReturns The first value for a given cgi parameter or
+ undefined if the given parameter name does not appear in the query
+ string.
!ArrayParameters stringReturns The values for a given cgi parameter as a list of
+ decoded query parameter values.
Returns QueryData object.
stringReturns The decoded user info.
booleanReturns Whether the URI has a fragment set.
booleanParameters goog.UriReturns true if same domain; false otherwise.
booleanReturns Whether the user info has been set.
booleanReturns Whether the URI is read only.
!goog.UriReturns Reference to this Uri object.
!goog.UriParameters stringReturns Reference to this URI object.
+ 1. foo - replaces the last part of the path, the whole query and fragment
+ 2. /foo - replaces the the path, the query and fragment
+ 3. //foo - replaces everything from the domain on. foo is a domain name
+ 4. ?foo - replace the query and fragment
+ 5. #foo - replace the fragment only
+
+ Additionally, if relative URI has a non-empty path, all ".." and "."
+ segments will be resolved, as described in RFC 3986.Parameters goog.UriReturns The resolved URI.
!goog.Uri!goog.UriParameters booleanReturns Reference to this Uri object.
!goog.UriParameters string*Returns Reference to this URI object.
!goog.Urigoog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new'])
+ yields foo?a=b&e=f&c=new.Parameters string*Returns Reference to this URI object.
!goog.UriParameters (goog.Uri.QueryData|string|undefined)boolean=Returns Reference to this URI object.
!goog.UriParameters booleanReturns Reference to this Uri object.
!goog.UriInstance Properties
Static Functions
!goog.UriParameters ?string=?string=?string=?number=?string=(string|goog.Uri.QueryData)=?string=boolean=Returns The new URI object.
stringstringParameters stringReturns Encoded string.
?stringParameters *RegExpReturns null iff unescapedPart == null.
boolean!goog.UriParameters *boolean=Returns The new URI object.
stringParameters stringReturns Path component with removed dot segments.
!goog.UriParameters **Returns Resolved uri.
Static Properties
Class goog.Uri.QueryData
code »Constructor
Parameters ?string=goog.Uri=boolean=Instance Methods
!goog.Uri.QueryDataParameters string*Returns Instance of this object.
!goog.Uri.QueryDataReturns New instance of the QueryData object.
booleanParameters stringReturns Whether there is a parameter with the given name.
booleanParameters *Returns Whether there is a parameter with the given value.
Parameters ...(goog.Uri.QueryData|goog.structs.Map|Object)!goog.Uri.QueryDataParameters string*=Returns The first string value associated with the key, or opt_default
+ if there's no value.
stringParameters *Returns valid key name which can be looked up in #keyMap_.
Parameters string=Returns All the values of the parameters with the given name.
!goog.Uri.QueryDataParameters string*Returns Instance of this object.
Parameters booleanstringReturns Decoded query string.
Instance Properties
Static Functions
!goog.Uri.QueryData!goog.Uri.QueryDataParameters (!goog.structs.Map|!Object)goog.Uri=boolean=Returns The populated query data instance.
Class goog.asserts.AssertionError
code »Error
+ └ goog.debug.Error
+ └ goog.asserts.AssertionErrorConstructor
Instance Properties
Defined in
goog.asserts.AssertionErrorStatic Properties
Class goog.debug.Error
code »Error
+ └ goog.debug.ErrorConstructor
Parameters *=Static Properties
Class goog.iter.GroupByIterator_.
code »<KEY, VALUE>goog.iter.Iterator.<Array>
+ └ goog.iter.GroupByIterator_goog.iter.groupBy iterator.Constructor
Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)function(VALUE): KEY=iterable. Default
+ is the identity function.Instance Methods
Defined in
goog.iter.GroupByIterator_!Array.<VALUE>Parameters KEYReturns An array of grouped objects.
Defined in
goog.iter.Iterator.<Array>!goog.iter.Iterator.<VALUE>Iterator object itself. This is used to implement
+ the iterator protocol in JavaScript 1.7Parameters boolean=Returns The object itself.
Instance Properties
Defined in
goog.iter.GroupByIterator_KEYVALUEStatic Properties
Class goog.iter.Iterator.
code »<VALUE>next
+ method and it needs to throw a goog.iter.StopIteration when the
+ iteration passes beyond the end. Iterators have no hasNext method.
+ It is recommended to always use the helper functions to iterate over the
+ iterator or in case you are only targeting JavaScript 1.7 for in loops.Constructor
Instance Methods
!goog.iter.Iterator.<VALUE>Iterator object itself. This is used to implement
+ the iterator protocol in JavaScript 1.7Parameters boolean=Returns The object itself.
Class goog.json.Serializer
code »Constructor
Parameters ?goog.json.Replacer=Instance Methods
Parameters *ArrayThrows Instance Properties
Static Properties
Class goog.labs.testing.AllOfMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !ArrayInstance Methods
Instance Properties
Class goog.labs.testing.AnyOfMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !ArrayInstance Methods
Instance Properties
Class goog.labs.testing.CloseToMatcher
code »goog.labs.testing.MatcherConstructor
Instance Methods
Instance Properties
Class goog.labs.testing.ContainsStringMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.EndsWithMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.EqualToIgnoringWhitespaceMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.EqualToMatcher
code »goog.labs.testing.MatcherConstructor
Parameters numberInstance Methods
Instance Properties
Class goog.labs.testing.EqualsMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.GreaterThanEqualToMatcher
code »goog.labs.testing.MatcherConstructor
Parameters numberInstance Methods
Instance Properties
Class goog.labs.testing.GreaterThanMatcher
code »goog.labs.testing.MatcherConstructor
Parameters numberInstance Methods
Instance Properties
Class goog.labs.testing.HasPropertyMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.InstanceOfMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !ObjectInstance Methods
Instance Properties
Class goog.labs.testing.IsNotMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !goog.labs.testing.MatcherInstance Methods
Instance Properties
Class goog.labs.testing.IsNullMatcher
code »goog.labs.testing.MatcherConstructor
Instance Methods
Class goog.labs.testing.IsNullOrUndefinedMatcher
code »goog.labs.testing.MatcherConstructor
Instance Methods
Class goog.labs.testing.IsUndefinedMatcher
code »goog.labs.testing.MatcherConstructor
Instance Methods
Class goog.labs.testing.LessThanEqualToMatcher
code »goog.labs.testing.MatcherConstructor
Parameters numberInstance Methods
Instance Properties
Class goog.labs.testing.LessThanMatcher
code »goog.labs.testing.MatcherConstructor
Parameters numberInstance Methods
Instance Properties
Class goog.labs.testing.MatcherError
code »Error
+ └ goog.debug.Error
+ └ goog.labs.testing.MatcherErrorConstructor
Parameters string=Static Properties
Class goog.labs.testing.ObjectEqualsMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !ObjectInstance Methods
Instance Properties
Class goog.labs.testing.RegexMatcher
code »goog.labs.testing.MatcherConstructor
Parameters !RegExpInstance Methods
Instance Properties
Class goog.labs.testing.StartsWithMatcher
code »goog.labs.testing.MatcherConstructor
Parameters stringInstance Methods
Instance Properties
Class goog.labs.testing.StringContainsInOrderMatcher
code »goog.labs.testing.MatcherConstructor
Instance Methods
Instance Properties
Class goog.net.DefaultXmlHttpFactory
code »goog.net.XmlHttpFactory
+ └ goog.net.DefaultXmlHttpFactoryConstructor
Instance Methods
Defined in
goog.net.DefaultXmlHttpFactory(XMLHttpRequest|goog.net.XhrLike)stringReturns The ActiveX PROG ID string to use to create xhr's in IE.
(Object|null)Defined in
goog.net.XmlHttpFactoryObjectReturns Options describing how xhr objects obtained from this
+ factory should be used.
Instance Properties
Defined in
goog.net.DefaultXmlHttpFactoryDefined in
goog.net.XmlHttpFactoryStatic Properties
Class goog.net.WrapperXmlHttpFactory
code »goog.net.XmlHttpFactory
+ └ goog.net.WrapperXmlHttpFactoryConstructor
Parameters function(): !goog.net.XhrLike.OrNativefunction(): !ObjectInstance Methods
Defined in
goog.net.WrapperXmlHttpFactory(XMLHttpRequest|goog.net.XhrLike)(Object|null)Object(XMLHttpRequest|goog.net.XhrLike)Defined in
goog.net.XmlHttpFactoryReturns Options describing how xhr objects obtained from this
+ factory should be used.
Instance Properties
Defined in
goog.net.XmlHttpFactoryStatic Properties
Class goog.net.XmlHttpFactory
code »Constructor
Instance Methods
Returns A new XhrLike instance.
ObjectReturns Options describing how xhr objects obtained from this
+ factory should be used.
Returns Options describing how xhr objects obtained from this
+ factory should be used.
Instance Properties
Class goog.structs.Map.
code »<K, V>Constructor
Parameters *=...*Instance Methods
!goog.iter.IteratorParameters boolean=Returns An iterator over the values or keys in the map.
!goog.structs.MapReturns A new map with the same key-value pairs.
booleanParameters *Returns Whether the map contains the key.
booleanParameters VReturns Whether the map contains the value.
Parameters goog.structs.Mapfunction(V, V): boolean=Returns Whether the maps are equal.
undefined.Parameters *DEFAULT=Returns The value for the given key.
Returns An iterator over the keys in the map.
Returns An iterator over the values in the map.
!goog.structs.MapReturns The transposed map.
Instance Properties
Static Functions
booleanParameters **Returns Whether a and b reference the same object.
booleanParameters Object*Returns Whether the object has the key.
Class webdriver.AbstractBuilder
code »webdriver.WebDriver clients. Upon instantiation, each
+ Builder will configure itself based on the following environment variables:
+
+
webdriver.AbstractBuilder.SERVER_URL_ENVwebdriver.AbstractBuilder.prototype.usingServer.Constructor
Instance Methods
!webdriver.WebDriverwebdriver.WebDriver instance using this builder's
+ current configuration.Returns A new WebDriver client.
Returns The current desired capabilities for this
+ builder.
stringReturns The URL of the WebDriver server this instance is configured
+ to use.
!webdriver.AbstractBuilderwebdriver.AbstractBuilder.SERVER_URL_ENV
+ upon creation of this instance.Parameters stringReturns This Builder instance for chain calling.
!webdriver.AbstractBuilderParameters !(Object|webdriver.Capabilities)Returns This Builder instance for chain calling.
Instance Properties
webdriver.AbstractBuilder.SERVER_URL_ENV environment
+ variable, but may be overridden using
+ webdriver.AbstractBuilder#usingServer.Static Properties
webdriver.AbstractBuilder.SERVER_URL_ENV is not set.#usingServer(url).Class webdriver.ActionSequence
code »#perform is called.
+
+
+ new webdriver.ActionSequence(driver).
+ keyDown(webdriver.Key.SHIFT).
+ click(element1).
+ click(element2).
+ dragAndDrop(element3, element4).
+ keyUp(webdriver.Key.SHIFT).
+ perform();
+ Constructor
Parameters !webdriver.WebDriverInstance Methods
!webdriver.ActionSequencesequence.mouseMove(element).click()Parameters (webdriver.WebElement|webdriver.Button)=webdriver.Button.LEFT if neither an element nor
+ button is specified.webdriver.Button=webdriver.Button.LEFT. Ignored if a button is provided as the
+ first argument.Returns A self reference.
!webdriver.ActionSequence
+
+ sequence.mouseMove(element).doubleClick()Parameters (webdriver.WebElement|webdriver.Button)=webdriver.Button.LEFT if neither an element nor
+ button is specified.webdriver.Button=webdriver.Button.LEFT. Ignored if a button is provided as the
+ first argument.Returns A self reference.
!webdriver.ActionSequenceParameters !webdriver.WebElement(!webdriver.WebElement|{x: number, y: number})Returns A self reference.
!webdriver.ActionSequence#keyUp or #sendKeys is called. The key press will be
+ targetted at the currently focused element.Parameters !webdriver.KeyReturns A self reference.
Throws Error!webdriver.ActionSequenceParameters !webdriver.KeyReturns A self reference.
Throws Error!webdriver.ActionSequence#mouseUp is called, regardless of whether that call is made in this
+ sequence or another. The behavior for out-of-order events (e.g. mouseDown,
+ click) is undefined.
+
+
+
+ sequence.mouseMove(element).mouseDown()Parameters (webdriver.WebElement|webdriver.Button)=webdriver.Button.LEFT if neither an element nor
+ button is specified.webdriver.Button=webdriver.Button.LEFT. Ignored if a button is provided as the
+ first argument.Returns A self reference.
!webdriver.ActionSequenceParameters (!webdriver.WebElement|{x: number, y: number}){x: number, y: number}=location
+ is defined as a webdriver.WebElement, this parameter defines an
+ offset within that element. The offset should be specified in pixels
+ relative to the top-left corner of the element's bounding box. If
+ omitted, the element's center will be used as the target offset.Returns A self reference.
!webdriver.ActionSequence#mouseDown.
+
+
+
+ sequence.mouseMove(element).mouseUp()Parameters (webdriver.WebElement|webdriver.Button)=webdriver.Button.LEFT if neither an element nor
+ button is specified.webdriver.Button=webdriver.Button.LEFT. Ignored if a button is provided as the
+ first argument.Returns A self reference.
Returns A promise that will be resolved once
+ this sequence has completed.
!webdriver.ActionSequence!webdriver.ActionSequenceParameters string!webdriver.CommandName(webdriver.WebElement|webdriver.Button)=webdriver.Button.LEFT if neither an element nor
+ button is specified.webdriver.Button=webdriver.Button.LEFT. Ignored if the previous argument is
+ provided as a button.Returns A self reference.
#perform is called on
+ this instance.Parameters string!webdriver.Command!webdriver.ActionSequenceParameters ...(string|!webdriver.Key|!Array)Returns A self reference.
Throws ErrorInstance Properties
Static Functions
Parameters !webdriver.KeyThrows ErrorClass webdriver.Alert
code »webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferred
+ └ webdriver.Alertalert, confirm, or
+ prompt. Provides functions to retrieve the message displayed with
+ the alert, accept or dismiss the alert, and set the response text (in the
+ case of prompt).Constructor
Parameters !webdriver.WebDriver!(string|webdriver.promise.Promise.<string>)Instance Methods
Defined in
webdriver.Alert!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when this command has completed.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when this command has completed.
!webdriver.promise.Promise.<string>Returns A promise that will be
+ resolved to the text displayed with this alert.
!webdriver.promise.Promise.<void>Parameters stringReturns A promise that will be resolved
+ when this command has completed.
Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.AlertDefined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Properties
Class webdriver.Builder
code »webdriver.AbstractBuilder
+ └ webdriver.BuilderConstructor
Instance Methods
Defined in
webdriver.BuilderstringReturns The ID of the session, if any, this builder is configured
+ to reuse.
!webdriver.AbstractBuilderParameters stringReturns This Builder instance for chain calling.
Defined in
webdriver.AbstractBuilderReturns The current desired capabilities for this
+ builder.
stringReturns The URL of the WebDriver server this instance is configured
+ to use.
!webdriver.AbstractBuilderwebdriver.AbstractBuilder.SERVER_URL_ENV
+ upon creation of this instance.Parameters stringReturns This Builder instance for chain calling.
!webdriver.AbstractBuilderParameters !(Object|webdriver.Capabilities)Returns This Builder instance for chain calling.
Instance Properties
Defined in
webdriver.Builderwebdriver.AbstractBuilder.SESSION_ID_ENV environment variable, but
+ may be overridden using
+ webdriver.AbstractBuilder#usingSession.Defined in
webdriver.AbstractBuilderwebdriver.AbstractBuilder.SERVER_URL_ENV environment
+ variable, but may be overridden using
+ webdriver.AbstractBuilder#usingServer.Static Properties
#useExistingSession(boolean). The use of this environment
+ variable requires that webdriver.AbstractBuilder.SERVER_URL_ENV also
+ be set.Class webdriver.Capabilities
code »Constructor
Parameters (webdriver.Capabilities|Object)=Instance Methods
!webdriver.CapabilitiesParameters !(webdriver.Capabilities|Object)Returns A self reference.
!webdriver.CapabilitiesParameters string*null to unset the capability.Returns A self reference.
Instance Properties
Static Functions
Returns A basic set of capabilities for Android.
Returns A basic set of capabilities for Chrome.
Returns A basic set of capabilities for Firefox.
Returns A basic set of capabilities for HTMLUnit.
Returns A basic set of capabilities for HTMLUnit
+ with enabled Javascript.
Returns A basic set of capabilities for
+ Internet Explorer.
Returns A basic set of capabilities for iPad.
Returns A basic set of capabilities for iPhone.
Returns A basic set of capabilities for Opera.
Returns A basic set of capabilities for
+ PhantomJS.
Returns A basic set of capabilities for Safari.
Class webdriver.Command
code »Constructor
Parameters !webdriver.CommandNameInstance Methods
Returns This command's name.
*Parameters stringReturns The parameter value, or undefined if it has not been set.
!ObjectReturns The parameters to send with this command.
!webdriver.CommandParameters string*Returns A self reference.
!webdriver.CommandParameters !ObjectReturns A self reference.
Instance Properties
Class webdriver.EventEmitter
code »Constructor
Instance Methods
!webdriver.EventEmitter!webdriver.EventEmitterParameters Returns A self reference.
!webdriver.EventEmitter#addListener().!webdriver.EventEmitter!webdriver.EventEmitterParameters string=Returns A self reference.
!webdriver.EventEmitterInstance Properties
Class webdriver.FirefoxDomExecutor
code »webdriver.CommandExecutorConstructor
Enumerations
Instance Methods
Instance Properties
?{name: string, callback: !Function}Static Functions
Returns Whether the current environment supports the
+ FirefoxDomExecutor.
Class webdriver.Locator
code »Constructor
Instance Methods
Instance Properties
Static Functions
!(webdriver.Locator|Function)value is a valid locator to use for searching for
+ elements on the page.Parameters *Returns A valid locator object or function.
Throws TypeErrorfunction(string): !webdriver.Locatorwebdriver.Locator.Parameters stringReturns The new factory function.
Class webdriver.Session
code »Constructor
Parameters string!(Object|webdriver.Capabilities)Instance Methods
Returns This session's capabilities.
*Parameters stringReturns The capability value.
Instance Properties
Class webdriver.UnhandledAlertError
code »Error
+ └ bot.Error
+ └ webdriver.UnhandledAlertErrorConstructor
Parameters string!webdriver.AlertInstance Methods
Defined in
webdriver.UnhandledAlertError!webdriver.AlertReturns The open alert.
Defined in
bot.ErrorInstance Properties
Defined in
webdriver.UnhandledAlertErrorDefined in
bot.ErrorStatic Properties
Class webdriver.WebDriver
code »webdriver.promise.Promise that
+ represents the result of that command. Callbacks may be registered on this
+ object to manipulate the command result or catch an expected error. Any
+ commands scheduled with a callback are considered sub-commands and will
+ execute before the next command in the current frame. For example:
+
+ var message = [];
+ driver.call(message.push, message, 'a').then(function() {
+ driver.call(message.push, message, 'b');
+ });
+ driver.call(message.push, message, 'c');
+ driver.call(function() {
+ alert('message is abc? ' + (message.join('') == 'abc'));
+ });
+ Constructor
Parameters !(webdriver.Session|webdriver.promise.Promise)!webdriver.CommandExecutorwebdriver.promise.ControlFlow=Classes
Instance Methods
webdriver.ActionSequence#perform is
+ called. Example:
+
+ driver.actions().
+ mouseDown(element1).
+ mouseMove(element2).
+ mouseUp().
+ perform();
+ Returns A new action sequence for this instance.
Parameters function(...): (T|webdriver.promise.Promise.<T>)Object=...*Returns A promise that will be resolved'
+ with the function's result.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when this command has completed.
Returns The control flow used by this
+ instance.
arguments object.
+ Arguments may be a boolean, number, string, or webdriver.WebElement.
+ Arrays and objects may also be used as script arguments as long as each item
+ adheres to the types previously mentioned.
+
+ Unlike executing synchronous JavaScript with
+ webdriver.WebDriver.prototype.executeScript, scripts executed with
+ this function must explicitly signal they are finished by invoking the
+ provided callback. This callback will always be injected into the
+ executed function as the last argument, and thus may be referenced with
+ arguments[arguments.length - 1]. The following steps will be taken
+ for resolving this functions return value against the first argument to the
+ script's callback function:
+
+
+
+ Example #1: Performing a sleep that is synchronized with the currently
+ selected window:
+ webdriver.WebElement
+
+ Example #2: Synchronizing a test with an AJAX application:
+
+ var start = new Date().getTime();
+ driver.executeAsyncScript(
+ 'window.setTimeout(arguments[arguments.length - 1], 500);').
+ then(function() {
+ console.log('Elapsed time: ' + (new Date().getTime() - start) + ' ms');
+ });
+
+
+ Example #3: Injecting a XMLHttpRequest and waiting for the result. In this
+ example, the inject script is specified with a function literal. When using
+ this format, the function is converted to a string for injection, so it
+ should not reference any symbols not defined in the scope of the page under
+ test.
+
+ var button = driver.findElement(By.id('compose-button'));
+ button.click();
+ driver.executeAsyncScript(
+ 'var callback = arguments[arguments.length - 1];' +
+ 'mailClient.getComposeWindowWidget().onload(callback);');
+ driver.switchTo().frame('composeWidget');
+ driver.findElement(By.id('to')).sendKEys('dog@example.com');
+
+ driver.executeAsyncScript(function() {
+ var callback = arguments[arguments.length - 1];
+ var xhr = new XMLHttpRequest();
+ xhr.open("GET", "/resource/data.json", true);
+ xhr.onreadystatechange = function() {
+ if (xhr.readyState == 4) {
+ callback(xhr.resposneText);
+ }
+ }
+ xhr.send('');
+ }).then(function(str) {
+ console.log(JSON.parse(str)['food']);
+ });
+ arguments object.
+ Arguments may be a boolean, number, string, or webdriver.WebElement.
+ Arrays and objects may also be used as script arguments as long as each item
+ adheres to the types previously mentioned.
+
+ The script may refer to any variables accessible from the current window.
+ Furthermore, the script will execute in the window's context, thus
+ document may be used to refer to the current document. Any local
+ variables will not be available once the script has finished executing,
+ though global variables will persist.
+
+ If the script has a return value (i.e. if the script contains a return
+ statement), then the following steps will be taken for resolving this
+ functions return value:
+
+
webdriver.WebElement!webdriver.promise.Promise.<webdriver.WebElement>webdriver.WebElement class. This is accomplished by storing a
+ reference to the element in an object on the element's ownerDocument.
+ #executeScript will then be used to create a WebElement from this
+ reference. This requires this driver to currently be focused on the
+ ownerDocument's window+frame.Parameters !ElementReturns A promise that
+ will be fulfilled with the located element, or null if the element
+ could not be found.
!webdriver.WebElementbot.ErrorCode.NO_SUCH_ELEMENT result will be returned
+ by the driver. Unlike other commands, this error cannot be suppressed. In
+ other words, scheduling a command to find an element doubles as an assert
+ that the element is present on the page. To test whether an element is
+ present on the page, use #isElementPresent instead.
+
+ webdriver.By namespace, or as a short-hand
+ webdriver.By.Hash object. For example, the following two statements
+ are equivalent:
+
+
+
+ var e1 = driver.findElement(By.id('foo'));
+ var e2 = driver.findElement({id:'foo'});
+ webdriver.WebElement, or a
+ promise that will resolve to a WebElement. For example, to find the first
+ visible link on a page, you could write:
+
+
+
+ var link = driver.findElement(firstVisibleLink);
+
+ function firstVisibleLink(driver) {
+ var links = driver.findElements(By.tagName('a'));
+ return webdriver.promise.filter(links, function(link) {
+ return links.isDisplayed();
+ }).then(function(visibleLinks) {
+ return visibleLinks[0];
+ });
+ }
+ webdriver.WebElement reference.
+ This function may be used to generate a WebElement from a DOM element. A
+ reference to the DOM element will be stored in a known location and this
+ driver will attempt to retrieve it through #executeScript. If the
+ element cannot be found (eg, it belongs to a different document than the
+ one this instance is currently focused on), a
+ bot.ErrorCode.NO_SUCH_ELEMENT error will be returned.Parameters !(webdriver.Locator|webdriver.By.Hash|Element|Function)Returns A WebElement that can be used to issue
+ commands against the located element. If the element is not found, the
+ element will be invalidated and all scheduled commands aborted.
!webdriver.promise.PromiseParameters !Function!(webdriver.WebDriver|webdriver.WebElement)Returns A
+ promise that will resolve to a list of WebElements.
!webdriver.promise.PromiseParameters !(webdriver.Locator|webdriver.By.Hash|Function)Returns A
+ promise that will resolve to an array of WebElements.
!webdriver.promise.PromiseParameters !Function!(webdriver.WebDriver|webdriver.WebElement)Returns A
+ promise that will resolve to an array of WebElements.
!webdriver.promise.Promise.<void>Parameters stringReturns A promise that will be resolved
+ when the document has finished loading.
Returns A promise that will
+ be resolved with an array of window handles.
Returns A promise
+ that will resolve with the this instance's capabilities.
Returns A promise that will be
+ resolved with the current URL.
Returns A promise that will be
+ resolved with the current page source.
Returns A promise for this
+ client's session.
Returns A promise that will be
+ resolved with the current page's title.
Returns A promise that will be
+ resolved with the current window handle.
!webdriver.promise.Promise.<boolean>Parameters !(webdriver.Locator|webdriver.By.Hash|Element|Function)Returns A promise that will resolve
+ with whether the element is present on the page.
Returns The options interface for this
+ instance.
Returns The navigation interface for this
+ instance.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the command has completed.
webdriver.Command to be executed by this driver's
+ webdriver.CommandExecutor.Parameters !webdriver.CommandstringReturns A promise that will be resolved
+ with the command result.
!webdriver.promise.Promise.<void>Parameters numberReturns A promise that will be resolved
+ when the sleep has finished.
Returns The target locator interface for
+ this instance.
+
Returns A promise that will be
+ resolved to the screenshot as a base-64 encoded PNG.
!webdriver.promise.Promisewebdriver.promise.Promise, the
+ polling loop will wait for it to be resolved and use the resolved value for
+ evaluating whether the condition has been satisfied. The resolution time for
+ a promise is factored into whether a wait has timed out.Instance Properties
Static Functions
!webdriver.WebDriverwebdriver.Session. This may either be an existing session, or a
+ newly created one.Parameters !webdriver.CommandExecutor!webdriver.CommandstringReturns A new WebDriver client for the session.
!webdriver.WebDriverParameters !webdriver.CommandExecutorstringReturns A new client for the specified session.
!webdriver.WebDriverParameters !webdriver.CommandExecutor!webdriver.CapabilitiesReturns The driver for the newly created session.
!webdriver.promise.Promiseexecutor for execution.Parameters !webdriver.CommandExecutor!webdriver.CommandReturns A promise that will resolve with the
+ command response.
*webdriver.WebElement.ELEMENT_KEY key will be decoded to a
+ webdriver.WebElement object. All other values will be passed through
+ as is.Parameters !webdriver.WebDriverwebdriver.WebElement values.*Returns The converted value.
+
Parameters *Returns A promise that will resolve to the
+ input value's JSON representation.
Class webdriver.WebDriver.Logs
code »Constructor
Parameters !webdriver.WebDriverInstance Methods
!webdriver.promise.PromiseParameters !webdriver.logging.TypeReturns A
+ promise that will resolve to a list of log entries for the specified
+ type.
Returns A
+ promise that will resolve to a list of available log types.
Instance Properties
Class webdriver.WebDriver.Navigation
code »Constructor
Parameters !webdriver.WebDriverInstance Methods
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the navigation event has completed.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the navigation event has completed.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the navigation event has completed.
!webdriver.promise.Promise.<void>Parameters stringReturns A promise that will be resolved
+ when the URL has been loaded.
Instance Properties
Class webdriver.WebDriver.Options
code »Constructor
Parameters !webdriver.WebDriverType Definitions
Instance Methods
!webdriver.promise.Promise.<void>Parameters stringstringstring=string=boolean=(number|!Date)=Returns A promise that will be resolved
+ when the cookie has been added to the page.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when all cookies have been deleted.
!webdriver.promise.Promise.<void>Parameters stringReturns A promise that will be resolved
+ when the cookie has been deleted.
!webdriver.promise.PromiseParameters stringReturns A
+ promise that will be resolved with the named cookie, or
null
+ if there is no such cookie.Returns A promise that will be
+ resolved with the cookies visible to the current page.
Returns The interface for managing driver
+ logs.
Returns The interface for managing driver
+ timeouts.
Returns The interface for managing the
+ current window.
Instance Properties
Class webdriver.WebDriver.TargetLocator
code »Constructor
Parameters !webdriver.WebDriverInstance Methods
document.activeElement element on
+ the current document, or document.body if activeElement is not
+ available.Returns The active element.
!webdriver.Alertbot.ErrorCode.NO_MODAL_DIALOG_OPEN error if a modal
+ dialog is not currently open.Returns The open alert.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the driver has changed focus to the default content.
!webdriver.promise.Promise.<void>window.frames collection.
+
+ If the frame is specified by a string, the command will select the frame by
+ its name or ID. To select sub-frames, simply separate the frame names/IDs by
+ dots. As an example, "main.child" will select the frame with the name "main"
+ and then its child "child".
+
+ If the specified frame can not be found, the deferred result will errback
+ with a bot.ErrorCode.NO_SUCH_FRAME error.!webdriver.promise.Promise.<void>window.name attribute or
+ by its handle (as returned by webdriver.WebDriver#getWindowHandles).
+ bot.ErrorCode.NO_SUCH_WINDOW error.Parameters stringReturns A promise that will be resolved
+ when the driver has changed focus to the specified window.
Instance Properties
Class webdriver.WebDriver.Timeouts
code »Constructor
Parameters !webdriver.WebDriverInstance Methods
!webdriver.promise.Promise.<void>bot.ErrorCode.NO_SUCH_ELEMENT error. When searching
+ for multiple elements, the driver should poll the page until at least one
+ element has been found or this timeout has expired.
+
+ Setting the wait timeout to 0 (its default value), disables implicit
+ waiting.
+
+ Increasing the implicit wait timeout should be used judiciously as it
+ will have an adverse effect on test run time, especially when used with
+ slower location strategies like XPath.Parameters numberReturns A promise that will be resolved
+ when the implicit wait timeout has been set.
!webdriver.promise.Promise.<void>Parameters numberReturns A promise that will be resolved
+ when the timeout has been set.
!webdriver.promise.Promise.<void>Parameters numberReturns A promise that will be resolved
+ when the script timeout has been set.
Instance Properties
Class webdriver.WebDriver.Window
code »Constructor
Parameters !webdriver.WebDriverInstance Methods
Returns A promise that
+ will be resolved with the window's position in the form of a
+ {x:number, y:number} object literal.
Returns A
+ promise that will be resolved with the window's size in the form of a
+ {width:number, height:number} object literal.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the command has completed.
!webdriver.promise.Promise.<void>!webdriver.promise.Promise.<void>Instance Properties
Class webdriver.WebElement
code »webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferred
+ └ webdriver.WebElementwebdriver.WebDriver instance, or by searching
+ under another webdriver.WebElement:
+
+
+ The WebElement is implemented as a promise for compatibility with the promise
+ API. It will always resolve itself when its internal state has been fully
+ resolved and commands may be issued against the element. This can be used to
+ catch errors when an element cannot be located on the page:
+
+ driver.get('http://www.google.com');
+ var searchForm = driver.findElement(By.tagName('form'));
+ var searchBox = searchForm.findElement(By.name('q'));
+ searchBox.sendKeys('webdriver');
+
+ driver.findElement(By.id('not-there')).then(function(element) {
+ alert('Found an element that was not expected to be there!');
+ }, function(error) {
+ alert('The element was not found, as expected');
+ });
+ Constructor
Parameters !webdriver.WebDriver!(string|webdriver.promise.Promise)Type Definitions
Instance Methods
Defined in
webdriver.WebElement!webdriver.promise.Promise.<void>value of this element. This command
+ has no effect if the underlying DOM element is neither a text INPUT element
+ nor a TEXTAREA element.Returns A promise that will be resolved
+ when the element has been cleared.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the click command has completed.
!webdriver.WebElementbot.ErrorCode.NO_SUCH_ELEMENT result will
+ be returned by the driver. Unlike other commands, this error cannot be
+ suppressed. In other words, scheduling a command to find an element doubles
+ as an assert that the element is present on the page. To test whether an
+ element is present on the page, use #isElementPresent instead.
+
+ webdriver.By namespace, or as a short-hand
+ webdriver.By.Hash object. For example, the following two statements
+ are equivalent:
+
+
+
+ var e1 = element.findElement(By.id('foo'));
+ var e2 = element.findElement({id:'foo'});
+ webdriver.WebElement, or a
+ promise that will resolve to a WebElement. For example, to find the first
+ visible link on a page, you could write:
+
+ var link = element.findElement(firstVisibleLink);
+
+ function firstVisibleLink(element) {
+ var links = element.findElements(By.tagName('a'));
+ return webdriver.promise.filter(links, function(link) {
+ return links.isDisplayed();
+ }).then(function(visibleLinks) {
+ return visibleLinks[0];
+ });
+ }
+ Parameters !(webdriver.Locator|webdriver.By.Hash|Function)Returns A WebElement that can be used to issue
+ commands against the located element. If the element is not found, the
+ element will be invalidated and all scheduled commands aborted.
!webdriver.promise.PromiseParameters !(webdriver.Locator|webdriver.By.Hash|Function)Returns A
+ promise that will resolve to an array of WebElements.
!webdriver.promise.Promise
+
Parameters stringReturns A promise that will be
+ resolved with the attribute's value. The returned value will always be
+ either a string or null.
!webdriver.promise.Promise.<string>Parameters stringReturns A promise that will be
+ resolved with the requested CSS value.
Returns The parent driver for this instance.
Returns A promise that will be
+ resolved with the element's inner HTML.
Returns A promise that
+ will be resolved to the element's location as a
+
{x:number, y:number} object.Returns A promise that will be
+ resolved with the element's outer HTML.
Returns A
+ promise that will be resolved with the element's size as a
+
{width:number, height:number} object.Returns A promise that will be
+ resolved with the element's tag name.
!webdriver.promise.Promise.<string>Returns A promise that will be
+ resolved with the element's visible text.
Returns A promise that will be
+ resolved with whether this element is currently visible on the page.
!webdriver.promise.Promise.<boolean>Parameters !(webdriver.Locator|webdriver.By.Hash|Function)Returns A promise that will be
+ resolved with whether an element could be located on the page.
disabled attribute.Returns A promise that will be
+ resolved with whether this element is currently enabled.
Returns A promise that will be
+ resolved with whether this element is currently selected.
Parameters !webdriver.CommandstringReturns A promise that will be resolved
+ with the command result.
!webdriver.promise.Promise.<void>
+
+ Note: On browsers where native keyboard events are not yet
+ supported (e.g. Firefox on OS X), key events will be synthesized. Special
+ punctionation keys will be synthesized according to a standard QWERTY en-us
+ keyboard layout.webdriver.Key.NULL key is encountered in the sequence. When
+ this key is encountered, all modifier keys current in the down state are
+ released (with accompanying keyup events). The NULL key can be used to
+ simulate common keyboard shortcuts:
+
+ element.sendKeys("text was",
+ webdriver.Key.CONTROL, "a", webdriver.Key.NULL,
+ "now text is");
+ // Alternatively:
+ element.sendKeys("text was",
+ webdriver.Key.chord(webdriver.Key.CONTROL, "a"),
+ "now text is");
+ Parameters ...stringReturns A promise that will be resolved
+ when all keys have been typed.
!webdriver.promise.Promise.<void>Returns A promise that will be resolved
+ when the form has been submitted.
Returns A promise
+ that resolves to this element's JSON representation as defined by the
+ WebDriver wire protocol.
Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.WebElementDefined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Functions
Parameters !webdriver.WebElement!webdriver.WebElementReturns A promise that will be
+ resolved to whether the two WebElements are equal.
Static Properties
Class webdriver.http.CorsClient
code »webdriver.http.Client
+
Constructor
Parameters stringInstance Methods
Instance Properties
Static Functions
Returns Whether cross-origin resource sharing is supported.
Static Properties
Class webdriver.http.Executor
code »webdriver.CommandExecutorConstructor
Parameters !webdriver.http.ClientInstance Methods
Instance Properties
Static Functions
string!bot.response.ResponseObjectwebdriver.http.Response objects from a
+ webdriver.http.Client.Parameters !webdriver.http.ResponseReturns The parsed response.
Static Properties
Class webdriver.http.Request
code »webdriver.http.Client's responsibility to build the full URL for the
+ final request.Constructor
Instance Methods
Instance Properties
Class webdriver.http.Response
code »Constructor
Instance Methods
Instance Properties
Static Functions
webdriver.http.Response from a XMLHttpRequest or
+ XDomainRequest response object.Parameters !(XDomainRequest|XMLHttpRequest)Returns The parsed response.
Class webdriver.http.XhrClient
code »webdriver.http.ClientConstructor
Parameters stringInstance Methods
Instance Properties
Class webdriver.logging.Entry
code »Constructor
Parameters (!webdriver.logging.Level|string)stringnumber=string=Instance Methods
Instance Properties
Static Functions
!webdriver.logging.Entrygoog.debug.LogRecord into a
+ webdriver.logging.Entry.Parameters !goog.debug.LogRecordstring=Returns The converted entry.
Class webdriver.promise.CanceledTaskError_
code »Error
+ └ goog.debug.Error
+ └ webdriver.promise.CanceledTaskError_Constructor
Parameters *Static Properties
Class webdriver.promise.ControlFlow
code »webdriver.EventEmitter
+ └ webdriver.promise.ControlFlowwebdriver.promise.Promise to indicate it is an asynchronous
+ operation. The ControlFlow will wait for such promises to be resolved before
+ marking the task as completed.
+
+ webdriver.promise.Deferred
+ will be run in their own ControlFlow frame. Any tasks scheduled within a
+ frame will have priority over previously scheduled tasks. Furthermore, if
+ any of the tasks in the frame fails, the remainder of the tasks in that frame
+ will be discarded and the failure will be propagated to the user through the
+ callback/task's promised result.
+
+ webdriver.promise.ControlFlow.EventType.IDLE event. Conversely,
+ whenever the flow terminates due to an unhandled error, it will remove all
+ remaining tasks in its queue and fire an
+ webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION event. If
+ there are no listeners registered with the flow, the error will be
+ rethrown to the global error handler.Constructor
Parameters webdriver.promise.ControlFlow.Timer=Enumerations
webdriver.promise.ControlFlow.Type Definitions
Instance Methods
Defined in
webdriver.promise.ControlFlowParameters *UNCAUGHT_EXCEPTION will be emitted with the
+ offending error, otherwise, the error will be rethrown to the
+ global error handler.Parameters *!(Error|goog.testing.JsUnitException)Parameters !(Error|goog.testing.JsUnitException)Returns The annotated error.
!webdriver.promise.PromiseParameters !webdriver.promise.PromiseReturns A promise that will resolve when the
+ task has completed.
webdriver.promise.ControlFlow.EventType.IDLE event to signal
+ listeners that it has completed. During this wait, if another task is
+ scheduled, the shutdown will be aborted.Parameters function(): (T|webdriver.promise.Promise.<T>)webdriver.promise.Promise, this instance will wait for it to be
+ resolved before starting the next task.string=Returns A promise that will be resolved
+ with the result of the action.
!Array.<string>Returns A summary of this instance's recent task
+ activity.
Returns The next task to execute, or
+
null if a frame was resolved.stringReturns The scheduled tasks still pending with this instance.
Parameters !webdriver.promise.Frame_Parameters !webdriver.promise.PromisesetTimeout into the command queue. This is equivalent to
+ a thread sleep in a synchronous programming language.!webdriver.promise.PromiseParameters Returns A promise that will be resolved when the
+ condition has been satisified. The promise shall be rejected if the wait
+ times out waiting for the condition.
Defined in
webdriver.EventEmitter!webdriver.EventEmitter!webdriver.EventEmitterParameters Returns A self reference.
!webdriver.EventEmitter#addListener().!webdriver.EventEmitter!webdriver.EventEmitterParameters string=Returns A self reference.
!webdriver.EventEmitterInstance Properties
Defined in
webdriver.promise.ControlFlownull, tasks will be scheduled within the active frame. When forcing
+ a function to run in the context of a new frame, this pointer is used to
+ ensure tasks are scheduled within the newly created frame, even though it
+ won't be active yet.webdriver.promise.ControlFlow.EventType.IDLE event. Idle events
+ always follow a brief timeout in order to catch latent errors from the last
+ completed task. If this task had a callback registered, but no errback, and
+ the task fails, the unhandled failure would not be reported by the promise
+ system until the next turn of the event loop:
+
+ // Schedule 1 task that fails.
+ var result = webriver.promise.controlFlow().schedule('example',
+ function() { return webdriver.promise.rejected('failed'); });
+ // Set a callback on the result. This delays reporting the unhandled
+ // failure for 1 turn of the event loop.
+ result.then(goog.nullFunction);Defined in
webdriver.EventEmitterStatic Properties
webdriver.promise.ControlFlow#annotateError.Class webdriver.promise.Deferred.
code »<T>webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferredpromise property that may be returned to consumers for
+ registering callbacks, reserving the ability to resolve the deferred to the
+ producer.
+
+ webdriver.promise.ControlFlow as an unhandled failure.
+
+ Constructor
Parameters Function=webdriver.promise.ControlFlow=Enumerations
webdriver.promise.Deferred object may be in.Type Definitions
Instance Methods
Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Properties
Class webdriver.promise.Frame_
code »webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferred
+ └ webdriver.promise.Node_
+ └ webdriver.promise.Frame_webdriver.promise.ControlFlow. Each
+ frame represents the execution context for either a
+ webdriver.promise.Task_ or a callback on a
+ webdriver.promise.Deferred.
+
+ Constructor
Parameters !webdriver.promise.ControlFlowInstance Methods
Defined in
webdriver.promise.Frame_Parameters !(webdriver.promise.Frame_|webdriver.promise.Task_)Parameters !webdriver.promise.CanceledTaskError_Returns This frame's
+ fist child.
Returns The task currently executing
+ within this frame, if any.
Parameters !(webdriver.promise.Frame_|webdriver.promise.Task_)Parameters webdriver.promise.Task_Defined in
webdriver.promise.Node_Returns This node's parent.
Returns The root of this node's tree.
Parameters webdriver.promise.Node_Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.promise.Frame_webdriver.promise.Deferred is being invoked and any
+ tasks scheduled within it should have priority over previously scheduled
+ tasks:
+
+ var flow = webdriver.promise.controlFlow();
+ flow.execute('start here', goog.nullFunction).then(function() {
+ flow.execute('this should execute 2nd', goog.nullFunction);
+ });
+ flow.execute('this should execute last', goog.nullFunction);
+ active, any new frames which are
+ added represent callbacks on a webdriver.promise.Deferred, whose
+ tasks must be given priority over previously scheduled tasks.Defined in
webdriver.promise.Node_Defined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Properties
Class webdriver.promise.Node_
code »webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferred
+ └ webdriver.promise.Node_webdriver.promise.ControlFlow's task tree.Constructor
Parameters !webdriver.promise.ControlFlowInstance Methods
Defined in
webdriver.promise.Node_Returns This node's parent.
Returns The root of this node's tree.
Parameters webdriver.promise.Node_Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.promise.Node_Defined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Properties
Class webdriver.promise.Promise.
code »<T>Constructor
Instance Methods
Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Class webdriver.promise.Task_
code »webdriver.promise.Promise.<(T|null)>
+ └ webdriver.promise.Deferred
+ └ webdriver.promise.Node_
+ └ webdriver.promise.Task_webdriver.promise.ControlFlow.Constructor
Parameters !webdriver.promise.ControlFlow!Functionwebdriver.promise.Promise, the flow will wait
+ for it to be resolved before starting the next task.string!webdriver.stacktrace.SnapshotInstance Methods
Defined in
webdriver.promise.Task_stringReturns This task's description.
Defined in
webdriver.promise.Node_Returns This node's parent.
Returns The root of this node's tree.
Parameters webdriver.promise.Node_Defined in
webdriver.promise.DeferredDefined in
webdriver.promise.Promise.<(T|null)>Parameters ?(function(T): (R|webdriver.promise.Promise.<R>))=?(function(*): (R|webdriver.promise.Promise.<R>))=Returns A new promise which will be
+ resolved with the result of the invoked callback.
catch clause in a synchronous API:
+
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } catch (ex) {
+ console.error(ex);
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenCatch(function(ex) {
+ console.error(ex);
+ });
+ Parameters function(*): (R|webdriver.promise.Promise.<R>)Returns A new promise which will be
+ resolved with the result of the invoked callback.
finally clause in a synchronous API:
+
+
+ Note: similar to the
+ // Synchronous API:
+ try {
+ doSynchronousWork();
+ } finally {
+ cleanUp();
+ }
+
+ // Asynchronous promise API:
+ doAsynchronousWork().thenFinally(cleanUp);
+ finally clause, if the registered
+ callback returns a rejected promise or throws an error, it will silently
+ replace the rejection error (if any) from this promise:
+
+ try {
+ throw Error('one');
+ } finally {
+ throw Error('two'); // Hides Error: one
+ }
+
+ webdriver.promise.rejected(Error('one'))
+ .thenFinally(function() {
+ throw Error('two'); // Hides Error: one
+ });
+ Parameters function(): (R|webdriver.promise.Promise.<R>)Returns A promise that will be fulfilled
+ with the callback result.
Instance Properties
Defined in
webdriver.promise.Task_Defined in
webdriver.promise.Node_Defined in
webdriver.promise.Deferredwebdriver.promise.Promise.Static Properties
Class webdriver.stacktrace.Frame
code »Constructor
Parameters (string|undefined)(string|undefined)(string|undefined)a.b = function c() {};.(string|undefined)Instance Methods
booleanReturns Whether the stack frame contains an anonymous function.
Instance Properties
Class webdriver.stacktrace.Snapshot
code »Constructor
Parameters number=Instance Methods
!ArrayReturns The parsed stack trace.
Instance Properties
Class webdriver.testing.Assertion
code »value. If the
+ value is a webdriver.promise.Promise, this assertion will wait
+ for it to resolve before applying any matchers.Constructor
Parameters *Classes
Instance Methods
webdriver.promise.Promisematcher accepts the value wrapped by this
+ instance. If the wrapped value is a promise, this function will defer
+ applying the assertion until the value has been resolved. Otherwise, it
+ will be applied immediately.Parameters !goog.labs.testing.Matcherstring=Returns The deferred assertion result, or
+
null if the assertion was immediately applied.webdriver.promise.Promisewebdriver.promise.PromiseParameters *string=Returns The assertion result.
webdriver.promise.Promisewebdriver.promise.Promisevalue.Parameters *string=Returns The assertion result.
webdriver.promise.Promisevalue.webdriver.promise.Promisewebdriver.promise.PromiseReturns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
Returns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
webdriver.promise.Promisewebdriver.promise.Promisewebdriver.promise.Promisewebdriver.promise.PromiseInstance Properties
Class webdriver.testing.Assertion.DelegatingMatcher_
code »goog.labs.testing.MatcherConstructor
Instance Methods
Class webdriver.testing.ContainsMatcher
code »goog.labs.testing.Matchervalue.Constructor
Parameters *Instance Methods
Instance Properties
Class webdriver.testing.NegatedAssertion
code »webdriver.testing.Assertion
+ └ webdriver.testing.NegatedAssertionConstructor
Parameters *Instance Methods
Defined in
webdriver.testing.NegatedAssertionDefined in
webdriver.testing.Assertionwebdriver.promise.Promisewebdriver.promise.PromiseParameters *string=Returns The assertion result.
webdriver.promise.Promisewebdriver.promise.Promisevalue.Parameters *string=Returns The assertion result.
webdriver.promise.Promisevalue.webdriver.promise.Promisewebdriver.promise.PromiseReturns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
Returns The assertion result.
webdriver.promise.PromiseParameters string=Returns The assertion result.
webdriver.promise.Promisewebdriver.promise.Promisewebdriver.promise.Promisewebdriver.promise.PromiseInstance Properties
Defined in
webdriver.testing.NegatedAssertionDefined in
webdriver.testing.AssertionStatic Properties
q[2]?1:0)}while(0==b)}b=Va[a]=0<=b}return b}var Wa=m.document,H=Wa&&D?Oa()||("CSS1Compat"==Wa.compatMode?parseInt(Pa,10):5):void 0;var Xa=!D||D&&9<=H;!E&&!D||D&&D&&9<=H||E&&G("1.9.1");D&&G("9");var Ya="H3";function Za(a,b){var c;c=a.className;c=s(c)&&c.match(/\S+/g)||[];for(var d=Da(arguments,1),e=c.length+d.length,f=c,g=0;g
",c=a.types,d=c.length,e=0;e
"}return b}
+function Cb(a){var b,c=0":"";for(var c=a.file.children,d=c.length,e=0;e
"};var Db=!D||D&&9<=H,Eb=D&&!G("9");!F||G("528");E&&G("1.9b")||D&&G("8")||C&&G("9.5")||F&&G("528");E&&!G("8")||D&&G("9");function O(){0!=Fb&&(Gb[ga(this)]=this)}var Fb=0,Gb={};O.prototype.Oa=!1;O.prototype.B=function(){if(!this.Oa&&(this.Oa=!0,this.e(),0!=Fb)){var a=ga(this);delete Gb[a]}};O.prototype.e=function(){if(this.bb)for(;this.bb.length;)this.bb.shift()()};function Hb(a){a&&"function"==typeof a.B&&a.B()};function P(a,b){this.type=a;this.currentTarget=this.target=b}h=P.prototype;h.e=function(){};h.B=function(){};h.G=!1;h.defaultPrevented=!1;h.nb=!0;h.stopPropagation=function(){this.G=!0};h.preventDefault=function(){this.defaultPrevented=!0;this.nb=!1};var Ib="change";function Jb(a){Jb[" "](a);return a}Jb[" "]=aa;function Q(a,b){a&&Kb(this,a,b)}x(Q,P);h=Q.prototype;h.target=null;h.relatedTarget=null;h.offsetX=0;h.offsetY=0;h.clientX=0;h.clientY=0;h.screenX=0;h.screenY=0;h.button=0;h.keyCode=0;h.charCode=0;h.ctrlKey=!1;h.altKey=!1;h.shiftKey=!1;h.metaKey=!1;h.O=null;
+function Kb(a,b,c){var d=a.type=b.type;P.call(a,d);a.target=b.target||b.srcElement;a.currentTarget=c;if(c=b.relatedTarget){if(E){var e;a:{try{Jb(c.nodeName);e=!0;break a}catch(f){}e=!1}e||(c=null)}}else"mouseover"==d?c=b.fromElement:"mouseout"==d&&(c=b.toElement);a.relatedTarget=c;a.offsetX=F||void 0!==b.offsetX?b.offsetX:b.layerX;a.offsetY=F||void 0!==b.offsetY?b.offsetY:b.layerY;a.clientX=void 0!==b.clientX?b.clientX:b.pageX;a.clientY=void 0!==b.clientY?b.clientY:b.pageY;a.screenX=b.screenX||0;
+a.screenY=b.screenY||0;a.button=b.button;a.keyCode=b.keyCode||0;a.charCode=b.charCode||("keypress"==d?b.keyCode:0);a.ctrlKey=b.ctrlKey;a.altKey=b.altKey;a.shiftKey=b.shiftKey;a.metaKey=b.metaKey;a.state=b.state;a.O=b;b.defaultPrevented&&a.preventDefault();delete a.G}h.stopPropagation=function(){Q.i.stopPropagation.call(this);this.O.stopPropagation?this.O.stopPropagation():this.O.cancelBubble=!0};
+h.preventDefault=function(){Q.i.preventDefault.call(this);var a=this.O;if(a.preventDefault)a.preventDefault();else if(a.returnValue=!1,Eb)try{if(a.ctrlKey||112<=a.keyCode&&123>=a.keyCode)a.keyCode=-1}catch(b){}};h.e=function(){};var Lb="closure_listenable_"+(1E6*Math.random()|0);function Mb(a){try{return!(!a||!a[Lb])}catch(b){return!1}}var Nb=0;function Ob(a,b,c,d,e){this.K=a;this.ma=null;this.src=b;this.type=c;this.capture=!!d;this.ga=e;this.key=++Nb;this.U=this.ea=!1}function Pb(a){a.U=!0;a.K=null;a.ma=null;a.src=null;a.ga=null};function Qb(a){this.src=a;this.l={};this.da=0}Qb.prototype.add=function(a,b,c,d,e){var f=this.l[a];f||(f=this.l[a]=[],this.da++);var g=Rb(f,b,d,e);-1Enum bot.ErrorCode
code »numberValues and Descriptions
Enum bot.Error.State
code »stringValues and Descriptions
Enum goog.dom.NodeType
code »numberValues and Descriptions
Enum goog.net.XmlHttp.OptionType
code »numberValues and Descriptions
Enum goog.net.XmlHttp.ReadyState
code »numberValues and Descriptions
Enum goog.string.Unicode
code »stringValues and Descriptions
Enum goog.uri.utils.CharCode_
code »numberValues and Descriptions
Enum goog.uri.utils.ComponentIndex
code »numberValues and Descriptions
Enum goog.uri.utils.StandardQueryParam
code »stringValues and Descriptions
Enum webdriver.Button
code »numberValues and Descriptions
Enum webdriver.Capability
code »stringValues and Descriptions
webdriver.Capability.SECURE_SSL.webdriver.Browser enum.webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR.webdriver.Capability.ACCEPT_SSL_CERTS.Enum webdriver.CommandName
code »stringValues and Descriptions
Enum webdriver.FirefoxDomExecutor.Attribute_
code »stringValues and Descriptions
Enum webdriver.FirefoxDomExecutor.EventType_
code »stringValues and Descriptions
Enum webdriver.Key
code »stringValues and Descriptions
Global Functions
stringwebdriver.Keys or strings, appends each of the values to a string,
+ and adds the chord termination key (webdriver.Key.NULL) and returns
+ the resultant string.
+
+ Note: when the low-level webdriver key handlers see Keys.NULL, active
+ modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event.Parameters ...stringReturns The null-terminated key sequence.
Enum webdriver.logging.Level
code »{value: number, name: webdriver.logging.LevelName}Values and Descriptions
Enum webdriver.logging.LevelName
code »stringValues and Descriptions
Enum webdriver.logging.Type
code »stringValues and Descriptions
Enum webdriver.promise.ControlFlow.EventType
code »stringwebdriver.promise.ControlFlow.Values and Descriptions
Enum webdriver.promise.Deferred.State_
code »numberwebdriver.promise.Deferred object may be in.Values and Descriptions
selenium-webdriver
+Installation
+npm:
+npm install selenium-webdriver
+selenium-webdriver
+natively supports the ChromeDriver.
+Simply download a copy and make sure it can be found on your PATH. The other
+drivers (e.g. Firefox, Internet Explorer, and Safari), still require the
+standalone Selenium server.Running the tests
+PATH.
+npm test selenium-webdriver
+SELENIUM_SERVER_JAR environment variable.
+You can use the SELENIUM_BROWSER environment variable to define a
+comma-separated list of browsers you wish to test against. For example:
+export SELENIUM_SERVER_JAR=path/to/selenium-server-standalone-2.33.0.jar
+SELENIUM_BROWSER=chrome,firefox npm test selenium-webdriver
+Usage
+
+var webdriver = require('selenium-webdriver');
+
+var driver = new webdriver.Builder().
+ withCapabilities(webdriver.Capabilities.chrome()).
+ build();
+
+driver.get('http://www.google.com');
+driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
+driver.findElement(webdriver.By.name('btnG')).click();
+driver.wait(function() {
+ return driver.getTitle().then(function(title) {
+ return title === 'webdriver - Google Search';
+ });
+}, 1000);
+
+driver.quit();
+Documentation
+Issues
+License
+
+ http://www.apache.org/licenses/LICENSE-2.0
+Interface goog.labs.testing.Matcher
code »Instance Methods
Global Functions
!FunctionInterface goog.net.XhrLike
code »Type Definitions
Instance Methods
stringParameters Parameters Instance Properties
(function()|null|undefined)Interface webdriver.CommandExecutor
code »webdriver.Command objects.Instance Methods
command. If there is an error executing the
+ command, the provided callback will be invoked with the offending error.
+ Otherwise, the callback will be invoked with a null Error and non-null
+ bot.response.ResponseObject object.Parameters !webdriver.Commandfunction(Error, !bot.response.ResponseObject==)Interface webdriver.http.Client
code »Instance Methods
Error describing the error. Otherwise, when
+ the server's response has been received, the callback will be invoked with a
+ null Error and non-null webdriver.http.Response object.Parameters !webdriver.http.Requestfunction(Error, !webdriver.http.Response==)License
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2007-2009 Google Inc.
+ Copyright 2007-2009 WebDriver committers
+
+ 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.
+
+ Module selenium-webdriver
code »Classes
WebDriver instances.Enumerations
Properties
webdriver.Locator
+ instances.Module selenium-webdriver/_base
code »require function; all required non-native modules must be specified
+ relative to this module.
+
+ Functions
!ObjectProperties
Class Builder
code »webdriver.AbstractBuilder
+ └ BuilderWebDriver instances.Constructor
Instance Methods
Defined in
Builder!BuilderParameters !chrome.OptionsReturns A self reference.
#withCapabilities after this function will
+ overwrite these settings.Parameters !proxy.ProxyConfigReturns A self reference.
Defined in
webdriver.AbstractBuilderReturns The current desired capabilities for this
+ builder.
stringReturns The URL of the WebDriver server this instance is configured
+ to use.
!webdriver.AbstractBuilderwebdriver.AbstractBuilder.SERVER_URL_ENV
+ upon creation of this instance.Parameters stringReturns This Builder instance for chain calling.
!webdriver.AbstractBuilderParameters !(Object|webdriver.Capabilities)Returns This Builder instance for chain calling.
Instance Properties
Defined in
webdriver.AbstractBuilderwebdriver.AbstractBuilder.SERVER_URL_ENV environment
+ variable, but may be overridden using
+ webdriver.AbstractBuilder#usingServer.Module selenium-webdriver/chrome
code »Classes
remote.DriverService instances that manage a ChromeDriver
+ server.Functions
!webdriver.WebDriverParameters (webdriver.Capabilities|Options)=remote.DriverService=default service by default.Returns A new WebDriver instance.
!remote.DriverServiceReturns The default ChromeDriver service.
Parameters !remote.DriverServiceThrows ErrorClass Options
code »Constructor
Instance Methods
!Options!Options!Optionswebdriver.WebDriver#quit() is
+ called.Parameters booleanReturns A self reference.
!OptionsParameters stringReturns A self reference.
!OptionsParameters stringReturns A self reference.
!OptionsParameters !ObjectReturns A self reference.
!OptionsParameters !webdriver.logging.PreferencesReturns A self reference.
!OptionsParameters !ObjectReturns A self reference.
!webdriver.Capabilitieswebdriver.Capabilities object.Parameters webdriver.Capabilities=Returns The capabilities.
{args: !Array.<string>, binary: (string|undefined), detach: boolean, extensions: !Array.<string>, localState: (Object|undefined), logFile: (string|undefined), prefs: (Object|undefined)}Returns The JSON wire protocol representation
+ of this instance.
Instance Properties
Static Functions
!OptionsParameters !webdriver.CapabilitiesReturns The ChromeDriver options.
Class ServiceBuilder
code »remote.DriverService instances that manage a ChromeDriver
+ server.Constructor
Instance Methods
!ServiceBuilderReturns A self reference.
!ServiceBuilderParameters numberReturns A self reference.
!ServiceBuilderParameters stringReturns A self reference.
!ServiceBuilderInstance Properties
Module selenium-webdriver/error
code »Classes
Enumerations
Module selenium-webdriver/executors
code »webdriver.CommandExecutor implementations.Functions
!webdriver.CommandExecutorParameters (string|!webdriver.promise.Promise.<string>)Module selenium-webdriver/http
code »webdriver.http.Client for use with
+ NodeJS.Classes
webdriver.http.Client implementation using Node's built-in http
+ module.Class HttpClient
code »webdriver.http.Clientwebdriver.http.Client implementation using Node's built-in http
+ module.Constructor
Parameters stringInstance Methods
Instance Properties
Module selenium-webdriver/http/util
code »Functions
!webdriver.promise.PromiseParameters stringReturns A promise that resolves with
+ a hash of the server status.
!webdriver.promise.Promise!webdriver.promise.PromiseModule selenium-webdriver/io
code »Functions
?stringPATH environment variable for the given file.Module selenium-webdriver/net
code »Functions
stringParameters string=Returns The IP address or undefined if not available.
stringParameters string=Returns The IP address or undefined if not available.
Module selenium-webdriver/net/portprober
code »Functions
!webdriver.promise.Promise.<number>Parameters string=port against.
+ Defaults to INADDR_ANY.Returns A promise that will resolve
+ to a free port. If a port cannot be found, the promise will be
+ rejected.
Module selenium-webdriver/phantomjs
code »Functions
!webdriver.WebDriverParameters webdriver.Capabilities=Returns A new WebDriver instance.
Module selenium-webdriver/proxy
code »
+ var webdriver = require('selenium-webdriver'),
+ proxy = require('selenium-webdriver/proxy');
+
+ var driver = new webdriver.Builder()
+ .withCapabilities(webdriver.Capabilities.chrome())
+ .setProxy(proxy.manual({http: 'host:1234'}))
+ .build();
+ Type Definitions
Functions
+
+
+ Behavior is undefined for FTP, HTTP, and HTTPS requests if the
+ corresponding key is omitted from the configuration options.ftp: Proxy host to use for FTP requests
+ http: Proxy host to use for HTTP requests
+ https: Proxy host to use for HTTPS requests
+ bypass: A list of hosts requests should directly connect to,
+ bypassing any other proxies for that request. May be specified as a
+ comma separated string, or a list of strings.
+ Module selenium-webdriver/remote
code »Classes
Type Definitions
{port: (number|!webdriver.promise.Promise.<number>), args: !(Array.<string>|webdriver.promise.Promise), path: (string|undefined), env: (!Object.<string, string>|undefined), stdio: (string|!Array|undefined)}
+
loopback - Whether the service should only be accessed on this
+ host's loopback address.
+ port - The port to start the server on (must be > 0). If the
+ port is provided as a promise, the service will wait for the promise to
+ resolve before starting.
+ args - The arguments to pass to the service. If a promise is
+ provided, the service will wait for it to resolve before starting.
+ path - The base path on the server for the WebDriver wire
+ protocol (e.g. '/wd/hub'). Defaults to '/'.
+ env - The environment variables that should be visible to the
+ server process. Defaults to inheriting the current process's
+ environment.
+ stdio - IO configuration for the spawned server process. For
+ more information, refer to the documentation of
+ child_process.spawn.
+ Class DriverService
code »Constructor
Parameters string!ServiceOptionsInstance Methods
!webdriver.promise.Promise.<string>Returns A promise that resolves to
+ the server's address.
Throws Error#stop().Returns A promise that will be resolved when
+ the server has been stopped.
!webdriver.promise.Promise.<string>Parameters number=Returns A promise that will resolve
+ to the server's base URL when it has started accepting requests. If the
+ timeout expires before the server has started, the promise will be
+ rejected.
Returns A promise that will be resolved when
+ the server has been stopped.
Instance Properties
Static Properties
Class SeleniumServer
code »DriverService
+ └ SeleniumServerConstructor
Type Definitions
{port: (number|!webdriver.promise.Promise.<number>), args: !(Array.<string>|webdriver.promise.Promise), jvmArgs: (!Array.<string>|!webdriver.promise.Promise|undefined), env: (!Object.<string, string>|undefined), stdio: (string|!Array|undefined)}
+
port - The port to start the server on (must be > 0). If the
+ port is provided as a promise, the service will wait for the promise to
+ resolve before starting.
+ args - The arguments to pass to the service. If a promise is
+ provided, the service will wait for it to resolve before starting.
+ jvmArgs - The arguments to pass to the JVM. If a promise is
+ provided, the service will wait for it to resolve before starting.
+ env - The environment variables that should be visible to the
+ server process. Defaults to inheriting the current process's
+ environment.
+ stdio - IO configuration for the spawned server process. For
+ more information, refer to the documentation of
+ child_process.spawn.
+ Instance Methods
!webdriver.promise.Promise.<string>Returns A promise that resolves to
+ the server's address.
Throws Error#stop().Returns A promise that will be resolved when
+ the server has been stopped.
!webdriver.promise.Promise.<string>Parameters number=Returns A promise that will resolve
+ to the server's base URL when it has started accepting requests. If the
+ timeout expires before the server has started, the promise will be
+ rejected.
Returns A promise that will be resolved when
+ the server has been stopped.
Instance Properties
Static Properties
Module selenium-webdriver/testing
code »
+
+
+ webdriver.promise.ControlFlow
+ to simplify writing asynchronous tests:
+
+
+
+ var webdriver = require('selenium-webdriver'),
+ portprober = require('selenium-webdriver/net/portprober'),
+ remote = require('selenium-webdriver/remote'),
+ test = require('selenium-webdriver/testing');
+
+ test.describe('Google Search', function() {
+ var driver, server;
+
+ test.before(function() {
+ server = new remote.SeleniumServer(
+ 'path/to/selenium-server-standalone.jar',
+ {port: portprober.findFreePort()});
+ server.start();
+
+ driver = new webdriver.Builder().
+ withCapabilities({'browserName': 'firefox'}).
+ usingServer(server.address()).
+ build();
+ });
+
+ test.after(function() {
+ driver.quit();
+ server.stop();
+ });
+
+ test.it('should append query to title', function() {
+ driver.get('http://www.google.com');
+ driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
+ driver.findElement(webdriver.By.name('btnG')).click();
+ driver.wait(function() {
+ return driver.getTitle().then(function(title) {
+ return 'webdriver - Google Search' === title;
+ });
+ }, 1000, 'Waiting for title to update');
+ });
+ });
+
+ test.ignore(maybe()).it('is flaky', function() {
+ if (Math.random() < 0.5) throw Error();
+ });
+
+ function maybe() { return Math.random() < 0.5; }
+ Functions
Parameters function()Parameters function(): booleanReturns An object with wrapped versions of
#it() and
+ #describe() that ignore tests as indicated by the predicate.Module selenium-webdriver/testing/assert
code »
+
+
+ Sample usage:
+
+ NOTE: This module is considered experimental and is subject to
+ change, or removal, at any time!
+
+
+ var driver = new webdriver.Builder().build();
+ driver.get('http://www.google.com');
+
+ assert(driver.getTitle()).equalTo('Google');
+ Main
!webdriver.testing.AssertionParameters *Returns The new assertion.
Functions
webdriver.testing.Assertion prototype.Parameters string(function(new: goog.labs.testing.Matcher, *)|{matches: function(*): boolean, describe: function(): string})Namespace bot
code »Classes
Enumerations
Namespace bot.json
code »Global Functions
*stringParameters *?(function(string, *): *)=Returns A JSON string representation of the input object.
Global Properties
Compiler Constants
Namespace bot.response
code »Type Definitions
Global Functions
!bot.response.ResponseObjectParameters !bot.response.ResponseObjectReturns The checked response object.
Throws bot.ErrorParameters *Returns The new response object.
booleanParameters *Returns Whether the given value is a response object.
Namespace bot.userAgent
code »Global Functions
boolean
+
boolean
+
Global Properties
(undefined|function((string|number)): boolean)(undefined|function((string|number)): boolean)Namespace goog
code »Classes
Global Functions
Throws ErrorgetInstance static method that always returns the same
+ instance object.Parameters !FunctionParameters !Object*=...*Returns The return value of the superclass method.
#partial.
+
+ Usage:
+ var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2');
+ barMethBound('arg3', 'arg4');Parameters ?function(this: T, ...)T...*Returns A partially-applied form of the function bind() was
+ invoked as a method of.
!FunctionParameters Returns A partially-applied form of the function bind() was
+ invoked as a method of.
!FunctionParameters Returns A partially-applied form of the function bind() was
+ invoked as a method of.
*goog.cloneObject does not detect reference loops. Objects that
+ refer to themselves will cause infinite recursion.
+
+ goog.cloneObject is unaware of unique identifiers, and copies
+ UIDs created by getUid into cloned results.Parameters *Returns A clone of the input value.
Parameters stringParameters stringstringnumberParameters ObjectReturns The hash code for the object.
string
+ var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'});
+ stringvar x = goog.getMsgWithFallback(MSG_A, MSG_B);
+ where MSG_A and MSG_B were initialized with goog.getMsg.?stringParameters stringReturns Url corresponding to the rule, or null.
numbergetUid. There is no guarantee that the ID is unique or
+ consistent across sessions. It is unsafe to generate unique ID for function
+ prototypes.Parameters ObjectReturns The unique ID for the object.
Parameters stringbooleanParameters ObjectReturns Whether there an assigned unique id for the object.
Parameters *=...*Returns The first argument. We can't know the type -- just pass it along
+ without type.
Parameters stringReturns True if it looks like HTML document.
+ function ParentClass(a, b) { }
+ ParentClass.prototype.foo = function(a) { }
+
+ function ChildClass(a, b, c) {
+ goog.base(this, a, b);
+ }
+ goog.inherits(ChildClass, ParentClass);
+
+ var child = new ChildClass('a', 'b', 'see');
+ child.foo(); // This works.
+
+
+ In addition, a superclass' implementation of a method can be invoked as
+ follows:
+
+
+ ChildClass.prototype.foo = function(a) {
+ ChildClass.superClass_.foo.call(this, a);
+ // Other code here.
+ };
+ booleanParameters ?Returns Whether variable is an array.
booleanParameters ?Returns Whether variable is an array.
booleanParameters ?Returns Whether variable is boolean.
booleanParameters ?Returns Whether variable is a like a Date.
booleanParameters ?Returns Whether variable is defined.
booleanParameters ?Returns Whether variable is defined and not null.
booleanParameters ?Returns Whether variable is a function.
booleanParameters ?Returns Whether variable is null.
booleanParameters ?Returns Whether variable is a number.
booleanParameters ?Returns Whether variable is an object.
booleanParameters stringReturns Whether the name has been provided.
booleanParameters ?Returns Whether variable is a string.
voidReturns Nothing.
!FunctionParameters Function...*Returns A partially-applied form of the function bind() was
+ invoked as a method of.
Parameters stringParameters Objectgoog.getUid in which case the mutation is
+ undone.Parameters ObjectParameters stringParameters function()
+ goog.setCssNameMapping({
+ "goog": "a",
+ "disabled": "b",
+ });
+
+ var x = goog.getCssName('goog');
+ // The following evaluates to: "a a-b".
+ goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled')
+
+ When declared as a map of string literals to string literals, the JSCompiler
+ will replace all calls to goog.getCssName() using the supplied map if the
+ --closure_pass flag is set.Parameters string=stringParameters *Returns The name of the type.
booleanParameters stringReturns True if the script was imported, false otherwise.
Global Properties
global thisgoog.testing.singleton module. The compiler
+ removes this variable if unused.Compiler Constants
Namespace goog.array
code »Type Definitions
Global Functions
Parameters (Array.<VALUE>|goog.array.ArrayLike)VALUEfunction(VALUE, VALUE): number=Returns True if an element was inserted.
Parameters (!Array.<VALUE>|!goog.array.ArrayLike)VALUEfunction(VALUE, VALUE): number=Returns True if an element was removed.
goog.array.defaultCompare, which compares the elements
+ using the built in < and > operators. This will produce the expected
+ behavior for homogeneous arrays of String(s) and Number(s). The array
+ specified must be sorted in ascending order (as defined by the
+ comparison function). If the array is not sorted, results are undefined.
+ If the array contains multiple instances of the specified target value, any
+ of these instances may be found.
+
+ Runtime: O(log n)Parameters (Array.<VALUE>|goog.array.ArrayLike)TARGETfunction(TARGET, VALUE): number=Returns Lowest index of the target value if found, otherwise
+ (-(insertion point) - 1). The insertion point is where the value should
+ be inserted into arr to preserve the sorted property. Return value >= 0
+ iff target is found.
<THIS, VALUE, TARGET> goog.array.binarySearch_ ( arr, compareFn, isEvaluator, opt_target, opt_selfObj ) ⇒ numberParameters (Array.<VALUE>|goog.array.ArrayLike)(function(TARGET, VALUE): number|function(this: THIS, VALUE, number, ?): number)booleanTARGET=THIS=Returns Lowest index of the target value if found, otherwise
+ (-(insertion point) - 1). The insertion point is where the value should
+ be inserted into arr to preserve the sorted property. Return value >= 0
+ iff target is found.
Parameters (Array.<VALUE>|goog.array.ArrayLike)function(this: THIS, VALUE, number, ?): numberTHIS=Returns Index of the leftmost element matched by the evaluator, if
+ such exists; otherwise (-(insertion point) - 1). The insertion point is
+ the index of the first element for which the evaluator returns negative,
+ or arr.length if no such element exists. The return value is non-negative
+ iff a match is found.
Parameters Array.<T>function(this: S, T, number, Array.<T>): ?S=Returns An object, with keys being all of the unique return values
+ of sorter, and values being arrays containing the items for
+ which the splitter returned that key.
Parameters goog.array.ArrayLikeParameters (Array.<T>|goog.array.ArrayLike)Returns Clone of the input array.
Parameters (!Array.<VALUE>|!goog.array.ArrayLike)(!Array.<VALUE>|!goog.array.ArrayLike)function(VALUE, VALUE): number=Returns Negative number, zero, or a positive number depending on
+ whether the first argument is less than, equal to, or greater than the
+ second.
!ArrayParameters ...*Returns The new resultant array.
booleanParameters goog.array.ArrayLike*Returns true if obj is present.
Parameters !(Array.<T>|goog.array.ArrayLike)function(this: S, T, number, ?): booleanS=Returns The number of the matching elements.
Parameters VALUEVALUEReturns A negative number, zero, or a positive number as the first
+ argument is less than, equal to, or greater than the second.
booleanParameters **Returns True if the two arguments are equal, false otherwise.
booleanParameters goog.array.ArrayLikegoog.array.ArrayLikeFunction=goog.array.defaultCompareEquality which
+ compares the elements using the built-in '===' operator.Returns Whether the two arrays are equal.
http://tinyurl.com/developer-mozilla-org-array-everyParameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns false if any element fails the test.
http://tinyurl.com/developer-mozilla-org-array-filterParameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns a new array in which only elements that passed the test
+ are present.
Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns The first array element that passes the test, or null if no
+ element is found.
Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns The index of the first array element that passes the test,
+ or -1 if no element is found.
Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanObject=Returns The index of the last array element that passes the test,
+ or -1 if no element is found.
Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns The last array element that passes the test, or null if no
+ element is found.
!ArrayParameters ...*Returns An array containing the flattened values.
http://tinyurl.com/developer-mozilla-org-array-foreachParameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): ?S=Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): ?S=http://tinyurl.com/developer-mozilla-org-array-indexofParameters (Array.<T>|goog.array.ArrayLike)Tnumber=Returns The index of the first matching array element.
Parameters Array.<T>TParameters goog.array.ArrayLikegoog.array.ArrayLikenumber=Parameters goog.array.ArrayLike*number=Parameters Array.<T>TT=booleanParameters goog.array.ArrayLikeReturns true if empty.
Parameters !Array.<T>?function(T, T): number=boolean=Returns Whether the array is sorted.
Parameters Parameters (Array.<T>|goog.array.ArrayLike)Returns Last item in array.
http://tinyurl.com/developer-mozilla-org-array-lastindexofParameters (!Array.<T>|!goog.array.ArrayLike)T?number=Returns The index of the last matching array element.
http://tinyurl.com/developer-mozilla-org-array-mapParameters (Array.<VALUE>|goog.array.ArrayLike)function(this: THIS, VALUE, number, ?): RESULTTHIS=Returns a new array with the results from f.
Parameters (Array.<T>|goog.array.ArrayLike)Returns Last item in array.
!Array.<number>
+ range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4]
+ range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4]
+ range(-2, -5, -1) produces [-2, -3, -4]
+ range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5.
+
Parameters Returns An array of numbers for the requested range. May be
+ an empty array if adding the step would not converge toward the end
+ value.
http://tinyurl.com/developer-mozilla-org-array-reduce
+
+ For example:
+ var a = [1, 2, 3, 4];
+ goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0);
+ returns 10Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, R, T, number, ?): R?S=Returns Result of evaluating f repeatedly across the values of the array.
http://tinyurl.com/developer-mozilla-org-array-reduceright
+
+ For example:
+ var a = ['a', 'b', 'c'];
+ goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, '');
+ returns 'cba'Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, R, T, number, ?): R?S=Returns Object returned as a result of evaluating f repeatedly across the
+ values of the array.
Parameters (Array.<T>|goog.array.ArrayLike)TReturns True if an element was removed.
booleanParameters goog.array.ArrayLikenumberReturns True if an element was removed.
goog.getUid.
+
+ Alternatively you can specify a custom hash function that returns a unique
+ value for each item in the array it should consider unique.
+
+ Runtime: N,
+ Worstcase space: 2N (no dupes)Parameters (Array.<T>|goog.array.ArrayLike)Array=function(T): string=Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns True if an element was removed.
Parameters VALUEnumberReturns An array with the repeated value.
Parameters (Array.<T>|goog.array.ArrayLike)numbernumber=Returns A new array containing the specified segment of the
+ original array.
http://tinyurl.com/developer-mozilla-org-array-someParameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): booleanS=Returns true if any element passes the test.
goog.array.defaultCompare, which compares the elements using
+ the built in < and > operators. This will produce the expected behavior
+ for homogeneous arrays of String(s) and Number(s), unlike the native sort,
+ but will give unpredictable results for heterogenous lists of strings and
+ numbers with different numbers of digits.
+
+ This sort is not guaranteed to be stable.
+
+ Runtime: Same as Array.prototype.sortParameters Array.<T>?function(T, T): number=goog.array.defaultCompare.
+ This won't work for keys that get renamed by the compiler. So use
+ {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.Parameters (Array.<T>|goog.array.ArrayLike)(number|undefined)number...TReturns the removed elements.
goog.array.defaultCompare, which compares the elements using
+ the built in < and > operators. This will produce the expected behavior
+ for homogeneous arrays of String(s) and Number(s).
+
+ Runtime: Same as Array.prototype.sort, plus an additional
+ O(n) overhead of copying the array twice.Parameters Array.<T>?function(T, T): number=Parameters (Array.<T>|goog.array.ArrayLike)Returns The object converted into an array. If object has a
+ length property, every property indexed with a non-negative number
+ less than length will be included in the result. If object does not
+ have a length property, an empty array will be returned.
Parameters (Array.<T>|goog.array.ArrayLike)?function(this: S, T, number, ?): stringS=Returns The new object.
!Arrayhttp://docs.python.org/library/functions.html#zipParameters ...!goog.array.ArrayLikeReturns A new array of arrays created from provided arrays.
Global Properties
Array.prototype.Compiler Constants
Namespace goog.asserts
code »Classes
Global Functions
Parameters Tstring=...*Returns The value of the condition.
Throws goog.asserts.AssertionError!ArrayParameters *string=...*Returns The value, guaranteed to be a non-null array.
Throws goog.asserts.AssertionErrorbooleanParameters *string=...*Returns The value, guaranteed to be a boolean when asserts are
+ enabled.
Throws goog.asserts.AssertionError!ElementParameters *string=...*Returns The value, likely to be a DOM Element when asserts are
+ enabled.
Throws goog.asserts.AssertionError!FunctionParameters *string=...*Returns The value, guaranteed to be a function when asserts
+ enabled.
Throws goog.asserts.AssertionErrorParameters *function(new: T, ...)string=...*Throws goog.asserts.AssertionErrornumberParameters *string=...*Returns The value, guaranteed to be a number when asserts enabled.
Throws goog.asserts.AssertionError!ObjectParameters *string=...*Returns The value, guaranteed to be a non-null object.
Throws goog.asserts.AssertionErrorfor (var ... in ...) loops.stringParameters *string=...*Returns The value, guaranteed to be a string when asserts enabled.
Throws goog.asserts.AssertionErrorParameters Throws goog.asserts.AssertionError
+ 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.
+ }
+ Parameters string=...*Throws goog.asserts.AssertionErrorCompiler Constants
Namespace goog.debug
code »Classes
Namespace goog.dom
code »Enumerations
Namespace goog.functions
code »Global Functions
booleannullbooleanfunction(?): booleanParameters ...FunctionReturns A function that ANDs its component
+ functions.
Parameters !function(): TReturns A wrapped version the function.
Parameters function(?): T...FunctionReturns The composition of all inputs.
Parameters TReturns The new function.
!ObjectParameters !Function...*Returns A new instance of the class given in
constructor.!FunctionParameters stringReturns The error-throwing function.
!FunctionParameters *Returns The error-throwing function.
Parameters T=...*Returns The first argument passed in, or undefined if nothing was passed.
!Functionfunction(?): booleanParameters !FunctionReturns A function that delegates to f and returns
+ opposite.
!FunctionParameters numberReturns A new function.
function(?): booleanParameters ...FunctionReturns A function that ORs its component
+ functions.
!FunctionParameters ...FunctionReturns A function that calls all inputs in sequence.
Parameters FunctionTReturns A new function.
Compiler Constants
Namespace goog.iter
code »Classes
goog.iter.groupBy iterator.Type Definitions
Global Functions
!goog.iter.Iterator.<number>iterable. For example, the array [1, 2, 3, 4, 5] yields
+ 1 -> 3 -> 6 -> 10 -> 15.Parameters !goog.iter.Iterable.<number>Returns A new iterator that returns the
+ numbers in the series.
Parameters ...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)Returns Returns a new iterator that will
+ iterate over all the given iterables' contents.
Parameters goog.iter.IterableReturns Returns a new iterator that will
+ iterate over all the contents of the iterables contained within
+
iterable.iterable.
+
+ Combinations are obtained by taking the goog.iter#permutations of
+ iterable and filtering those whose elements appear in the order they
+ are encountered in iterable. For example, the 3-length combinations
+ of [0,1,2,3] are [[0,1,2], [0,1,3], [0,2,3], [1,2,3]].Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)numberReturns A new iterator containing
+ combinations from the
iterable.iterable, with repeated elements possible.
+
+ Combinations are obtained by taking the Cartesian product of length
+ iterables and filtering those whose elements appear in the order they are
+ encountered in iterable. For example, the 2-length combinations of
+ [1,2,3] are [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]].Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)numberReturns A new iterator containing
+ combinations from the
iterable.iterable based on a series of
+ selectors. On each call to next(), one item is taken from
+ both the iterable and selectors iterators. If the item from
+ selectors evaluates to true, the item from iterable is given.
+ Otherwise, it is skipped. Once either iterable or selectors
+ is exhausted, subsequent calls to next() will throw
+ goog.iter.StopIteration.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)iterable should be included in the
+ result.Returns A new iterator that returns the
+ filtered values.
count steps ahead. Consumed
+ values are silently discarded. If count is greater than the number
+ of elements in iterable, an empty iterator is returned. Subsequent
+ calls to next() will throw goog.iter.StopIteration.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)numberReturns An iterator advanced zero or more steps
+ ahead.
!goog.iter.Iterator.<number>Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)Returns An iterator that iterates indefinitely
+ over the values in
iterable.Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=f.Returns A new iterator that drops elements from
+ the original iterator as long as
f is true.iterable.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)number=Returns A new iterator containing count/item
+ pairs.
Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)Returns true if the iterables contain the same sequence of elements
+ and have the same length.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=f.Returns true if every value passes the test.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=f.Returns A new iterator in which only elements
+ that passed the test are present.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=f.Returns A new iterator in which only elements
+ that did not pass the test are present.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)toIterator will be
+ called on it.(function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>)|function(this: THIS, number, undefined, goog.iter.Iterator.<VALUE>))THIS=f.iterable grouped by a key value. For iterables with repeated
+ elements (i.e. sorted according to a particular key function), this function
+ has a uniq-like effect. For example, grouping the array:
+ [A, B, B, C, C, A] produces
+ [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]].Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)function(VALUE): KEY=iterable. Default
+ is the identity function.Returns A new iterator that returns arrays of
+ consecutive key and groups.
Parameters (Array.<VALUE>|goog.array.ArrayLike)Returns True, if the array contains duplicates, false otherwise.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)stringReturns The joined value string.
limitSize elements from an
+ iterable. If this number is greater than the number of elements in the
+ iterable, all the elements are returned.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)numberReturns A new iterator containing
+
limitSize elements.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)function(this: THIS, VALUE, undefined, !goog.iter.Iterator.<VALUE>): RESULTTHIS=f.Returns A new iterator that returns the
+ results of applying the function to each element in the original
+ iterator.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)VALUEReturns The next item in the iteration, or defaultValue if the
+ iterator was empty.
iterable.
+
+ Permutations are obtained by taking the Cartesian product of
+ opt_length iterables and filtering out those with repeated
+ elements. For example, the permutations of [1,2,3] are
+ [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]].Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)number=iterable.Returns A new iterator containing the
+ permutations of
iterable.Parameters ...!goog.array.ArrayLike.<VALUE>Returns An iterator that gives each
+ n-tuple (as an array).
!goog.iter.Iterator.<number>
+ range(5) same as range(0, 5, 1)
+ range(2, 5) same as range(2, 5, 1)
+
Parameters numbernumber=number=Returns A new iterator that returns the values
+ in the range.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, VALUE): VALUEVALUETHIS=Returns Result of evaluating f repeatedly across the values of
+ the iterator.
Parameters VALUEReturns A new iterator that returns the
+ repeated value.
Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)numbernumber=start.Returns A new iterator containing a slice of
+ the original.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=f.Returns true if any value passes the test.
f with the arguments taken from the next element from
+ iterable (the elements are expected to also be iterables).
+
+ Similar to goog.iter#map but allows the function to accept multiple
+ arguments from the iterable.Parameters !goog.iter.Iterablefunction(this: THIS, *): RESULTTHIS=f.Returns A new iterator that returns the
+ results of applying the function to each element in the original
+ iterator.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)function(this: THIS, VALUE, undefined, goog.iter.Iterator.<VALUE>): booleanTHIS=Returns A new iterator that keeps elements in
+ the original iterator as long as the function is true.
iterable without advancing the others.Parameters (!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)number=Returns An array of iterators.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)Returns An array of the elements the iterator iterates over.
Parameters (goog.iter.Iterator.<VALUE>|goog.iter.Iterable)__iterator__ method that will be called to get the value
+ iterator. If the object is an array-like object we create an iterator
+ for that.Returns An iterator that knows how to iterate
+ over the values in
iterable.var_args. Once the shortest iterable is
+ exhausted, subsequent calls to next() will throw
+ goog.iter.StopIteration.Parameters ...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)Returns A new iterator that returns
+ arrays of elements from the provided iterables.
var_args. Shorter iterables will be extended
+ with fillValue. Once the longest iterable is exhausted, subsequent
+ calls to next() will throw goog.iter.StopIteration.Parameters VALUE...(!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable)Returns A new iterator that returns
+ arrays of elements from the provided iterables.
Global Properties
Namespace goog.json
code »Classes
Type Definitions
Global Functions
booleanParameters stringReturns True if the input is a valid JSON string.
ObjectParameters *Returns The object generated from the JSON string, or null.
Throws stringParameters *?goog.json.Replacer=Returns A JSON string representation of the input.
Throws ObjectParameters stringReturns The object generated from the JSON string.
Compiler Constants
Namespace goog.labs
code »Namespace goog.labs.testing
code »Interfaces
Classes
Global Functions
Parameters *!goog.labs.testing.Matcherstring=Namespace goog.labs.userAgent
code »Namespace goog.labs.userAgent.browser
code »Global Functions
stringParameters stringstringParameters stringReturns The browser version or empty string if version cannot be
+ determined. Note that for Internet Explorer, this returns the version of
+ the browser, not the version of the rendering engine. (IE 8 in
+ compatibility mode will return 8.0 rather than 7.0. To determine the
+ rendering engine version, look at document.documentMode instead. See
+ http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more
+ details.)
stringParameters Returns The version or empty string if it cannot be determined.
Returns Whether the user's browser is the Android browser.
Returns Whether the user's browser is Chrome.
Returns Whether the user's browser is Firefox.
Returns Whether the user's browser is IE.
Returns Whether the user's browser is Opera.
Returns Whether the user's browser is Safari.
Returns Whether the user's browser is Silk.
booleanReturns Whether the user's browser is the Android browser.
Returns Whether the user's browser is Chrome.
Returns Whether the user's browser is Firefox.
Returns Whether the user's browser is IE.
Returns Whether the user's browser is Opera.
Returns Whether the user's browser is Safari.
Namespace goog.labs.userAgent.engine
code »Global Functions
Returns The rendering engine's version or empty string if version
+ can't be determined.
stringReturns Whether the rendering engine is Gecko.
Returns Whether the rendering engine is Presto.
Returns Whether the rendering engine is Trident.
booleanReturns Whether the rendering engine is WebKit.
Namespace goog.labs.userAgent.util
code »Global Functions
!ArrayParameters Returns Tuples of key, version, and the contents
+ of the parenthetical.
NavigatorReturns The user agent string.
Parameters Returns Whether the user agent contains the given string, ignoring
+ case.
Parameters Returns Whether the user agent contains the given string.
Parameters ?string=Global Properties
Namespace goog.math
code »Global Functions
numbernumberParameters Returns The number of degrees that when added to
+ startAngle will result in endAngle. Positive numbers mean that the
+ direction is clockwise. Negative numbers indicate a counter-clockwise
+ direction.
+ The shortest route (clockwise vs counter-clockwise) between the angles
+ is used.
+ When the difference is 180 degrees, the function returns 180 (not -180)
+ angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10.
+ angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20.
numbernumbernumberParameters ...numberReturns The average of the arguments (
NaN if no arguments
+ were provided or any of the arguments is not a valid number).numberbooleanParameters numberReturns Whether
num is a finite number.booleanParameters numberReturns Whether
num is an integer.numbernumber
+
Parameters numberReturns Its logarithm to base 10 rounded down to the nearest
+ integer if num > 0. -Infinity if num == 0. NaN if num < 0.
!Array.<Object>Parameters Array.<Object>Array.<Object>Function=Function=Returns A list of objects that are common to both arrays
+ such that there is no common subsequence with size greater than the
+ length of the list.
numberbooleannumbera.Parameters numberReturns A random integer N such that 0 <= N < a.
numberMath.ceil. See goog.math.safeFloor for
+ details.numberMath.floor which tolerates if the passed number
+ is infinitesimally smaller than the closest integer. It often happens with
+ the results of floating point calculations because of the finite precision
+ of the intermediate results. For example Math.floor(Math.log(1000) /
+ Math.LN10) == 2, not 3 as one would expect.numberParameters ...numberReturns The unbiased sample variance of the arguments (0 if fewer
+ than two samples were provided, or
NaN if any of the samples is
+ not a valid number).numberParameters numberReturns -1 when negative, 1 when positive, 0 when 0.
numberParameters numberReturns Standardized angle.
numberParameters numberReturns Standardized angle.
numberParameters ...numberReturns The sample standard deviation of the arguments (0 if fewer
+ than two samples were provided, or
NaN if any of the samples is
+ not a valid number).numberParameters ...numberReturns The sum of the arguments (0 if no arguments were provided,
+
NaN if any of the arguments is not a valid number).numberParameters numberReturns Angle in degrees.
numberParameters numberReturns Angle in radians.
numbera and less than
+ b.Namespace goog.net
code »Interfaces
Classes
Global Functions
Returns A new XMLHttpRequest object.
Namespace goog.net.XmlHttp
code »Main
!goog.net.XhrLike.OrNativeReturns A new XMLHttpRequest object.
Enumerations
Global Functions
Returns The options.
Parameters !goog.net.XmlHttpFactoryGlobal Properties
Compiler Constants
Namespace goog.net.XmlHttpDefines
code »Compiler Constants
Namespace goog.object
code »Global Functions
Parameters ObjectParameters Object.<K, V>Returns Clone of the input object.
Parameters Object.<K, V>VReturns true if val is present.
booleanParameters Object*Returns true If the map contains the key.
Parameters Object.<K, V>VReturns true If the map contains the value.
!ObjectParameters ...*Returns The new object.
Throws ErrorParameters !Object.<K, V>Returns An immutable view of that object, or the
+ original object if this browser does not support immutables.
!ObjectParameters ...*Returns The new object.
Parameters Returns false if any element fails the test.
Parameters Object.<K, V>function(this: T, V, ?, Object.<K, V>): booleanT=Returns a new object in which only elements that passed the
+ test are present.
Parameters Returns The key of an element for which the function
+ returns true or undefined if no such element is found.
Parameters Returns The value of an element for which the function returns true or
+ undefined if no such element is found.
Parameters (string|undefined)Parameters ObjectReturns The key or undefined if the object is empty.
Parameters Object.<K, V>Returns The value or undefined if the object is empty.
numberParameters ObjectReturns The number of key-value pairs in the object map.
!Array.<string>Parameters ObjectReturns Array of property keys.
*Parameters Returns The resulting value. If, at any point, the value for a key
+ is undefined, returns undefined.
Parameters Object.<K, V>Returns The values in the object/map/hash.
booleanParameters ObjectReturns true if obj is empty.
booleanParameters !ObjectReturns Whether this is an immutable view of the object.
Parameters Object.<K, V>function(this: T, V, ?, Object.<K, V>): RT=Returns a new object with the results from f.
booleanParameters Object*Returns Whether an element was removed.
Parameters Returns true if any element passes the test.
!ObjectParameters ObjectReturns The transposed object.
*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.Parameters *Returns A clone of the input value.
Global Properties
Namespace goog.string
code »Enumerations
Global Functions
stringbuildString('a', 'b', 'c', 'd') -> 'abcd'
+ buildString(null, undefined) -> ''
+ Parameters ...*Returns The concatenation of
var_args.stringParameters stringReturns str A copy of {@code} with canonicalized newlines.numberbooleanbooleanbooleanbooleanParameters stringReturns Copy of the string with normalized breaking spaces.
stringParameters stringReturns A copy of
str with collapsed whitespace.numberbooleannumberReturns A unique id.
booleanstringParameters stringReturns An escaped string representing
c.stringParameters stringReturns An escaped string representing
str.Returns A random string, e.g. sn1s7vb4gcic.
numberParameters stringReturns Hash value for
str, between 0 (inclusive) and 2^32
+ (exclusive). The empty string returns 0.stringParameters stringboolean=Returns An escaped copy of
str.booleanParameters stringReturns True if
str consists entirely of letters.booleanParameters stringReturns True if
str is alphanumeric.Parameters stringReturns Whether the string is all breaking whitespace.
booleanParameters stringReturns True if
str is empty or whitespace only.booleanParameters *Returns True if
str is null, undefined, empty, or
+ whitespace only.booleanParameters stringReturns Whether the string is lower camel case.
booleanParameters *Returns True if
str is numeric.booleanParameters stringReturns True if {code ch} is a space.
booleanParameters stringReturns True if {code ch} is a valid unicode character.
booleanParameters stringReturns Whether the string is upper camel case.
stringParameters *Returns A string representation of the
obj.string
s or
s.stringParameters stringReturns A copy of
str with all consecutive spaces and tabs
+ replaced with a single space.stringParameters stringReturns A copy of
str with all whitespace normalized.numberstringpadNumber(1.25, 2, 3) -> '01.250'
+ padNumber(1.25, 2) -> '01.25'
+ padNumber(1.25, 2, 1) -> '01.3'
+ padNumber(1.25, 0) -> '1.25'
numberstringParameters stringReturns A copy of
str with preserved whitespace.stringParameters stringReturns A copy of
s surrounded by double quotes.stringParameters *Returns A RegExp safe, escaped copy of
s.stringstringstringstring!Array.<string>booleanstringParameters stringReturns A copy of
str stripped of newlines.string
+ goog.string.stripQuotes('"abc"', '"`') --> 'abc'
+ goog.string.stripQuotes('`abc`', '"`') --> 'abc'
+ stringParameters string...*Returns A copy of
str in which each occurrence of
+ %s has been replaced an argument from var_args.stringParameters stringReturns The string in camelCase form.
!ObjectParameters stringReturns The map of characters used.
numberParameters stringReturns The number the supplied string represents, or NaN.
stringParameters stringReturns The string in selector-case form.
stringParameters Returns String value in TitleCase form.
stringParameters stringReturns A trimmed copy of
str.stringParameters stringReturns A trimmed copy of
str.stringParameters stringReturns A trimmed copy of
str.stringstringParameters stringnumberboolean=number=Returns A truncated copy of
str.stringParameters stringReturns An unescaped copy of
str.stringstringParameters stringReturns An unescaped copy of
str.stringParameters stringReturns The decoded
str.stringParameters *Returns An encoded copy of
str that is safe for urls.
+ Note that '#', ':', and other characters used to delimit portions
+ of URLs *will* be encoded.stringGlobal Properties
Compiler Constants
Namespace goog.structs
code »Classes
Global Functions
Parameters ObjectbooleanParameters Object*Returns True if the map contains the value.
Parameters Sfunction(this: T, ?, ?, S): booleanT=f.Returns True if all key-value pairs pass the test.
Parameters Sfunction(this: T, ?, ?, S): booleanT=f.Returns A new collection where the passed values are
+ present. If col is a key-less collection an array is returned. If col
+ has keys and values a plain old JS object is returned.
Parameters Sfunction(this: T, ?, ?, S): ?T=f.numberParameters ObjectReturns The number of values in the collection-like object.
(!Array|undefined)Parameters ObjectReturns The keys in the collection.
!ArrayParameters ObjectReturns The values in the collection-like object.
booleanParameters ObjectReturns True if empty.
Parameters Sfunction(this: T, ?, ?, S): VT=f.Returns A new collection with the new values. If
+ col is a key-less collection an array is returned. If col has keys and
+ values a plain old JS object is returned.
Parameters Sfunction(this: T, ?, ?, S): booleanT=f.Returns True if any value passes the test.
Namespace goog.uri
code »Namespace goog.uri.utils
code »Enumerations
Type Definitions
+ var data = [
+ // Simple param: ?name=BobBarker
+ 'name', 'BobBarker',
+ // Conditional param -- may be omitted entirely.
+ 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null,
+ // Multi-valued param: &house=LosAngeles&house=NewYork&house=null
+ 'house', ['LosAngeles', 'NewYork', null]
+ ];
+
Global Functions
Parameters stringgoog.uri.utils.QueryValue!Array.<string>stringParameters Returns The URI with the query parameter added.
string
+ appendParams('http://www.foo.com?existing=true',
+ 'key1', 'value1',
+ 'key2', 'value?willBeEncoded',
+ 'key3', ['valueA', 'valueB', 'valueC'],
+ 'key4', null);
+ result: 'http://www.foo.com?existing=true&' +
+ 'key1=value1&' +
+ 'key2=value%3FwillBeEncoded&' +
+ 'key3=valueA&key3=valueB&key3=valueC'
+
+
+ A single call to this function will not exhibit quadratic behavior in IE,
+ whereas multiple repeated calls may, although the effect is limited by
+ fact that URL's generally can't exceed 2kb.Parameters string...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)Returns The URI with all query parameters added.
stringstringstringParameters !ArrayReturns The concatenated URI and query data.
Parameters stringstringParameters ?string=?string=?string=(string|number|null)=?string=?string=?string=Returns The fully combined URI.
stringParameters goog.uri.utils.QueryArraynumber=Returns The encoded query string, in the form 'a=1&b=2'.
!ArrayParameters !ArrayObject.<goog.uri.utils.QueryValue>Returns The buffer argument.
!ArrayParameters !Array(goog.uri.utils.QueryArray|Arguments)number=Returns The buffer argument.
Parameters ObjectReturns The encoded query string, in the form 'a=1&b=2'.
?stringParameters ?stringReturns The string URI-decoded, or null if uri is null.
numberParameters stringnumberstringnumberReturns The position of the first character in the key's name,
+ immediately after either a question mark or a dot.
?stringParameters goog.uri.utils.ComponentIndexstringReturns The still-encoded component, or null if the component
+ is not present.
?stringParameters stringReturns The decoded domain, or null if none.
?stringParameters stringReturns The domain name still encoded, or null if none.
Parameters stringReturns The protocol or scheme, always lower case.
?stringParameters stringReturns The decoded fragment identifier, or null if none. Does
+ not include the hash mark.
?stringParameters stringReturns The fragment identifier, or null if none. Does not
+ include the hash mark itself.
stringParameters stringReturns Everything up to and including the port.
?string!Array.<string>?stringParameters stringReturns The decoded path, or null if none. Includes the leading
+ slash, if any.
stringParameters stringReturns The URI, starting at the path and including the query
+ parameters and fragment identifier.
?stringParameters stringReturns The path still encoded, or null if none. Includes the
+ leading slash, if any.
?numberParameters stringReturns The port number, or null if none.
?stringParameters stringReturns The query data still encoded, or null if none. Does not
+ include the question mark itself.
?stringParameters stringReturns The protocol or scheme, or null if none. Does not
+ include trailing colons or slashes.
?stringParameters stringReturns The decoded user info, or null if none.
?stringParameters stringReturns The user name still encoded, or null if none.
booleanbooleanstringParameters stringReturns That URI with the "zx" parameter added or replaced to
+ contain a random string.
stringParameters stringReturns Everything preceding the hash mark.
stringstringstringstring!Array
+ goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA];
+
Parameters stringReturns Each component still URI-encoded.
+ Each component that is present will contain the encoded value, whereas
+ components that are not present will be undefined or empty, depending
+ on the browser's regular expression implementation. Never null, since
+ arbitrary strings may still look like path names.
Global Properties
http://www.ietf.org/rfc/rfc3986.txt says in Appendix B
+ As the "first-match-wins" algorithm is identical to the "greedy"
+ disambiguation method used by POSIX regular expressions, it is natural and
+ commonplace to use a regular expression for parsing the potential five
+ components of a URI reference.
+
+ The following line is the regular expression for breaking-down a
+ well-formed URI reference into its components.
+
+
+ ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))?
+ 12 3 4 5 6 7 8 9
+
+
+ The numbers in the second line above are only to assist readability; they
+ indicate the reference points for each subexpression (i.e., each paired
+ parenthesis). We refer to the value matched for subexpression
+ http://www.ics.uci.edu/pub/ietf/uri/#Related
+
+ results in the following subexpression matches:
+
+ $1 = http:
+ $2 = http
+ $3 = //www.ics.uci.edu
+ $4 = www.ics.uci.edu
+ $5 = /pub/ietf/uri/
+ $6 =
+ where
+ scheme = $2
+ authority = $4
+ path = $5
+ query = $7
+ fragment = $9
+
+
+ The regular expression has been modified slightly to expose the
+ userInfo, domain, and port separately from the authority.
+ The modified version yields
+
+ $1 = http scheme
+ $2 =
Namespace goog.userAgent
code »Global Functions
numberReturns 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).
Returns The string that describes the version number of the user
+ agent.
Returns Returns the document mode (for testing).
Returns The native navigator object.
Returns The userAgent string.
booleangoog.userAgent.isDocumentModeOrHigher.Parameters numberReturns Whether the IE effective document mode is higher or the
+ same as the given version.
booleanParameters numberReturns Whether the IE effective document mode is higher or the
+ same as the given version.
Returns Whether the user agent is running on a mobile device.
booleangoog.userAgent.isVersionOrHigher.booleanGlobal Properties
goog.userAgent.product.SAFARI instead.
+ TODO(nicksantos): Delete this from goog.userAgent.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.Compiler Constants
Namespace goog.userAgent.product
code »Global Functions
Returns The string that describes the version number of the user
+ agent product. This is a string rather than a number because it may
+ contain 'b', 'a', and so on.
Parameters !RegExpReturns A result array, or null for no match.
Parameters !RegExpReturns Contents of the first group or an empty string if no match.
booleanGlobal Properties
Compiler Constants
Namespace webdriver
code »Interfaces
webdriver.Command objects.Classes
webdriver.WebDriver clients.alert, confirm, or
+ prompt.Enumerations
Namespace webdriver.By
code »webdriver.Locator
+ instances.Type Definitions
({className: string}|{css: string}|{id: string}|{js: string}|{linkText: string}|{name: string}|{partialLinkText: string}|{tagName: string}|{xpath: string})
+
+
+ var e1 = driver.findElement(webdriver.By.id('foo'));
+ var e2 = driver.findElement({id: 'foo'});
+ Global Functions
!webdriver.LocatorParameters stringReturns The new locator.
!webdriver.Locatorinvalid selector error. An
+ implementation may, however, emulate the CSS selector API.Parameters stringReturns The new locator.
!webdriver.LocatorParameters stringReturns The new locator.
function(!webdriver.WebDriver): !webdriver.promise.PromiseJavaScript expression.
+ The result of this expression must be an element or list of elements.!webdriver.Locatorvisible
+ text matches the given string.Parameters stringReturns The new locator.
!webdriver.Locatorname attribute has the given value.Parameters stringReturns The new locator.
!webdriver.Locatorvisible
+ text contains the given substring.Parameters stringReturns The new locator.
!webdriver.LocatorgetElementsByTagName DOM function.Parameters stringReturns The new locator.
!webdriver.Locatorwebdriver.WebElement as WebDriver
+ will respect the context in the specified in the selector. For example,
+ given the selector "//div", WebDriver will search from the
+ document root regardless of whether the locator was used with a
+ WebElement.Parameters stringReturns The new locator.
Namespace webdriver.http
code »Interfaces
Classes
Global Functions
stringNamespace webdriver.logging
code »Classes
Enumerations
Type Definitions
Global Functions
!webdriver.logging.Levelwebdriver.logging.Level value.
+ If the name/value is not recognized, webdriver.logging.Level.ALL
+ will be returned.Namespace webdriver.process
code »Global Functions
string!ObjectParameters !Window=Returns The new process object.
Returns Whether the current process is Node's native process
+ object.
Parameters string*Global Properties
process object, or an approximation of it for use in a browser
+ environment.Namespace webdriver.promise
code »Classes
webdriver.promise.ControlFlow.webdriver.promise.ControlFlow's task tree.webdriver.promise.ControlFlow.Global Functions
Parameters !ArrayReturns A promise that is
+ fulfilled with an array containing the fulfilled values of the
+ input array, or rejected with the same reason as the first
+ rejected value.
value is resolved. This function is similar to
+ webdriver.promise.when, except it does not return a new promise.Parameters !FunctionReturns A promise that will be resolved with the
+ result of the provided function's callback.
Returns The currently active control flow.
!webdriver.promise.PromiseParameters function(!webdriver.promise.ControlFlow)Returns A promise that resolves to the callback
+ result.
Parameters Function=Returns The new deferred object.
Parameters numberReturns The promise.
Parameters !(Array.<TYPE>|webdriver.promise.Promise)function(this: SELF, TYPE, number, !Array.<TYPE>): (boolean|webdriver.promise.Promise.<boolean>)SELF=fn.Parameters T=Returns The resolved promise.
Parameters *Returns A promise for a fully resolved version
+ of the input value.
value and not a copy.
+
+ Warning: This function makes no checks against objects that contain
+ cyclical references:
+
+ var value = {};
+ value['self'] = value;
+ webdriver.promise.fullyResolved(value); // Stack overflow.
+ Parameters *Returns A promise for a fully resolved version
+ of the input value.
booleanParameters *Returns Whether the value is an error.
booleanvalue should be treated as a promise.
+ Any object whose "then" property is a function will be considered a promise.Parameters *Returns Whether the value is a promise.
Parameters !(Array.<TYPE>|webdriver.promise.Promise)function(this: SELF, TYPE, number, !Array.<TYPE>): ?SELF=fn.Parameters !webdriver.promise.ControlFlowParameters *=Returns The rejected promise.
Parameters !webdriver.promise.ControlFlowThrows Error!webdriver.promise.Promisevalue, returning a new promise
+ that will be resolved when the value is. If value is not a promise,
+ then the return promise will be immediately resolved.Global Properties
Namespace webdriver.stacktrace
code »Classes
Global Functions
!(Error|goog.testing.JsUnitException)Parameters !(Error|goog.testing.JsUnitException)Returns The formatted error.
Returns The frames of the stack trace.
stringParameters (Error|goog.testing.JsUnitException)Returns The stack trace string.
Parameters stringReturns Stack frame object.
Parameters stringReturns Stack frame object or null if the
+ parsing failed.
!ArrayParameters stringReturns Stack frames. The
+ unrecognized frames will be nulled out.
Global Properties
goog.testing.stacktrace.goog.testing.stacktrace.goog.testing.stacktrace.Namespace webdriver.testing
code »Classes
value.value.Global Functions
Parameters *Returns The new assertion.
Namespace webdriver.testing.assert
code »Main
!webdriver.testing.AssertionParameters *Returns The new assertion.
Global Functions
webdriver.testing.Assertion prototype.Parameters string(function(new: goog.labs.testing.Matcher, *)|{matches: function(*): boolean, describe: function(): string})Namespace webdriver.testing.asserts
code »Global Functions
!webdriver.promise.PromiseParameters **goog.labs.testing.Matcher=Returns The assertion result.
!goog.labs.testing.MatcherParameters *Returns The new matcher.
_base.js
1 // Copyright 2012 Selenium committers 2 // Copyright 2012 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview The base module responsible for bootstrapping the Closure 18 * library and providing a means of loading Closure-based modules. 19 * 20 * <p>Each script loaded by this module will be granted access to this module's 21 * {@code require} function; all required non-native modules must be specified 22 * relative to <em>this</em> module. 23 * 24 * <p>This module will load all scripts from the "lib" subdirectory, unless the 25 * SELENIUM_DEV_MODE environment variable has been set to 1, in which case all 26 * scripts will be loaded from the Selenium client containing this script. 27 */ 28 29 'use strict'; 30 31 var fs = require('fs'), 32 path = require('path'), 33 vm = require('vm'); 34 35 36 /** 37 * If this script was loaded from the Selenium project repo, it will operate in 38 * development mode, adjusting how it loads Closure-based dependencies. 39 * @type {boolean} 40 */ 41 var devMode = (function() { 42 var buildDescFile = path.join(__dirname, '..', 'build.desc'); 43 return fs.existsSync(buildDescFile); 44 })(); 45 46 47 /** @return {boolean} Whether this script was loaded in dev mode. */ 48 function isDevMode() { 49 return devMode; 50 } 51 52 53 /** 54 * @type {string} Path to Closure's base file, relative to this module. 55 * @const 56 */ 57 var CLOSURE_BASE_FILE_PATH = (function() { 58 var relativePath = isDevMode() ? 59 '../../../third_party/closure/goog/base.js' : 60 './lib/goog/base.js'; 61 return path.join(__dirname, relativePath); 62 })(); 63 64 65 /** 66 * @type {string} Path to Closure's base file, relative to this module. 67 * @const 68 */ 69 var DEPS_FILE_PATH = (function() { 70 var relativePath = isDevMode() ? 71 '../../../javascript/deps.js' : 72 './lib/goog/deps.js'; 73 return path.join(__dirname, relativePath); 74 })(); 75 76 77 78 /** 79 * Synchronously loads a script into the protected Closure context. 80 * @param {string} src Path to the file to load. 81 */ 82 function loadScript(src) { 83 src = path.normalize(src); 84 var contents = fs.readFileSync(src, 'utf8'); 85 vm.runInContext(contents, closure, src); 86 } 87 88 89 /** 90 * The protected context to host the Closure library. 91 * @type {!Object} 92 * @const 93 */ 94 var closure = vm.createContext({ 95 console: console, 96 setTimeout: setTimeout, 97 setInterval: setInterval, 98 clearTimeout: clearTimeout, 99 clearInterval: clearInterval, 100 process: process, 101 require: require, 102 Buffer: Buffer, 103 Error: Error, 104 CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/', 105 CLOSURE_IMPORT_SCRIPT: function(src) { 106 loadScript(src); 107 return true; 108 }, 109 CLOSURE_NO_DEPS: !isDevMode(), 110 goog: {} 111 }); 112 closure.window = closure; 113 114 115 loadScript(CLOSURE_BASE_FILE_PATH); 116 loadScript(DEPS_FILE_PATH); 117 118 119 /** 120 * Loads a symbol by name from the protected Closure context. 121 * @param {string} symbol The symbol to load. 122 * @return {?} The loaded symbol, or {@code null} if not found. 123 * @throws {Error} If the symbol has not been defined. 124 */ 125 function closureRequire(symbol) { 126 closure.goog.require(symbol); 127 return closure.goog.getObjectByName(symbol); 128 } 129 130 131 // PUBLIC API 132 133 134 /** 135 * Loads a symbol by name from the protected Closure context and exports its 136 * public API to the provided object. This function relies on Closure code 137 * conventions to define the public API of an object as those properties whose 138 * name does not end with "_". 139 * @param {string} symbol The symbol to load. This must resolve to an object. 140 * @return {!Object} An object with the exported API. 141 * @throws {Error} If the symbol has not been defined or does not resolve to 142 * an object. 143 */ 144 exports.exportPublicApi = function(symbol) { 145 var src = closureRequire(symbol); 146 if (typeof src != 'object' || src === null) { 147 throw Error('"' + symbol + '" must resolve to an object'); 148 } 149 150 var dest = {}; 151 Object.keys(src).forEach(function(key) { 152 if (key[key.length - 1] != '_') { 153 dest[key] = src[key]; 154 } 155 }); 156 157 return dest; 158 }; 159 160 161 if (isDevMode()) { 162 exports.closure = closure; 163 } 164 exports.isDevMode = isDevMode; 165 exports.require = closureRequire; builder.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 var base = require('./_base'), 16 executors = require('./executors'); 17 18 var goog = base.require('goog'), 19 AbstractBuilder = base.require('webdriver.AbstractBuilder'), 20 Browser = base.require('webdriver.Browser'), 21 Capability = base.require('webdriver.Capability'), 22 WebDriver = base.require('webdriver.WebDriver'), 23 promise = base.require('webdriver.promise'); 24 25 26 /** 27 * @param {!webdriver.Capabilities} capabilities The desired capabilities. 28 * @return {webdriver.WebDriver} A new WebDriver instance or {@code null} 29 * if the requested browser is not natively supported in Node. 30 */ 31 function createNativeDriver(capabilities) { 32 switch (capabilities.get(Capability.BROWSER_NAME)) { 33 case Browser.CHROME: 34 // Requiring 'chrome' above would create a cycle: 35 // index -> builder -> chrome -> index 36 var chrome = require('./chrome'); 37 return chrome.createDriver(capabilities); 38 39 case Browser.PHANTOM_JS: 40 // Requiring 'phantomjs' would create a cycle: 41 // index -> builder -> phantomjs -> index 42 var phantomjs = require('./phantomjs'); 43 return phantomjs.createDriver(capabilities); 44 45 default: 46 return null; 47 } 48 } 49 50 51 52 /** 53 * Creates new {@link webdriver.WebDriver WebDriver} instances. 54 * @constructor 55 * @extends {webdriver.AbstractBuilder} 56 */ 57 var Builder = function() { 58 goog.base(this); 59 }; 60 goog.inherits(Builder, AbstractBuilder); 61 62 63 /** 64 * Sets the proxy configuration to use for WebDriver clients created by this 65 * builder. Any calls to {@link #withCapabilities} after this function will 66 * overwrite these settings. 67 * @param {!proxy.ProxyConfig} config The configuration to use. 68 * @return {!Builder} A self reference. 69 */ 70 Builder.prototype.setProxy = function(config) { 71 this.getCapabilities().set(Capability.PROXY, config); 72 return this; 73 }; 74 75 76 /** 77 * Sets Chrome-specific options for drivers created by this builder. 78 * @param {!chrome.Options} options The ChromeDriver options to use. 79 * @return {!Builder} A self reference. 80 */ 81 Builder.prototype.setChromeOptions = function(options) { 82 var newCapabilities = options.toCapabilities(this.getCapabilities()); 83 return /** @type {!Builder} */(this.withCapabilities(newCapabilities)); 84 }; 85 86 87 /** 88 * @override 89 */ 90 Builder.prototype.build = function() { 91 var url = this.getServerUrl(); 92 93 // If a remote server wasn't specified, check for browsers we support 94 // natively in node before falling back to using the java Selenium server. 95 if (!url) { 96 var driver = createNativeDriver(this.getCapabilities()); 97 if (driver) { 98 return driver; 99 } 100 101 // Nope, fall-back to using the default java server. 102 url = AbstractBuilder.DEFAULT_SERVER_URL; 103 } 104 105 var executor = executors.createExecutor(url); 106 return WebDriver.createSession(executor, this.getCapabilities()); 107 }; 108 109 110 // PUBLIC API 111 112 113 exports.Builder = Builder; chrome.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 'use strict'; 17 18 var fs = require('fs'), 19 util = require('util'); 20 21 var webdriver = require('./index'), 22 executors = require('./executors'), 23 io = require('./io'), 24 portprober = require('./net/portprober'), 25 remote = require('./remote'); 26 27 28 /** 29 * Name of the ChromeDriver executable. 30 * @type {string} 31 * @const 32 */ 33 var CHROMEDRIVER_EXE = 34 process.platform === 'win32' ? 'chromedriver.exe' : 'chromedriver'; 35 36 37 /** 38 * Creates {@link remote.DriverService} instances that manage a ChromeDriver 39 * server. 40 * @param {string=} opt_exe Path to the server executable to use. If omitted, 41 * the builder will attempt to locate the chromedriver on the current 42 * PATH. 43 * @throws {Error} If provided executable does not exist, or the chromedriver 44 * cannot be found on the PATH. 45 * @constructor 46 */ 47 var ServiceBuilder = function(opt_exe) { 48 /** @private {string} */ 49 this.exe_ = opt_exe || io.findInPath(CHROMEDRIVER_EXE, true); 50 if (!this.exe_) { 51 throw Error( 52 'The ChromeDriver could not be found on the current PATH. Please ' + 53 'download the latest version of the ChromeDriver from ' + 54 'http://chromedriver.storage.googleapis.com/index.html and ensure ' + 55 'it can be found on your PATH.'); 56 } 57 58 if (!fs.existsSync(this.exe_)) { 59 throw Error('File does not exist: ' + this.exe_); 60 } 61 62 /** @private {!Array.<string>} */ 63 this.args_ = []; 64 this.stdio_ = 'ignore'; 65 }; 66 67 68 /** @private {number} */ 69 ServiceBuilder.prototype.port_ = 0; 70 71 72 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */ 73 ServiceBuilder.prototype.stdio_ = 'ignore'; 74 75 76 /** @private {Object.<string, string>} */ 77 ServiceBuilder.prototype.env_ = null; 78 79 80 /** 81 * Sets the port to start the ChromeDriver on. 82 * @param {number} port The port to use, or 0 for any free port. 83 * @return {!ServiceBuilder} A self reference. 84 * @throws {Error} If the port is invalid. 85 */ 86 ServiceBuilder.prototype.usingPort = function(port) { 87 if (port < 0) { 88 throw Error('port must be >= 0: ' + port); 89 } 90 this.port_ = port; 91 return this; 92 }; 93 94 95 /** 96 * Sets the path of the log file the driver should log to. If a log file is 97 * not specified, the driver will log to stderr. 98 * @param {string} path Path of the log file to use. 99 * @return {!ServiceBuilder} A self reference. 100 */ 101 ServiceBuilder.prototype.loggingTo = function(path) { 102 this.args_.push('--log-path=' + path); 103 return this; 104 }; 105 106 107 /** 108 * Enables verbose logging. 109 * @return {!ServiceBuilder} A self reference. 110 */ 111 ServiceBuilder.prototype.enableVerboseLogging = function() { 112 this.args_.push('--verbose'); 113 return this; 114 }; 115 116 117 /** 118 * Sets the number of threads the driver should use to manage HTTP requests. 119 * By default, the driver will use 4 threads. 120 * @param {number} n The number of threads to use. 121 * @return {!ServiceBuilder} A self reference. 122 */ 123 ServiceBuilder.prototype.setNumHttpThreads = function(n) { 124 this.args_.push('--http-threads=' + n); 125 return this; 126 }; 127 128 129 /** 130 * Sets the base path for WebDriver REST commands (e.g. "/wd/hub"). 131 * By default, the driver will accept commands relative to "/". 132 * @param {string} path The base path to use. 133 * @return {!ServiceBuilder} A self reference. 134 */ 135 ServiceBuilder.prototype.setUrlBasePath = function(path) { 136 this.args_.push('--url-base=' + path); 137 return this; 138 }; 139 140 141 /** 142 * Defines the stdio configuration for the driver service. See 143 * {@code child_process.spawn} for more information. 144 * @param {(string|!Array.<string|number|!Stream|null|undefined>)} config The 145 * configuration to use. 146 * @return {!ServiceBuilder} A self reference. 147 */ 148 ServiceBuilder.prototype.setStdio = function(config) { 149 this.stdio_ = config; 150 return this; 151 }; 152 153 154 /** 155 * Defines the environment to start the server under. This settings will be 156 * inherited by every browser session started by the server. 157 * @param {!Object.<string, string>} env The environment to use. 158 * @return {!ServiceBuilder} A self reference. 159 */ 160 ServiceBuilder.prototype.withEnvironment = function(env) { 161 this.env_ = env; 162 return this; 163 }; 164 165 166 /** 167 * Creates a new DriverService using this instance's current configuration. 168 * @return {remote.DriverService} A new driver service using this instance's 169 * current configuration. 170 * @throws {Error} If the driver exectuable was not specified and a default 171 * could not be found on the current PATH. 172 */ 173 ServiceBuilder.prototype.build = function() { 174 var port = this.port_ || portprober.findFreePort(); 175 var args = this.args_.concat(); // Defensive copy. 176 177 return new remote.DriverService(this.exe_, { 178 loopback: true, 179 port: port, 180 args: webdriver.promise.when(port, function(port) { 181 return args.concat('--port=' + port); 182 }), 183 env: this.env_, 184 stdio: this.stdio_ 185 }); 186 }; 187 188 189 /** @type {remote.DriverService} */ 190 var defaultService = null; 191 192 193 /** 194 * Sets the default service to use for new ChromeDriver instances. 195 * @param {!remote.DriverService} service The service to use. 196 * @throws {Error} If the default service is currently running. 197 */ 198 function setDefaultService(service) { 199 if (defaultService && defaultService.isRunning()) { 200 throw Error( 201 'The previously configured ChromeDriver service is still running. ' + 202 'You must shut it down before you may adjust its configuration.'); 203 } 204 defaultService = service; 205 } 206 207 208 /** 209 * Returns the default ChromeDriver service. If such a service has not been 210 * configured, one will be constructed using the default configuration for 211 * a ChromeDriver executable found on the system PATH. 212 * @return {!remote.DriverService} The default ChromeDriver service. 213 */ 214 function getDefaultService() { 215 if (!defaultService) { 216 defaultService = new ServiceBuilder().build(); 217 } 218 return defaultService; 219 } 220 221 222 /** 223 * @type {string} 224 * @const 225 */ 226 var OPTIONS_CAPABILITY_KEY = 'chromeOptions'; 227 228 229 /** 230 * Class for managing ChromeDriver specific options. 231 * @constructor 232 */ 233 var Options = function() { 234 /** @private {!Array.<string>} */ 235 this.args_ = []; 236 237 /** @private {!Array.<(string|!Buffer)>} */ 238 this.extensions_ = []; 239 }; 240 241 242 /** 243 * Extracts the ChromeDriver specific options from the given capabilities 244 * object. 245 * @param {!webdriver.Capabilities} capabilities The capabilities object. 246 * @return {!Options} The ChromeDriver options. 247 */ 248 Options.fromCapabilities = function(capabilities) { 249 var options = new Options(); 250 251 var o = capabilities.get(OPTIONS_CAPABILITY_KEY); 252 if (o instanceof Options) { 253 options = o; 254 } else if (o) { 255 options. 256 addArguments(o.args || []). 257 addExtensions(o.extensions || []). 258 detachDriver(!!o.detach). 259 setChromeBinaryPath(o.binary). 260 setChromeLogFile(o.logFile). 261 setLocalState(o.localState). 262 setUserPreferences(o.prefs); 263 } 264 265 if (capabilities.has(webdriver.Capability.PROXY)) { 266 options.setProxy(capabilities.get(webdriver.Capability.PROXY)); 267 } 268 269 if (capabilities.has(webdriver.Capability.LOGGING_PREFS)) { 270 options.setLoggingPreferences( 271 capabilities.get(webdriver.Capability.LOGGING_PREFS)); 272 } 273 274 return options; 275 }; 276 277 278 /** 279 * Add additional command line arguments to use when launching the Chrome 280 * browser. Each argument may be specified with or without the "--" prefix 281 * (e.g. "--foo" and "foo"). Arguments with an associated value should be 282 * delimited by an "=": "foo=bar". 283 * @param {...(string|!Array.<string>)} var_args The arguments to add. 284 * @return {!Options} A self reference. 285 */ 286 Options.prototype.addArguments = function(var_args) { 287 this.args_ = this.args_.concat.apply(this.args_, arguments); 288 return this; 289 }; 290 291 292 /** 293 * Add additional extensions to install when launching Chrome. Each extension 294 * should be specified as the path to the packed CRX file, or a Buffer for an 295 * extension. 296 * @param {...(string|!Buffer|!Array.<(string|!Buffer)>)} var_args The 297 * extensions to add. 298 * @return {!Options} A self reference. 299 */ 300 Options.prototype.addExtensions = function(var_args) { 301 this.extensions_ = this.extensions_.concat.apply( 302 this.extensions_, arguments); 303 return this; 304 }; 305 306 307 /** 308 * Sets the path to the Chrome binary to use. On Mac OS X, this path should 309 * reference the actual Chrome executable, not just the application binary 310 * (e.g. "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"). 311 * 312 * The binary path be absolute or relative to the chromedriver server 313 * executable, but it must exist on the machine that will launch Chrome. 314 * 315 * @param {string} path The path to the Chrome binary to use. 316 * @return {!Options} A self reference. 317 */ 318 Options.prototype.setChromeBinaryPath = function(path) { 319 this.binary_ = path; 320 return this; 321 }; 322 323 324 /** 325 * Sets whether to leave the started Chrome browser running if the controlling 326 * ChromeDriver service is killed before {@link webdriver.WebDriver#quit()} is 327 * called. 328 * @param {boolean} detach Whether to leave the browser running if the 329 * chromedriver service is killed before the session. 330 * @return {!Options} A self reference. 331 */ 332 Options.prototype.detachDriver = function(detach) { 333 this.detach_ = detach; 334 return this; 335 }; 336 337 338 /** 339 * Sets the user preferences for Chrome's user profile. See the "Preferences" 340 * file in Chrome's user data directory for examples. 341 * @param {!Object} prefs Dictionary of user preferences to use. 342 * @return {!Options} A self reference. 343 */ 344 Options.prototype.setUserPreferences = function(prefs) { 345 this.prefs_ = prefs; 346 return this; 347 }; 348 349 350 /** 351 * Sets the logging preferences for the new session. 352 * @param {!webdriver.logging.Preferences} prefs The logging preferences. 353 * @return {!Options} A self reference. 354 */ 355 Options.prototype.setLoggingPreferences = function(prefs) { 356 this.logPrefs_ = prefs; 357 return this; 358 }; 359 360 361 /** 362 * Sets preferences for the "Local State" file in Chrome's user data 363 * directory. 364 * @param {!Object} state Dictionary of local state preferences. 365 * @return {!Options} A self reference. 366 */ 367 Options.prototype.setLocalState = function(state) { 368 this.localState_ = state; 369 return this; 370 }; 371 372 373 /** 374 * Sets the path to Chrome's log file. This path should exist on the machine 375 * that will launch Chrome. 376 * @param {string} path Path to the log file to use. 377 * @return {!Options} A self reference. 378 */ 379 Options.prototype.setChromeLogFile = function(path) { 380 this.logFile_ = path; 381 return this; 382 }; 383 384 385 /** 386 * Sets the proxy settings for the new session. 387 * @param {ProxyConfig} proxy The proxy configuration to use. 388 * @return {!Options} A self reference. 389 */ 390 Options.prototype.setProxy = function(proxy) { 391 this.proxy_ = proxy; 392 return this; 393 }; 394 395 396 /** 397 * Converts this options instance to a {@link webdriver.Capabilities} object. 398 * @param {webdriver.Capabilities=} opt_capabilities The capabilities to merge 399 * these options into, if any. 400 * @return {!webdriver.Capabilities} The capabilities. 401 */ 402 Options.prototype.toCapabilities = function(opt_capabilities) { 403 var capabilities = opt_capabilities || webdriver.Capabilities.chrome(); 404 capabilities. 405 set(webdriver.Capability.PROXY, this.proxy_). 406 set(webdriver.Capability.LOGGING_PREFS, this.logPrefs_). 407 set(OPTIONS_CAPABILITY_KEY, this); 408 return capabilities; 409 }; 410 411 412 /** 413 * Converts this instance to its JSON wire protocol representation. Note this 414 * function is an implementation not intended for general use. 415 * @return {{args: !Array.<string>, 416 * binary: (string|undefined), 417 * detach: boolean, 418 * extensions: !Array.<string>, 419 * localState: (Object|undefined), 420 * logFile: (string|undefined), 421 * prefs: (Object|undefined)}} The JSON wire protocol representation 422 * of this instance. 423 */ 424 Options.prototype.toJSON = function() { 425 return { 426 args: this.args_, 427 binary: this.binary_, 428 detach: !!this.detach_, 429 extensions: this.extensions_.map(function(extension) { 430 if (Buffer.isBuffer(extension)) { 431 return extension.toString('base64'); 432 } 433 return fs.readFileSync(extension, 'base64'); 434 }), 435 localState: this.localState_, 436 logFile: this.logFile_, 437 prefs: this.prefs_ 438 }; 439 }; 440 441 442 /** 443 * Creates a new ChromeDriver session. 444 * @param {(webdriver.Capabilities|Options)=} opt_options The session options. 445 * @param {remote.DriverService=} opt_service The session to use; will use 446 * the {@link getDefaultService default service} by default. 447 * @return {!webdriver.WebDriver} A new WebDriver instance. 448 */ 449 function createDriver(opt_options, opt_service) { 450 var service = opt_service || getDefaultService(); 451 var executor = executors.createExecutor(service.start()); 452 453 var options = opt_options || new Options(); 454 if (opt_options instanceof webdriver.Capabilities) { 455 // Extract the Chrome-specific options so we do not send unnecessary 456 // data across the wire. 457 options = Options.fromCapabilities(options); 458 } 459 460 return webdriver.WebDriver.createSession( 461 executor, options.toCapabilities()); 462 } 463 464 465 466 // PUBLIC API 467 468 469 exports.ServiceBuilder = ServiceBuilder; 470 exports.Options = Options; 471 exports.createDriver = createDriver; 472 exports.getDefaultService = getDefaultService; 473 exports.setDefaultService = setDefaultService; error.js
1 // Copyright 2014 Selenium committers 2 // Copyright 2014 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 'use strict'; 17 18 var base = require('./_base'); 19 20 21 /** @type {function(new: bot.Error)} */ 22 exports.Error = base.require('bot.Error'); 23 24 /** @type {bot.ErrorCode.} */ 25 exports.ErrorCode = base.require('bot.ErrorCode'); executors.js
1 // Copyright 2013 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Various utilities for working with 17 * {@link webdriver.CommandExecutor} implementations. 18 */ 19 20 var HttpClient = require('./http').HttpClient, 21 HttpExecutor = require('./http').Executor, 22 promise = require('./_base').require('webdriver.promise'); 23 24 25 26 /** 27 * Wraps a promised {@link webdriver.CommandExecutor}, ensuring no commands 28 * are executed until the wrapped executor has been fully built. 29 * @param {!webdriver.promise.Promise.<!webdriver.CommandExecutor>} delegate The 30 * promised delegate. 31 * @constructor 32 * @implements {webdriver.CommandExecutor} 33 */ 34 var DeferredExecutor = function(delegate) { 35 36 /** @override */ 37 this.execute = function(command, callback) { 38 delegate.then(function(executor) { 39 executor.execute(command, callback); 40 }, callback); 41 }; 42 }; 43 44 45 // PUBLIC API 46 47 48 /** 49 * Creates a command executor that uses WebDriver's JSON wire protocol. 50 * @param {(string|!webdriver.promise.Promise.<string>)} url The server's URL, 51 * or a promise that will resolve to that URL. 52 * @returns {!webdriver.CommandExecutor} The new command executor. 53 */ 54 exports.createExecutor = function(url) { 55 return new DeferredExecutor(promise.when(url, function(url) { 56 var client = new HttpClient(url); 57 return new HttpExecutor(client); 58 })); 59 }; http/index.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Defines a the {@code webdriver.http.Client} for use with 17 * NodeJS. 18 */ 19 20 var http = require('http'), 21 url = require('url'); 22 23 var base = require('../_base'), 24 HttpResponse = base.require('webdriver.http.Response'); 25 26 27 /** 28 * A {@link webdriver.http.Client} implementation using Node's built-in http 29 * module. 30 * @param {string} serverUrl URL for the WebDriver server to send commands to. 31 * @constructor 32 * @implements {webdriver.http.Client} 33 */ 34 var HttpClient = function(serverUrl) { 35 var parsedUrl = url.parse(serverUrl); 36 if (!parsedUrl.hostname) { 37 throw new Error('Invalid server URL: ' + serverUrl); 38 } 39 40 /** 41 * Base options for each request. 42 * @private {!Object} 43 */ 44 this.options_ = { 45 host: parsedUrl.hostname, 46 path: parsedUrl.pathname, 47 port: parsedUrl.port 48 }; 49 }; 50 51 52 /** @override */ 53 HttpClient.prototype.send = function(httpRequest, callback) { 54 var data; 55 httpRequest.headers['Content-Length'] = 0; 56 if (httpRequest.method == 'POST' || httpRequest.method == 'PUT') { 57 data = JSON.stringify(httpRequest.data); 58 httpRequest.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); 59 httpRequest.headers['Content-Type'] = 'application/json;charset=UTF-8'; 60 } 61 62 var path = this.options_.path; 63 if (path[path.length - 1] === '/' && httpRequest.path[0] === '/') { 64 path += httpRequest.path.substring(1); 65 } else { 66 path += httpRequest.path; 67 } 68 69 sendRequest({ 70 method: httpRequest.method, 71 host: this.options_.host, 72 port: this.options_.port, 73 path: path, 74 headers: httpRequest.headers 75 }, callback, data); 76 }; 77 78 79 /** 80 * Sends a single HTTP request. 81 * @param {!Object} options The request options. 82 * @param {function(Error, !webdriver.http.Response=)} callback The function to 83 * invoke with the server's response. 84 * @param {string=} opt_data The data to send with the request. 85 */ 86 var sendRequest = function(options, callback, opt_data) { 87 var request = http.request(options, function(response) { 88 if (response.statusCode == 302 || response.statusCode == 303) { 89 try { 90 var location = url.parse(response.headers['location']); 91 } catch (ex) { 92 callback(Error( 93 'Failed to parse "Location" header for server redirect: ' + 94 ex.message + '\nResponse was: \n' + 95 new HttpResponse(response.statusCode, response.headers, ''))); 96 return; 97 } 98 99 if (!location.hostname) { 100 location.hostname = options.host; 101 location.port = options.port; 102 } 103 104 request.abort(); 105 sendRequest({ 106 method: 'GET', 107 host: location.hostname, 108 path: location.pathname + (location.search || ''), 109 port: location.port, 110 headers: { 111 'Accept': 'application/json; charset=utf-8' 112 } 113 }, callback); 114 return; 115 } 116 117 var body = []; 118 response.on('data', body.push.bind(body)); 119 response.on('end', function() { 120 var resp = new HttpResponse(response.statusCode, 121 response.headers, body.join('').replace(/\0/g, '')); 122 callback(null, resp); 123 }); 124 }); 125 126 request.on('error', function(e) { 127 if (e.code === 'ECONNRESET') { 128 setTimeout(function() { 129 sendRequest(options, callback, opt_data); 130 }, 15); 131 } else { 132 var message = e.message; 133 if (e.code) { 134 message = e.code + ' ' + message; 135 } 136 callback(new Error(message)); 137 } 138 }); 139 140 if (opt_data) { 141 request.write(opt_data); 142 } 143 144 request.end(); 145 }; 146 147 148 // PUBLIC API 149 150 /** @type {webdriver.http.Executor.} */ 151 exports.Executor = base.require('webdriver.http.Executor'); 152 153 /** @type {webdriver.http.Request.} */ 154 exports.Request = base.require('webdriver.http.Request'); 155 156 /** @type {webdriver.http.Response.} */ 157 exports.Response = base.require('webdriver.http.Response'); 158 159 exports.HttpClient = HttpClient; http/util.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview Various HTTP utilities. 18 */ 19 20 var base = require('../_base'), 21 HttpClient = require('./index').HttpClient, 22 checkResponse = base.require('bot.response').checkResponse, 23 Executor = base.require('webdriver.http.Executor'), 24 HttpRequest = base.require('webdriver.http.Request'), 25 Command = base.require('webdriver.Command'), 26 CommandName = base.require('webdriver.CommandName'), 27 promise = base.require('webdriver.promise'); 28 29 30 31 /** 32 * Queries a WebDriver server for its current status. 33 * @param {string} url Base URL of the server to query. 34 * @param {function(Error, *=)} callback The function to call with the 35 * response. 36 */ 37 function getStatus(url, callback) { 38 var client = new HttpClient(url); 39 var executor = new Executor(client); 40 var command = new Command(CommandName.GET_SERVER_STATUS); 41 executor.execute(command, function(err, responseObj) { 42 if (err) return callback(err); 43 try { 44 checkResponse(responseObj); 45 } catch (ex) { 46 return callback(ex); 47 } 48 callback(null, responseObj['value']); 49 }); 50 } 51 52 53 // PUBLIC API 54 55 56 /** 57 * Queries a WebDriver server for its current status. 58 * @param {string} url Base URL of the server to query. 59 * @return {!webdriver.promise.Promise.<!Object>} A promise that resolves with 60 * a hash of the server status. 61 */ 62 exports.getStatus = function(url) { 63 return promise.checkedNodeCall(getStatus.bind(null, url)); 64 }; 65 66 67 /** 68 * Waits for a WebDriver server to be healthy and accepting requests. 69 * @param {string} url Base URL of the server to query. 70 * @param {number} timeout How long to wait for the server. 71 * @return {!webdriver.promise.Promise} A promise that will resolve when the 72 * server is ready. 73 */ 74 exports.waitForServer = function(url, timeout) { 75 var ready = promise.defer(), 76 start = Date.now(), 77 checkServerStatus = getStatus.bind(null, url, onResponse); 78 checkServerStatus(); 79 return ready.promise; 80 81 function onResponse(err) { 82 if (!ready.isPending()) return; 83 if (!err) return ready.fulfill(); 84 85 if (Date.now() - start > timeout) { 86 ready.reject( 87 Error('Timed out waiting for the WebDriver server at ' + url)); 88 } else { 89 setTimeout(function() { 90 if (ready.isPending()) { 91 checkServerStatus(); 92 } 93 }, 50); 94 } 95 } 96 }; 97 98 99 /** 100 * Polls a URL with GET requests until it returns a 2xx response or the 101 * timeout expires. 102 * @param {string} url The URL to poll. 103 * @param {number} timeout How long to wait, in milliseconds. 104 * @return {!webdriver.promise.Promise} A promise that will resolve when the 105 * URL responds with 2xx. 106 */ 107 exports.waitForUrl = function(url, timeout) { 108 var client = new HttpClient(url), 109 request = new HttpRequest('GET', ''), 110 testUrl = client.send.bind(client, request, onResponse), 111 ready = promise.defer(), 112 start = Date.now(); 113 testUrl(); 114 return ready.promise; 115 116 function onResponse(err, response) { 117 if (!ready.isPending()) return; 118 if (!err && response.status > 199 && response.status < 300) { 119 return ready.fulfill(); 120 } 121 122 if (Date.now() - start > timeout) { 123 ready.reject(Error( 124 'Timed out waiting for the URL to return 2xx: ' + url)); 125 } else { 126 setTimeout(function() { 127 if (ready.isPending()) { 128 testUrl(); 129 } 130 }, 50); 131 } 132 } 133 }; index.js
1 // Copyright 2012 Selenium committers 2 // Copyright 2012 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview The main user facing module. Exports WebDriver's primary 18 * public API and provides convenience assessors to certain sub-modules. 19 */ 20 21 var base = require('./_base'); 22 var builder = require('./builder'); 23 var error = require('./error'); 24 25 26 // NOTE: the remainder of this file is nasty and verbose, but the annotations 27 // are necessary to guide the Closure Compiler's type analysis. Without them, 28 // we would not be able to extract any meaningful API documentation. 29 30 31 /** @type {function(new: webdriver.ActionSequence)} */ 32 exports.ActionSequence = base.require('webdriver.ActionSequence'); 33 34 35 /** @type {function(new: builder.Builder)} */ 36 exports.Builder = builder.Builder; 37 38 39 /** @type {webdriver.By.} */ 40 exports.By = base.require('webdriver.By'); 41 42 43 /** @type {function(new: webdriver.Capabilities)} */ 44 exports.Capabilities = base.require('webdriver.Capabilities'); 45 46 47 /** @type {function(new: webdriver.Command)} */ 48 exports.Command = base.require('webdriver.Command'); 49 50 51 /** @type {function(new: webdriver.EventEmitter)} */ 52 exports.EventEmitter = base.require('webdriver.EventEmitter'); 53 54 55 /** @type {function(new: webdriver.Session)} */ 56 exports.Session = base.require('webdriver.Session'); 57 58 59 /** @type {function(new: webdriver.WebDriver)} */ 60 exports.WebDriver = base.require('webdriver.WebDriver'); 61 62 63 /** @type {function(new: webdriver.WebElement)} */ 64 exports.WebElement = base.require('webdriver.WebElement'); 65 66 67 // Export the remainder of our API through getters to keep things cleaner 68 // when this module is used in a REPL environment. 69 70 71 /** @type {webdriver.Browser.} */ 72 (exports.__defineGetter__('Browser', function() { 73 return base.require('webdriver.Browser'); 74 })); 75 76 77 /** @type {webdriver.Button.} */ 78 (exports.__defineGetter__('Button', function() { 79 return base.require('webdriver.Button'); 80 })); 81 82 83 /** @type {webdriver.Capability.} */ 84 (exports.__defineGetter__('Capability', function() { 85 return base.require('webdriver.Capability'); 86 })); 87 88 89 /** @type {webdriver.CommandName.} */ 90 (exports.__defineGetter__('CommandName', function() { 91 return base.require('webdriver.CommandName'); 92 })); 93 94 95 /** @type {webdriver.Key.} */ 96 (exports.__defineGetter__('Key', function() { 97 return base.require('webdriver.Key'); 98 })); 99 100 101 /** @type {error.} */ 102 (exports.__defineGetter__('error', function() { 103 return error; 104 })); 105 106 107 /** @type {error.} */ 108 (exports.__defineGetter__('error', function() { 109 return error; 110 })); 111 112 113 /** @type {webdriver.logging.} */ 114 (exports.__defineGetter__('logging', function() { 115 return base.exportPublicApi('webdriver.logging'); 116 })); 117 118 119 /** @type {webdriver.promise.} */ 120 (exports.__defineGetter__('promise', function() { 121 return base.exportPublicApi('webdriver.promise'); 122 })); 123 124 125 /** @type {webdriver.stacktrace.} */ 126 (exports.__defineGetter__('stacktrace', function() { 127 return base.exportPublicApi('webdriver.stacktrace'); 128 })); io/index.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 var fs = require('fs'), 17 path = require('path'); 18 19 20 var PATH_SEPARATOR = process.platform === 'win32' ? ';' : ':'; 21 22 23 // PUBLIC API 24 25 26 /** 27 * Searches the {@code PATH} environment variable for the given file. 28 * @param {string} file The file to locate on the PATH. 29 * @param {boolean=} opt_checkCwd Whether to always start with the search with 30 * the current working directory, regardless of whether it is explicitly 31 * listed on the PATH. 32 * @return {?string} Path to the located file, or {@code null} if it could 33 * not be found. 34 */ 35 exports.findInPath = function(file, opt_checkCwd) { 36 if (opt_checkCwd) { 37 var tmp = path.join(process.cwd(), file); 38 if (fs.existsSync(tmp)) { 39 return tmp; 40 } 41 } 42 43 var dirs = process.env['PATH'].split(PATH_SEPARATOR); 44 var found = null; 45 dirs.forEach(function(dir) { 46 var tmp = path.join(dir, file); 47 if (!found && fs.existsSync(tmp)) { 48 found = tmp; 49 } 50 }); 51 return found; 52 }; lib/atoms/error.js
1 // Copyright 2010 WebDriver committers 2 // Copyright 2010 Google Inc. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview Utilities for working with errors as defined by WebDriver's 18 * wire protocol: http://code.google.com/p/selenium/wiki/JsonWireProtocol. 19 */ 20 21 goog.provide('bot.Error'); 22 goog.provide('bot.ErrorCode'); 23 24 25 /** 26 * Error codes from the WebDriver wire protocol: 27 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes 28 * 29 * @enum {number} 30 */ 31 bot.ErrorCode = { 32 SUCCESS: 0, // Included for completeness 33 34 NO_SUCH_ELEMENT: 7, 35 NO_SUCH_FRAME: 8, 36 UNKNOWN_COMMAND: 9, 37 UNSUPPORTED_OPERATION: 9, // Alias. 38 STALE_ELEMENT_REFERENCE: 10, 39 ELEMENT_NOT_VISIBLE: 11, 40 INVALID_ELEMENT_STATE: 12, 41 UNKNOWN_ERROR: 13, 42 ELEMENT_NOT_SELECTABLE: 15, 43 JAVASCRIPT_ERROR: 17, 44 XPATH_LOOKUP_ERROR: 19, 45 TIMEOUT: 21, 46 NO_SUCH_WINDOW: 23, 47 INVALID_COOKIE_DOMAIN: 24, 48 UNABLE_TO_SET_COOKIE: 25, 49 MODAL_DIALOG_OPENED: 26, 50 NO_MODAL_DIALOG_OPEN: 27, 51 SCRIPT_TIMEOUT: 28, 52 INVALID_ELEMENT_COORDINATES: 29, 53 IME_NOT_AVAILABLE: 30, 54 IME_ENGINE_ACTIVATION_FAILED: 31, 55 INVALID_SELECTOR_ERROR: 32, 56 SESSION_NOT_CREATED: 33, 57 MOVE_TARGET_OUT_OF_BOUNDS: 34, 58 SQL_DATABASE_ERROR: 35, 59 INVALID_XPATH_SELECTOR: 51, 60 INVALID_XPATH_SELECTOR_RETURN_TYPE: 52, 61 // The following error codes are derived straight from HTTP return codes. 62 METHOD_NOT_ALLOWED: 405 63 }; 64 65 66 67 /** 68 * Error extension that includes error status codes from the WebDriver wire 69 * protocol: 70 * http://code.google.com/p/selenium/wiki/JsonWireProtocol#Response_Status_Codes 71 * 72 * @param {!bot.ErrorCode} code The error's status code. 73 * @param {string=} opt_message Optional error message. 74 * @constructor 75 * @extends {Error} 76 */ 77 bot.Error = function(code, opt_message) { 78 79 /** 80 * This error's status code. 81 * @type {!bot.ErrorCode} 82 */ 83 this.code = code; 84 85 /** @type {string} */ 86 this.state = 87 bot.Error.CODE_TO_STATE_[code] || bot.Error.State.UNKNOWN_ERROR; 88 89 /** @override */ 90 this.message = opt_message || ''; 91 92 var name = this.state.replace(/((?:^|\s+)[a-z])/g, function(str) { 93 // IE<9 does not support String#trim(). Also, IE does not include 0xa0 94 // (the non-breaking-space) in the \s character class, so we have to 95 // explicitly include it. 96 return str.toUpperCase().replace(/^[\s\xa0]+/g, ''); 97 }); 98 99 var l = name.length - 'Error'.length; 100 if (l < 0 || name.indexOf('Error', l) != l) { 101 name += 'Error'; 102 } 103 104 /** @override */ 105 this.name = name; 106 107 // Generate a stacktrace for our custom error; ensure the error has our 108 // custom name and message so the stack prints correctly in all browsers. 109 var template = new Error(this.message); 110 template.name = this.name; 111 112 /** @override */ 113 this.stack = template.stack || ''; 114 }; 115 goog.inherits(bot.Error, Error); 116 117 118 /** 119 * Status strings enumerated in the W3C WebDriver working draft. 120 * @enum {string} 121 * @see http://www.w3.org/TR/webdriver/#status-codes 122 */ 123 bot.Error.State = { 124 ELEMENT_NOT_SELECTABLE: 'element not selectable', 125 ELEMENT_NOT_VISIBLE: 'element not visible', 126 IME_ENGINE_ACTIVATION_FAILED: 'ime engine activation failed', 127 IME_NOT_AVAILABLE: 'ime not available', 128 INVALID_COOKIE_DOMAIN: 'invalid cookie domain', 129 INVALID_ELEMENT_COORDINATES: 'invalid element coordinates', 130 INVALID_ELEMENT_STATE: 'invalid element state', 131 INVALID_SELECTOR: 'invalid selector', 132 JAVASCRIPT_ERROR: 'javascript error', 133 MOVE_TARGET_OUT_OF_BOUNDS: 'move target out of bounds', 134 NO_SUCH_ALERT: 'no such alert', 135 NO_SUCH_DOM: 'no such dom', 136 NO_SUCH_ELEMENT: 'no such element', 137 NO_SUCH_FRAME: 'no such frame', 138 NO_SUCH_WINDOW: 'no such window', 139 SCRIPT_TIMEOUT: 'script timeout', 140 SESSION_NOT_CREATED: 'session not created', 141 STALE_ELEMENT_REFERENCE: 'stale element reference', 142 SUCCESS: 'success', 143 TIMEOUT: 'timeout', 144 UNABLE_TO_SET_COOKIE: 'unable to set cookie', 145 UNEXPECTED_ALERT_OPEN: 'unexpected alert open', 146 UNKNOWN_COMMAND: 'unknown command', 147 UNKNOWN_ERROR: 'unknown error', 148 UNSUPPORTED_OPERATION: 'unsupported operation' 149 }; 150 151 152 /** 153 * A map of error codes to state string. 154 * @private {!Object.<bot.ErrorCode, bot.Error.State>} 155 */ 156 bot.Error.CODE_TO_STATE_ = {}; 157 goog.scope(function() { 158 var map = bot.Error.CODE_TO_STATE_; 159 var code = bot.ErrorCode; 160 var state = bot.Error.State; 161 162 map[code.ELEMENT_NOT_SELECTABLE] = state.ELEMENT_NOT_SELECTABLE; 163 map[code.ELEMENT_NOT_VISIBLE] = state.ELEMENT_NOT_VISIBLE; 164 map[code.IME_ENGINE_ACTIVATION_FAILED] = state.IME_ENGINE_ACTIVATION_FAILED; 165 map[code.IME_NOT_AVAILABLE] = state.IME_NOT_AVAILABLE; 166 map[code.INVALID_COOKIE_DOMAIN] = state.INVALID_COOKIE_DOMAIN; 167 map[code.INVALID_ELEMENT_COORDINATES] = state.INVALID_ELEMENT_COORDINATES; 168 map[code.INVALID_ELEMENT_STATE] = state.INVALID_ELEMENT_STATE; 169 map[code.INVALID_SELECTOR_ERROR] = state.INVALID_SELECTOR; 170 map[code.INVALID_XPATH_SELECTOR] = state.INVALID_SELECTOR; 171 map[code.INVALID_XPATH_SELECTOR_RETURN_TYPE] = state.INVALID_SELECTOR; 172 map[code.JAVASCRIPT_ERROR] = state.JAVASCRIPT_ERROR; 173 map[code.METHOD_NOT_ALLOWED] = state.UNSUPPORTED_OPERATION; 174 map[code.MOVE_TARGET_OUT_OF_BOUNDS] = state.MOVE_TARGET_OUT_OF_BOUNDS; 175 map[code.NO_MODAL_DIALOG_OPEN] = state.NO_SUCH_ALERT; 176 map[code.NO_SUCH_ELEMENT] = state.NO_SUCH_ELEMENT; 177 map[code.NO_SUCH_FRAME] = state.NO_SUCH_FRAME; 178 map[code.NO_SUCH_WINDOW] = state.NO_SUCH_WINDOW; 179 map[code.SCRIPT_TIMEOUT] = state.SCRIPT_TIMEOUT; 180 map[code.SESSION_NOT_CREATED] = state.SESSION_NOT_CREATED; 181 map[code.STALE_ELEMENT_REFERENCE] = state.STALE_ELEMENT_REFERENCE; 182 map[code.SUCCESS] = state.SUCCESS; 183 map[code.TIMEOUT] = state.TIMEOUT; 184 map[code.UNABLE_TO_SET_COOKIE] = state.UNABLE_TO_SET_COOKIE; 185 map[code.MODAL_DIALOG_OPENED] = state.UNEXPECTED_ALERT_OPEN; 186 map[code.UNKNOWN_ERROR] = state.UNKNOWN_ERROR; 187 map[code.UNSUPPORTED_OPERATION] = state.UNKNOWN_COMMAND; 188 }); // goog.scope 189 190 191 /** 192 * Flag used for duck-typing when this code is embedded in a Firefox extension. 193 * This is required since an Error thrown in one component and then reported 194 * to another will fail instanceof checks in the second component. 195 * @type {boolean} 196 */ 197 bot.Error.prototype.isAutomationError = true; 198 199 200 if (goog.DEBUG) { 201 /** @return {string} The string representation of this error. */ 202 bot.Error.prototype.toString = function() { 203 return this.name + ': ' + this.message; 204 }; 205 } lib/atoms/json.js
1 // Copyright 2012 WebDriver committers 2 // Copyright 2012 Google Inc. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview Provides JSON utilities that uses native JSON parsing where 18 * possible (a feature not currently offered by Closure). 19 */ 20 21 goog.provide('bot.json'); 22 23 goog.require('bot.userAgent'); 24 goog.require('goog.json'); 25 goog.require('goog.userAgent'); 26 27 28 /** 29 * @define {boolean} NATIVE_JSON indicates whether the code should rely on the 30 * native {@code JSON} functions, if available. 31 * 32 * <p>The JSON functions can be defined by external libraries like Prototype 33 * and setting this flag to false forces the use of Closure's goog.json 34 * implementation. 35 * 36 * <p>If your JavaScript can be loaded by a third_party site and you are wary 37 * about relying on the native functions, specify 38 * "--define bot.json.NATIVE_JSON=false" to the Closure compiler. 39 */ 40 bot.json.NATIVE_JSON = true; 41 42 43 /** 44 * Whether the current browser supports the native JSON interface. 45 * @const 46 * @see http://caniuse.com/#search=JSON 47 * @private {boolean} 48 */ 49 bot.json.SUPPORTS_NATIVE_JSON_ = 50 // List WebKit and Opera first since every supported version of these 51 // browsers supports native JSON (and we can compile away large chunks of 52 // code for individual fragments by setting the appropriate compiler flags). 53 goog.userAgent.WEBKIT || goog.userAgent.OPERA || 54 (goog.userAgent.GECKO && bot.userAgent.isEngineVersion(3.5)) || 55 (goog.userAgent.IE && bot.userAgent.isEngineVersion(8)); 56 57 58 /** 59 * Converts a JSON object to its string representation. 60 * @param {*} jsonObj The input object. 61 * @param {?(function(string, *): *)=} opt_replacer A replacer function called 62 * for each (key, value) pair that determines how the value should be 63 * serialized. By default, this just returns the value and allows default 64 * serialization to kick in. 65 * @return {string} A JSON string representation of the input object. 66 */ 67 bot.json.stringify = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ? 68 JSON.stringify : goog.json.serialize; 69 70 71 /** 72 * Parses a JSON string and returns the result. 73 * @param {string} jsonStr The string to parse. 74 * @return {*} The JSON object. 75 * @throws {Error} If the input string is an invalid JSON string. 76 */ 77 bot.json.parse = bot.json.NATIVE_JSON && bot.json.SUPPORTS_NATIVE_JSON_ ? 78 JSON.parse : goog.json.parse; lib/atoms/response.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities for working with WebDriver response objects. 17 * @see: http://code.google.com/p/selenium/wiki/JsonWireProtocol#Responses 18 */ 19 20 goog.provide('bot.response'); 21 goog.provide('bot.response.ResponseObject'); 22 23 goog.require('bot.Error'); 24 goog.require('bot.ErrorCode'); 25 26 27 /** 28 * Type definition for a response object, as defined by the JSON wire protocol. 29 * @typedef {{status: bot.ErrorCode, value: (*|{message: string})}} 30 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Responses 31 */ 32 bot.response.ResponseObject; 33 34 35 /** 36 * @param {*} value The value to test. 37 * @return {boolean} Whether the given value is a response object. 38 */ 39 bot.response.isResponseObject = function(value) { 40 return goog.isObject(value) && goog.isNumber(value['status']); 41 }; 42 43 44 /** 45 * Creates a new success response object with the provided value. 46 * @param {*} value The response value. 47 * @return {!bot.response.ResponseObject} The new response object. 48 */ 49 bot.response.createResponse = function(value) { 50 if (bot.response.isResponseObject(value)) { 51 return /** @type {!bot.response.ResponseObject} */ (value); 52 } 53 return { 54 'status': bot.ErrorCode.SUCCESS, 55 'value': value 56 }; 57 }; 58 59 60 /** 61 * Converts an error value into its JSON representation as defined by the 62 * WebDriver wire protocol. 63 * @param {(bot.Error|Error|*)} error The error value to convert. 64 * @return {!bot.response.ResponseObject} The new response object. 65 */ 66 bot.response.createErrorResponse = function(error) { 67 if (bot.response.isResponseObject(error)) { 68 return /** @type {!bot.response.ResponseObject} */ (error); 69 } 70 71 var statusCode = error && goog.isNumber(error.code) ? error.code : 72 bot.ErrorCode.UNKNOWN_ERROR; 73 return { 74 'status': /** @type {bot.ErrorCode} */ (statusCode), 75 'value': { 76 'message': (error && error.message || error) + '' 77 } 78 }; 79 }; 80 81 82 /** 83 * Checks that a response object does not specify an error as defined by the 84 * WebDriver wire protocol. If the response object defines an error, it will 85 * be thrown. Otherwise, the response will be returned as is. 86 * @param {!bot.response.ResponseObject} responseObj The response object to 87 * check. 88 * @return {!bot.response.ResponseObject} The checked response object. 89 * @throws {bot.Error} If the response describes an error. 90 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Failed_Commands 91 */ 92 bot.response.checkResponse = function(responseObj) { 93 var status = responseObj['status']; 94 if (status == bot.ErrorCode.SUCCESS) { 95 return responseObj; 96 } 97 98 // If status is not defined, assume an unknown error. 99 status = status || bot.ErrorCode.UNKNOWN_ERROR; 100 101 var value = responseObj['value']; 102 if (!value || !goog.isObject(value)) { 103 throw new bot.Error(status, value + ''); 104 } 105 106 throw new bot.Error(status, value['message'] + ''); 107 }; lib/atoms/userAgent.js
1 // Copyright 2011 WebDriver committers 2 // Copyright 2011 Google Inc. 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview Similar to goog.userAgent.isVersion, but with support for 18 * getting the version information when running in a firefox extension. 19 */ 20 goog.provide('bot.userAgent'); 21 22 goog.require('goog.string'); 23 goog.require('goog.userAgent'); 24 goog.require('goog.userAgent.product'); 25 goog.require('goog.userAgent.product.isVersion'); 26 27 28 /** 29 * Whether the rendering engine version of the current browser is equal to or 30 * greater than the given version. This implementation differs from 31 * goog.userAgent.isVersion in the following ways: 32 * <ol> 33 * <li>in a Firefox extension, tests the engine version through the XUL version 34 * comparator service, because no window.navigator object is available 35 * <li>in IE, compares the given version to the current documentMode 36 * </ol> 37 * 38 * @param {string|number} version The version number to check. 39 * @return {boolean} Whether the browser engine version is the same or higher 40 * than the given version. 41 */ 42 bot.userAgent.isEngineVersion = function(version) { 43 if (bot.userAgent.FIREFOX_EXTENSION) { 44 return bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_(version); 45 } else if (goog.userAgent.IE) { 46 return goog.string.compareVersions( 47 /** @type {number} */ (goog.userAgent.DOCUMENT_MODE), version) >= 0; 48 } else { 49 return goog.userAgent.isVersionOrHigher(version); 50 } 51 }; 52 53 54 /** 55 * Whether the product version of the current browser is equal to or greater 56 * than the given version. This implementation differs from 57 * goog.userAgent.product.isVersion in the following ways: 58 * <ol> 59 * <li>in a Firefox extension, tests the product version through the XUL version 60 * comparator service, because no window.navigator object is available 61 * <li>on Android, always compares to the version to the OS version 62 * </ol> 63 * 64 * @param {string|number} version The version number to check. 65 * @return {boolean} Whether the browser product version is the same or higher 66 * than the given version. 67 */ 68 bot.userAgent.isProductVersion = function(version) { 69 if (bot.userAgent.FIREFOX_EXTENSION) { 70 return bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_(version); 71 } else if (goog.userAgent.product.ANDROID) { 72 return goog.string.compareVersions( 73 bot.userAgent.ANDROID_VERSION_, version) >= 0; 74 } else { 75 return goog.userAgent.product.isVersion(version); 76 } 77 }; 78 79 80 /** 81 * When we are in a Firefox extension, this is a function that accepts a version 82 * and returns whether the version of Gecko we are on is the same or higher 83 * than the given version. When we are not in a Firefox extension, this is null. 84 * @private {(undefined|function((string|number)): boolean)} 85 */ 86 bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_; 87 88 89 /** 90 * When we are in a Firefox extension, this is a function that accepts a version 91 * and returns whether the version of Firefox we are on is the same or higher 92 * than the given version. When we are not in a Firefox extension, this is null. 93 * @private {(undefined|function((string|number)): boolean)} 94 */ 95 bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_; 96 97 98 /** 99 * Whether we are in a Firefox extension. 100 * 101 * @const 102 * @type {boolean} 103 */ 104 bot.userAgent.FIREFOX_EXTENSION = (function() { 105 // False if this browser is not a Gecko browser. 106 if (!goog.userAgent.GECKO) { 107 return false; 108 } 109 110 // False if this code isn't running in an extension. 111 var Components = goog.global.Components; 112 if (!Components) { 113 return false; 114 } 115 try { 116 if (!Components['classes']) { 117 return false; 118 } 119 } catch (e) { 120 return false; 121 } 122 123 // Populate the version checker functions. 124 var cc = Components['classes']; 125 var ci = Components['interfaces']; 126 var versionComparator = cc['@mozilla.org/xpcom/version-comparator;1'][ 127 'getService'](ci['nsIVersionComparator']); 128 var appInfo = cc['@mozilla.org/xre/app-info;1']['getService']( 129 ci['nsIXULAppInfo']); 130 var geckoVersion = appInfo['platformVersion']; 131 var firefoxVersion = appInfo['version']; 132 133 bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_ = function(version) { 134 return versionComparator.compare(geckoVersion, '' + version) >= 0; 135 }; 136 bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_ = function(version) { 137 return versionComparator.compare(firefoxVersion, '' + version) >= 0; 138 }; 139 140 return true; 141 })(); 142 143 144 /** 145 * Whether we are on IOS. 146 * 147 * @const 148 * @type {boolean} 149 */ 150 bot.userAgent.IOS = goog.userAgent.product.IPAD || 151 goog.userAgent.product.IPHONE; 152 153 154 /** 155 * Whether we are on a mobile browser. 156 * 157 * @const 158 * @type {boolean} 159 */ 160 bot.userAgent.MOBILE = bot.userAgent.IOS || goog.userAgent.product.ANDROID; 161 162 163 /** 164 * Android Operating System Version. 165 * @private {string} 166 * @const 167 */ 168 bot.userAgent.ANDROID_VERSION_ = (function() { 169 if (goog.userAgent.product.ANDROID) { 170 var userAgentString = goog.userAgent.getUserAgentString(); 171 var match = /Android\s+([0-9\.]+)/.exec(userAgentString); 172 return match ? match[1] : '0'; 173 } else { 174 return '0'; 175 } 176 })(); 177 178 179 /** 180 * Whether the current document is IE in a documentMode older than 8. 181 * @type {boolean} 182 * @const 183 */ 184 bot.userAgent.IE_DOC_PRE8 = goog.userAgent.IE && 185 !goog.userAgent.isDocumentModeOrHigher(8); 186 187 188 /** 189 * Whether the current document is IE in IE9 (or newer) standards mode. 190 * @type {boolean} 191 * @const 192 */ 193 bot.userAgent.IE_DOC_9 = goog.userAgent.isDocumentModeOrHigher(9); 194 195 196 /** 197 * Whether the current document is IE in a documentMode older than 9. 198 * @type {boolean} 199 * @const 200 */ 201 bot.userAgent.IE_DOC_PRE9 = goog.userAgent.IE && 202 !goog.userAgent.isDocumentModeOrHigher(9); 203 204 205 /** 206 * Whether the current document is IE in IE10 (or newer) standards mode. 207 * @type {boolean} 208 * @const 209 */ 210 bot.userAgent.IE_DOC_10 = goog.userAgent.isDocumentModeOrHigher(10); 211 212 213 /** 214 * Whether the current document is IE in a documentMode older than 10. 215 * @type {boolean} 216 * @const 217 */ 218 bot.userAgent.IE_DOC_PRE10 = goog.userAgent.IE && 219 !goog.userAgent.isDocumentModeOrHigher(10); 220 221 222 /** 223 * Whether the current browser is Android pre-gingerbread. 224 * @type {boolean} 225 * @const 226 */ 227 bot.userAgent.ANDROID_PRE_GINGERBREAD = goog.userAgent.product.ANDROID && 228 !bot.userAgent.isProductVersion(2.3); 229 230 231 /** 232 * Whether the current browser is Android pre-icecreamsandwich 233 * @type {boolean} 234 * @const 235 */ 236 bot.userAgent.ANDROID_PRE_ICECREAMSANDWICH = goog.userAgent.product.ANDROID && 237 !bot.userAgent.isProductVersion(4); 238 239 240 /** 241 * Whether the current browser is Safari 6. 242 * @type {boolean} 243 * @const 244 */ 245 bot.userAgent.SAFARI_6 = goog.userAgent.product.SAFARI && 246 bot.userAgent.isProductVersion(6); 247 248 249 /** 250 * Whether the current browser is Windows Phone. 251 * @type {boolean} 252 * @const 253 */ 254 bot.userAgent.WINDOWS_PHONE = goog.userAgent.IE && 255 goog.userAgent.getUserAgentString().indexOf('IEMobile') != -1; lib/goog/array/array.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities for manipulating arrays. 17 * 18 */ 19 20 21 goog.provide('goog.array'); 22 goog.provide('goog.array.ArrayLike'); 23 24 goog.require('goog.asserts'); 25 26 27 /** 28 * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should 29 * rely on Array.prototype functions, if available. 30 * 31 * The Array.prototype functions can be defined by external libraries like 32 * Prototype and setting this flag to false forces closure to use its own 33 * goog.array implementation. 34 * 35 * If your javascript can be loaded by a third party site and you are wary about 36 * relying on the prototype functions, specify 37 * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler. 38 * 39 * Setting goog.TRUSTED_SITE to false will automatically set 40 * NATIVE_ARRAY_PROTOTYPES to false. 41 */ 42 goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE); 43 44 45 /** 46 * @define {boolean} If true, JSCompiler will use the native implementation of 47 * array functions where appropriate (e.g., {@code Array#filter}) and remove the 48 * unused pure JS implementation. 49 */ 50 goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false); 51 52 53 /** 54 * @typedef {Array|NodeList|Arguments|{length: number}} 55 */ 56 goog.array.ArrayLike; 57 58 59 /** 60 * Returns the last element in an array without removing it. 61 * Same as goog.array.last. 62 * @param {Array.<T>|goog.array.ArrayLike} array The array. 63 * @return {T} Last item in array. 64 * @template T 65 */ 66 goog.array.peek = function(array) { 67 return array[array.length - 1]; 68 }; 69 70 71 /** 72 * Returns the last element in an array without removing it. 73 * Same as goog.array.peek. 74 * @param {Array.<T>|goog.array.ArrayLike} array The array. 75 * @return {T} Last item in array. 76 * @template T 77 */ 78 goog.array.last = goog.array.peek; 79 80 81 /** 82 * Reference to the original {@code Array.prototype}. 83 * @private 84 */ 85 goog.array.ARRAY_PROTOTYPE_ = Array.prototype; 86 87 88 // NOTE(arv): Since most of the array functions are generic it allows you to 89 // pass an array-like object. Strings have a length and are considered array- 90 // like. However, the 'in' operator does not work on strings so we cannot just 91 // use the array path even if the browser supports indexing into strings. We 92 // therefore end up splitting the string. 93 94 95 /** 96 * Returns the index of the first element of an array with a specified value, or 97 * -1 if the element is not present in the array. 98 * 99 * See {@link http://tinyurl.com/developer-mozilla-org-array-indexof} 100 * 101 * @param {Array.<T>|goog.array.ArrayLike} arr The array to be searched. 102 * @param {T} obj The object for which we are searching. 103 * @param {number=} opt_fromIndex The index at which to start the search. If 104 * omitted the search starts at index 0. 105 * @return {number} The index of the first matching array element. 106 * @template T 107 */ 108 goog.array.indexOf = goog.NATIVE_ARRAY_PROTOTYPES && 109 (goog.array.ASSUME_NATIVE_FUNCTIONS || 110 goog.array.ARRAY_PROTOTYPE_.indexOf) ? 111 function(arr, obj, opt_fromIndex) { 112 goog.asserts.assert(arr.length != null); 113 114 return goog.array.ARRAY_PROTOTYPE_.indexOf.call(arr, obj, opt_fromIndex); 115 } : 116 function(arr, obj, opt_fromIndex) { 117 var fromIndex = opt_fromIndex == null ? 118 0 : (opt_fromIndex < 0 ? 119 Math.max(0, arr.length + opt_fromIndex) : opt_fromIndex); 120 121 if (goog.isString(arr)) { 122 // Array.prototype.indexOf uses === so only strings should be found. 123 if (!goog.isString(obj) || obj.length != 1) { 124 return -1; 125 } 126 return arr.indexOf(obj, fromIndex); 127 } 128 129 for (var i = fromIndex; i < arr.length; i++) { 130 if (i in arr && arr[i] === obj) 131 return i; 132 } 133 return -1; 134 }; 135 136 137 /** 138 * Returns the index of the last element of an array with a specified value, or 139 * -1 if the element is not present in the array. 140 * 141 * See {@link http://tinyurl.com/developer-mozilla-org-array-lastindexof} 142 * 143 * @param {!Array.<T>|!goog.array.ArrayLike} arr The array to be searched. 144 * @param {T} obj The object for which we are searching. 145 * @param {?number=} opt_fromIndex The index at which to start the search. If 146 * omitted the search starts at the end of the array. 147 * @return {number} The index of the last matching array element. 148 * @template T 149 */ 150 goog.array.lastIndexOf = goog.NATIVE_ARRAY_PROTOTYPES && 151 (goog.array.ASSUME_NATIVE_FUNCTIONS || 152 goog.array.ARRAY_PROTOTYPE_.lastIndexOf) ? 153 function(arr, obj, opt_fromIndex) { 154 goog.asserts.assert(arr.length != null); 155 156 // Firefox treats undefined and null as 0 in the fromIndex argument which 157 // leads it to always return -1 158 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; 159 return goog.array.ARRAY_PROTOTYPE_.lastIndexOf.call(arr, obj, fromIndex); 160 } : 161 function(arr, obj, opt_fromIndex) { 162 var fromIndex = opt_fromIndex == null ? arr.length - 1 : opt_fromIndex; 163 164 if (fromIndex < 0) { 165 fromIndex = Math.max(0, arr.length + fromIndex); 166 } 167 168 if (goog.isString(arr)) { 169 // Array.prototype.lastIndexOf uses === so only strings should be found. 170 if (!goog.isString(obj) || obj.length != 1) { 171 return -1; 172 } 173 return arr.lastIndexOf(obj, fromIndex); 174 } 175 176 for (var i = fromIndex; i >= 0; i--) { 177 if (i in arr && arr[i] === obj) 178 return i; 179 } 180 return -1; 181 }; 182 183 184 /** 185 * Calls a function for each element in an array. Skips holes in the array. 186 * See {@link http://tinyurl.com/developer-mozilla-org-array-foreach} 187 * 188 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over 189 * which to iterate. 190 * @param {?function(this: S, T, number, ?): ?} f The function to call for every 191 * element. This function takes 3 arguments (the element, the index and the 192 * array). The return value is ignored. 193 * @param {S=} opt_obj The object to be used as the value of 'this' within f. 194 * @template T,S 195 */ 196 goog.array.forEach = goog.NATIVE_ARRAY_PROTOTYPES && 197 (goog.array.ASSUME_NATIVE_FUNCTIONS || 198 goog.array.ARRAY_PROTOTYPE_.forEach) ? 199 function(arr, f, opt_obj) { 200 goog.asserts.assert(arr.length != null); 201 202 goog.array.ARRAY_PROTOTYPE_.forEach.call(arr, f, opt_obj); 203 } : 204 function(arr, f, opt_obj) { 205 var l = arr.length; // must be fixed during loop... see docs 206 var arr2 = goog.isString(arr) ? arr.split('') : arr; 207 for (var i = 0; i < l; i++) { 208 if (i in arr2) { 209 f.call(opt_obj, arr2[i], i, arr); 210 } 211 } 212 }; 213 214 215 /** 216 * Calls a function for each element in an array, starting from the last 217 * element rather than the first. 218 * 219 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 220 * like object over which to iterate. 221 * @param {?function(this: S, T, number, ?): ?} f The function to call for every 222 * element. This function 223 * takes 3 arguments (the element, the index and the array). The return 224 * value is ignored. 225 * @param {S=} opt_obj The object to be used as the value of 'this' 226 * within f. 227 * @template T,S 228 */ 229 goog.array.forEachRight = function(arr, f, opt_obj) { 230 var l = arr.length; // must be fixed during loop... see docs 231 var arr2 = goog.isString(arr) ? arr.split('') : arr; 232 for (var i = l - 1; i >= 0; --i) { 233 if (i in arr2) { 234 f.call(opt_obj, arr2[i], i, arr); 235 } 236 } 237 }; 238 239 240 /** 241 * Calls a function for each element in an array, and if the function returns 242 * true adds the element to a new array. 243 * 244 * See {@link http://tinyurl.com/developer-mozilla-org-array-filter} 245 * 246 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 247 * like object over which to iterate. 248 * @param {?function(this:S, T, number, ?):boolean} f The function to call for 249 * every element. This function 250 * takes 3 arguments (the element, the index and the array) and must 251 * return a Boolean. If the return value is true the element is added to the 252 * result array. If it is false the element is not included. 253 * @param {S=} opt_obj The object to be used as the value of 'this' 254 * within f. 255 * @return {!Array.<T>} a new array in which only elements that passed the test 256 * are present. 257 * @template T,S 258 */ 259 goog.array.filter = goog.NATIVE_ARRAY_PROTOTYPES && 260 (goog.array.ASSUME_NATIVE_FUNCTIONS || 261 goog.array.ARRAY_PROTOTYPE_.filter) ? 262 function(arr, f, opt_obj) { 263 goog.asserts.assert(arr.length != null); 264 265 return goog.array.ARRAY_PROTOTYPE_.filter.call(arr, f, opt_obj); 266 } : 267 function(arr, f, opt_obj) { 268 var l = arr.length; // must be fixed during loop... see docs 269 var res = []; 270 var resLength = 0; 271 var arr2 = goog.isString(arr) ? arr.split('') : arr; 272 for (var i = 0; i < l; i++) { 273 if (i in arr2) { 274 var val = arr2[i]; // in case f mutates arr2 275 if (f.call(opt_obj, val, i, arr)) { 276 res[resLength++] = val; 277 } 278 } 279 } 280 return res; 281 }; 282 283 284 /** 285 * Calls a function for each element in an array and inserts the result into a 286 * new array. 287 * 288 * See {@link http://tinyurl.com/developer-mozilla-org-array-map} 289 * 290 * @param {Array.<VALUE>|goog.array.ArrayLike} arr Array or array like object 291 * over which to iterate. 292 * @param {function(this:THIS, VALUE, number, ?): RESULT} f The function to call 293 * for every element. This function takes 3 arguments (the element, 294 * the index and the array) and should return something. The result will be 295 * inserted into a new array. 296 * @param {THIS=} opt_obj The object to be used as the value of 'this' within f. 297 * @return {!Array.<RESULT>} a new array with the results from f. 298 * @template THIS, VALUE, RESULT 299 */ 300 goog.array.map = goog.NATIVE_ARRAY_PROTOTYPES && 301 (goog.array.ASSUME_NATIVE_FUNCTIONS || 302 goog.array.ARRAY_PROTOTYPE_.map) ? 303 function(arr, f, opt_obj) { 304 goog.asserts.assert(arr.length != null); 305 306 return goog.array.ARRAY_PROTOTYPE_.map.call(arr, f, opt_obj); 307 } : 308 function(arr, f, opt_obj) { 309 var l = arr.length; // must be fixed during loop... see docs 310 var res = new Array(l); 311 var arr2 = goog.isString(arr) ? arr.split('') : arr; 312 for (var i = 0; i < l; i++) { 313 if (i in arr2) { 314 res[i] = f.call(opt_obj, arr2[i], i, arr); 315 } 316 } 317 return res; 318 }; 319 320 321 /** 322 * Passes every element of an array into a function and accumulates the result. 323 * 324 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduce} 325 * 326 * For example: 327 * var a = [1, 2, 3, 4]; 328 * goog.array.reduce(a, function(r, v, i, arr) {return r + v;}, 0); 329 * returns 10 330 * 331 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 332 * like object over which to iterate. 333 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for 334 * every element. This function 335 * takes 4 arguments (the function's previous result or the initial value, 336 * the value of the current array element, the current array index, and the 337 * array itself) 338 * function(previousValue, currentValue, index, array). 339 * @param {?} val The initial value to pass into the function on the first call. 340 * @param {S=} opt_obj The object to be used as the value of 'this' 341 * within f. 342 * @return {R} Result of evaluating f repeatedly across the values of the array. 343 * @template T,S,R 344 */ 345 goog.array.reduce = goog.NATIVE_ARRAY_PROTOTYPES && 346 (goog.array.ASSUME_NATIVE_FUNCTIONS || 347 goog.array.ARRAY_PROTOTYPE_.reduce) ? 348 function(arr, f, val, opt_obj) { 349 goog.asserts.assert(arr.length != null); 350 if (opt_obj) { 351 f = goog.bind(f, opt_obj); 352 } 353 return goog.array.ARRAY_PROTOTYPE_.reduce.call(arr, f, val); 354 } : 355 function(arr, f, val, opt_obj) { 356 var rval = val; 357 goog.array.forEach(arr, function(val, index) { 358 rval = f.call(opt_obj, rval, val, index, arr); 359 }); 360 return rval; 361 }; 362 363 364 /** 365 * Passes every element of an array into a function and accumulates the result, 366 * starting from the last element and working towards the first. 367 * 368 * See {@link http://tinyurl.com/developer-mozilla-org-array-reduceright} 369 * 370 * For example: 371 * var a = ['a', 'b', 'c']; 372 * goog.array.reduceRight(a, function(r, v, i, arr) {return r + v;}, ''); 373 * returns 'cba' 374 * 375 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 376 * like object over which to iterate. 377 * @param {?function(this:S, R, T, number, ?) : R} f The function to call for 378 * every element. This function 379 * takes 4 arguments (the function's previous result or the initial value, 380 * the value of the current array element, the current array index, and the 381 * array itself) 382 * function(previousValue, currentValue, index, array). 383 * @param {?} val The initial value to pass into the function on the first call. 384 * @param {S=} opt_obj The object to be used as the value of 'this' 385 * within f. 386 * @return {R} Object returned as a result of evaluating f repeatedly across the 387 * values of the array. 388 * @template T,S,R 389 */ 390 goog.array.reduceRight = goog.NATIVE_ARRAY_PROTOTYPES && 391 (goog.array.ASSUME_NATIVE_FUNCTIONS || 392 goog.array.ARRAY_PROTOTYPE_.reduceRight) ? 393 function(arr, f, val, opt_obj) { 394 goog.asserts.assert(arr.length != null); 395 if (opt_obj) { 396 f = goog.bind(f, opt_obj); 397 } 398 return goog.array.ARRAY_PROTOTYPE_.reduceRight.call(arr, f, val); 399 } : 400 function(arr, f, val, opt_obj) { 401 var rval = val; 402 goog.array.forEachRight(arr, function(val, index) { 403 rval = f.call(opt_obj, rval, val, index, arr); 404 }); 405 return rval; 406 }; 407 408 409 /** 410 * Calls f for each element of an array. If any call returns true, some() 411 * returns true (without checking the remaining elements). If all calls 412 * return false, some() returns false. 413 * 414 * See {@link http://tinyurl.com/developer-mozilla-org-array-some} 415 * 416 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 417 * like object over which to iterate. 418 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for 419 * for every element. This function takes 3 arguments (the element, the 420 * index and the array) and should return a boolean. 421 * @param {S=} opt_obj The object to be used as the value of 'this' 422 * within f. 423 * @return {boolean} true if any element passes the test. 424 * @template T,S 425 */ 426 goog.array.some = goog.NATIVE_ARRAY_PROTOTYPES && 427 (goog.array.ASSUME_NATIVE_FUNCTIONS || 428 goog.array.ARRAY_PROTOTYPE_.some) ? 429 function(arr, f, opt_obj) { 430 goog.asserts.assert(arr.length != null); 431 432 return goog.array.ARRAY_PROTOTYPE_.some.call(arr, f, opt_obj); 433 } : 434 function(arr, f, opt_obj) { 435 var l = arr.length; // must be fixed during loop... see docs 436 var arr2 = goog.isString(arr) ? arr.split('') : arr; 437 for (var i = 0; i < l; i++) { 438 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { 439 return true; 440 } 441 } 442 return false; 443 }; 444 445 446 /** 447 * Call f for each element of an array. If all calls return true, every() 448 * returns true. If any call returns false, every() returns false and 449 * does not continue to check the remaining elements. 450 * 451 * See {@link http://tinyurl.com/developer-mozilla-org-array-every} 452 * 453 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 454 * like object over which to iterate. 455 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for 456 * for every element. This function takes 3 arguments (the element, the 457 * index and the array) and should return a boolean. 458 * @param {S=} opt_obj The object to be used as the value of 'this' 459 * within f. 460 * @return {boolean} false if any element fails the test. 461 * @template T,S 462 */ 463 goog.array.every = goog.NATIVE_ARRAY_PROTOTYPES && 464 (goog.array.ASSUME_NATIVE_FUNCTIONS || 465 goog.array.ARRAY_PROTOTYPE_.every) ? 466 function(arr, f, opt_obj) { 467 goog.asserts.assert(arr.length != null); 468 469 return goog.array.ARRAY_PROTOTYPE_.every.call(arr, f, opt_obj); 470 } : 471 function(arr, f, opt_obj) { 472 var l = arr.length; // must be fixed during loop... see docs 473 var arr2 = goog.isString(arr) ? arr.split('') : arr; 474 for (var i = 0; i < l; i++) { 475 if (i in arr2 && !f.call(opt_obj, arr2[i], i, arr)) { 476 return false; 477 } 478 } 479 return true; 480 }; 481 482 483 /** 484 * Counts the array elements that fulfill the predicate, i.e. for which the 485 * callback function returns true. Skips holes in the array. 486 * 487 * @param {!(Array.<T>|goog.array.ArrayLike)} arr Array or array like object 488 * over which to iterate. 489 * @param {function(this: S, T, number, ?): boolean} f The function to call for 490 * every element. Takes 3 arguments (the element, the index and the array). 491 * @param {S=} opt_obj The object to be used as the value of 'this' within f. 492 * @return {number} The number of the matching elements. 493 * @template T,S 494 */ 495 goog.array.count = function(arr, f, opt_obj) { 496 var count = 0; 497 goog.array.forEach(arr, function(element, index, arr) { 498 if (f.call(opt_obj, element, index, arr)) { 499 ++count; 500 } 501 }, opt_obj); 502 return count; 503 }; 504 505 506 /** 507 * Search an array for the first element that satisfies a given condition and 508 * return that element. 509 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 510 * like object over which to iterate. 511 * @param {?function(this:S, T, number, ?) : boolean} f The function to call 512 * for every element. This function takes 3 arguments (the element, the 513 * index and the array) and should return a boolean. 514 * @param {S=} opt_obj An optional "this" context for the function. 515 * @return {?T} The first array element that passes the test, or null if no 516 * element is found. 517 * @template T,S 518 */ 519 goog.array.find = function(arr, f, opt_obj) { 520 var i = goog.array.findIndex(arr, f, opt_obj); 521 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; 522 }; 523 524 525 /** 526 * Search an array for the first element that satisfies a given condition and 527 * return its index. 528 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 529 * like object over which to iterate. 530 * @param {?function(this:S, T, number, ?) : boolean} f The function to call for 531 * every element. This function 532 * takes 3 arguments (the element, the index and the array) and should 533 * return a boolean. 534 * @param {S=} opt_obj An optional "this" context for the function. 535 * @return {number} The index of the first array element that passes the test, 536 * or -1 if no element is found. 537 * @template T,S 538 */ 539 goog.array.findIndex = function(arr, f, opt_obj) { 540 var l = arr.length; // must be fixed during loop... see docs 541 var arr2 = goog.isString(arr) ? arr.split('') : arr; 542 for (var i = 0; i < l; i++) { 543 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { 544 return i; 545 } 546 } 547 return -1; 548 }; 549 550 551 /** 552 * Search an array (in reverse order) for the last element that satisfies a 553 * given condition and return that element. 554 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 555 * like object over which to iterate. 556 * @param {?function(this:S, T, number, ?) : boolean} f The function to call 557 * for every element. This function 558 * takes 3 arguments (the element, the index and the array) and should 559 * return a boolean. 560 * @param {S=} opt_obj An optional "this" context for the function. 561 * @return {?T} The last array element that passes the test, or null if no 562 * element is found. 563 * @template T,S 564 */ 565 goog.array.findRight = function(arr, f, opt_obj) { 566 var i = goog.array.findIndexRight(arr, f, opt_obj); 567 return i < 0 ? null : goog.isString(arr) ? arr.charAt(i) : arr[i]; 568 }; 569 570 571 /** 572 * Search an array (in reverse order) for the last element that satisfies a 573 * given condition and return its index. 574 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 575 * like object over which to iterate. 576 * @param {?function(this:S, T, number, ?) : boolean} f The function to call 577 * for every element. This function 578 * takes 3 arguments (the element, the index and the array) and should 579 * return a boolean. 580 * @param {Object=} opt_obj An optional "this" context for the function. 581 * @return {number} The index of the last array element that passes the test, 582 * or -1 if no element is found. 583 * @template T,S 584 */ 585 goog.array.findIndexRight = function(arr, f, opt_obj) { 586 var l = arr.length; // must be fixed during loop... see docs 587 var arr2 = goog.isString(arr) ? arr.split('') : arr; 588 for (var i = l - 1; i >= 0; i--) { 589 if (i in arr2 && f.call(opt_obj, arr2[i], i, arr)) { 590 return i; 591 } 592 } 593 return -1; 594 }; 595 596 597 /** 598 * Whether the array contains the given object. 599 * @param {goog.array.ArrayLike} arr The array to test for the presence of the 600 * element. 601 * @param {*} obj The object for which to test. 602 * @return {boolean} true if obj is present. 603 */ 604 goog.array.contains = function(arr, obj) { 605 return goog.array.indexOf(arr, obj) >= 0; 606 }; 607 608 609 /** 610 * Whether the array is empty. 611 * @param {goog.array.ArrayLike} arr The array to test. 612 * @return {boolean} true if empty. 613 */ 614 goog.array.isEmpty = function(arr) { 615 return arr.length == 0; 616 }; 617 618 619 /** 620 * Clears the array. 621 * @param {goog.array.ArrayLike} arr Array or array like object to clear. 622 */ 623 goog.array.clear = function(arr) { 624 // For non real arrays we don't have the magic length so we delete the 625 // indices. 626 if (!goog.isArray(arr)) { 627 for (var i = arr.length - 1; i >= 0; i--) { 628 delete arr[i]; 629 } 630 } 631 arr.length = 0; 632 }; 633 634 635 /** 636 * Pushes an item into an array, if it's not already in the array. 637 * @param {Array.<T>} arr Array into which to insert the item. 638 * @param {T} obj Value to add. 639 * @template T 640 */ 641 goog.array.insert = function(arr, obj) { 642 if (!goog.array.contains(arr, obj)) { 643 arr.push(obj); 644 } 645 }; 646 647 648 /** 649 * Inserts an object at the given index of the array. 650 * @param {goog.array.ArrayLike} arr The array to modify. 651 * @param {*} obj The object to insert. 652 * @param {number=} opt_i The index at which to insert the object. If omitted, 653 * treated as 0. A negative index is counted from the end of the array. 654 */ 655 goog.array.insertAt = function(arr, obj, opt_i) { 656 goog.array.splice(arr, opt_i, 0, obj); 657 }; 658 659 660 /** 661 * Inserts at the given index of the array, all elements of another array. 662 * @param {goog.array.ArrayLike} arr The array to modify. 663 * @param {goog.array.ArrayLike} elementsToAdd The array of elements to add. 664 * @param {number=} opt_i The index at which to insert the object. If omitted, 665 * treated as 0. A negative index is counted from the end of the array. 666 */ 667 goog.array.insertArrayAt = function(arr, elementsToAdd, opt_i) { 668 goog.partial(goog.array.splice, arr, opt_i, 0).apply(null, elementsToAdd); 669 }; 670 671 672 /** 673 * Inserts an object into an array before a specified object. 674 * @param {Array.<T>} arr The array to modify. 675 * @param {T} obj The object to insert. 676 * @param {T=} opt_obj2 The object before which obj should be inserted. If obj2 677 * is omitted or not found, obj is inserted at the end of the array. 678 * @template T 679 */ 680 goog.array.insertBefore = function(arr, obj, opt_obj2) { 681 var i; 682 if (arguments.length == 2 || (i = goog.array.indexOf(arr, opt_obj2)) < 0) { 683 arr.push(obj); 684 } else { 685 goog.array.insertAt(arr, obj, i); 686 } 687 }; 688 689 690 /** 691 * Removes the first occurrence of a particular value from an array. 692 * @param {Array.<T>|goog.array.ArrayLike} arr Array from which to remove 693 * value. 694 * @param {T} obj Object to remove. 695 * @return {boolean} True if an element was removed. 696 * @template T 697 */ 698 goog.array.remove = function(arr, obj) { 699 var i = goog.array.indexOf(arr, obj); 700 var rv; 701 if ((rv = i >= 0)) { 702 goog.array.removeAt(arr, i); 703 } 704 return rv; 705 }; 706 707 708 /** 709 * Removes from an array the element at index i 710 * @param {goog.array.ArrayLike} arr Array or array like object from which to 711 * remove value. 712 * @param {number} i The index to remove. 713 * @return {boolean} True if an element was removed. 714 */ 715 goog.array.removeAt = function(arr, i) { 716 goog.asserts.assert(arr.length != null); 717 718 // use generic form of splice 719 // splice returns the removed items and if successful the length of that 720 // will be 1 721 return goog.array.ARRAY_PROTOTYPE_.splice.call(arr, i, 1).length == 1; 722 }; 723 724 725 /** 726 * Removes the first value that satisfies the given condition. 727 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array 728 * like object over which to iterate. 729 * @param {?function(this:S, T, number, ?) : boolean} f The function to call 730 * for every element. This function 731 * takes 3 arguments (the element, the index and the array) and should 732 * return a boolean. 733 * @param {S=} opt_obj An optional "this" context for the function. 734 * @return {boolean} True if an element was removed. 735 * @template T,S 736 */ 737 goog.array.removeIf = function(arr, f, opt_obj) { 738 var i = goog.array.findIndex(arr, f, opt_obj); 739 if (i >= 0) { 740 goog.array.removeAt(arr, i); 741 return true; 742 } 743 return false; 744 }; 745 746 747 /** 748 * Returns a new array that is the result of joining the arguments. If arrays 749 * are passed then their items are added, however, if non-arrays are passed they 750 * will be added to the return array as is. 751 * 752 * Note that ArrayLike objects will be added as is, rather than having their 753 * items added. 754 * 755 * goog.array.concat([1, 2], [3, 4]) -> [1, 2, 3, 4] 756 * goog.array.concat(0, [1, 2]) -> [0, 1, 2] 757 * goog.array.concat([1, 2], null) -> [1, 2, null] 758 * 759 * There is bug in all current versions of IE (6, 7 and 8) where arrays created 760 * in an iframe become corrupted soon (not immediately) after the iframe is 761 * destroyed. This is common if loading data via goog.net.IframeIo, for example. 762 * This corruption only affects the concat method which will start throwing 763 * Catastrophic Errors (#-2147418113). 764 * 765 * See http://endoflow.com/scratch/corrupted-arrays.html for a test case. 766 * 767 * Internally goog.array should use this, so that all methods will continue to 768 * work on these broken array objects. 769 * 770 * @param {...*} var_args Items to concatenate. Arrays will have each item 771 * added, while primitives and objects will be added as is. 772 * @return {!Array} The new resultant array. 773 */ 774 goog.array.concat = function(var_args) { 775 return goog.array.ARRAY_PROTOTYPE_.concat.apply( 776 goog.array.ARRAY_PROTOTYPE_, arguments); 777 }; 778 779 780 /** 781 * Returns a new array that contains the contents of all the arrays passed. 782 * @param {...!Array.<T>} var_args 783 * @return {!Array.<T>} 784 * @template T 785 */ 786 goog.array.join = function(var_args) { 787 return goog.array.ARRAY_PROTOTYPE_.concat.apply( 788 goog.array.ARRAY_PROTOTYPE_, arguments); 789 }; 790 791 792 /** 793 * Converts an object to an array. 794 * @param {Array.<T>|goog.array.ArrayLike} object The object to convert to an 795 * array. 796 * @return {!Array.<T>} The object converted into an array. If object has a 797 * length property, every property indexed with a non-negative number 798 * less than length will be included in the result. If object does not 799 * have a length property, an empty array will be returned. 800 * @template T 801 */ 802 goog.array.toArray = function(object) { 803 var length = object.length; 804 805 // If length is not a number the following it false. This case is kept for 806 // backwards compatibility since there are callers that pass objects that are 807 // not array like. 808 if (length > 0) { 809 var rv = new Array(length); 810 for (var i = 0; i < length; i++) { 811 rv[i] = object[i]; 812 } 813 return rv; 814 } 815 return []; 816 }; 817 818 819 /** 820 * Does a shallow copy of an array. 821 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array-like object to 822 * clone. 823 * @return {!Array.<T>} Clone of the input array. 824 * @template T 825 */ 826 goog.array.clone = goog.array.toArray; 827 828 829 /** 830 * Extends an array with another array, element, or "array like" object. 831 * This function operates 'in-place', it does not create a new Array. 832 * 833 * Example: 834 * var a = []; 835 * goog.array.extend(a, [0, 1]); 836 * a; // [0, 1] 837 * goog.array.extend(a, 2); 838 * a; // [0, 1, 2] 839 * 840 * @param {Array.<VALUE>} arr1 The array to modify. 841 * @param {...(Array.<VALUE>|VALUE)} var_args The elements or arrays of elements 842 * to add to arr1. 843 * @template VALUE 844 */ 845 goog.array.extend = function(arr1, var_args) { 846 for (var i = 1; i < arguments.length; i++) { 847 var arr2 = arguments[i]; 848 // If we have an Array or an Arguments object we can just call push 849 // directly. 850 var isArrayLike; 851 if (goog.isArray(arr2) || 852 // Detect Arguments. ES5 says that the [[Class]] of an Arguments object 853 // is "Arguments" but only V8 and JSC/Safari gets this right. We instead 854 // detect Arguments by checking for array like and presence of "callee". 855 (isArrayLike = goog.isArrayLike(arr2)) && 856 // The getter for callee throws an exception in strict mode 857 // according to section 10.6 in ES5 so check for presence instead. 858 Object.prototype.hasOwnProperty.call(arr2, 'callee')) { 859 arr1.push.apply(arr1, arr2); 860 } else if (isArrayLike) { 861 // Otherwise loop over arr2 to prevent copying the object. 862 var len1 = arr1.length; 863 var len2 = arr2.length; 864 for (var j = 0; j < len2; j++) { 865 arr1[len1 + j] = arr2[j]; 866 } 867 } else { 868 arr1.push(arr2); 869 } 870 } 871 }; 872 873 874 /** 875 * Adds or removes elements from an array. This is a generic version of Array 876 * splice. This means that it might work on other objects similar to arrays, 877 * such as the arguments object. 878 * 879 * @param {Array.<T>|goog.array.ArrayLike} arr The array to modify. 880 * @param {number|undefined} index The index at which to start changing the 881 * array. If not defined, treated as 0. 882 * @param {number} howMany How many elements to remove (0 means no removal. A 883 * value below 0 is treated as zero and so is any other non number. Numbers 884 * are floored). 885 * @param {...T} var_args Optional, additional elements to insert into the 886 * array. 887 * @return {!Array.<T>} the removed elements. 888 * @template T 889 */ 890 goog.array.splice = function(arr, index, howMany, var_args) { 891 goog.asserts.assert(arr.length != null); 892 893 return goog.array.ARRAY_PROTOTYPE_.splice.apply( 894 arr, goog.array.slice(arguments, 1)); 895 }; 896 897 898 /** 899 * Returns a new array from a segment of an array. This is a generic version of 900 * Array slice. This means that it might work on other objects similar to 901 * arrays, such as the arguments object. 902 * 903 * @param {Array.<T>|goog.array.ArrayLike} arr The array from 904 * which to copy a segment. 905 * @param {number} start The index of the first element to copy. 906 * @param {number=} opt_end The index after the last element to copy. 907 * @return {!Array.<T>} A new array containing the specified segment of the 908 * original array. 909 * @template T 910 */ 911 goog.array.slice = function(arr, start, opt_end) { 912 goog.asserts.assert(arr.length != null); 913 914 // passing 1 arg to slice is not the same as passing 2 where the second is 915 // null or undefined (in that case the second argument is treated as 0). 916 // we could use slice on the arguments object and then use apply instead of 917 // testing the length 918 if (arguments.length <= 2) { 919 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start); 920 } else { 921 return goog.array.ARRAY_PROTOTYPE_.slice.call(arr, start, opt_end); 922 } 923 }; 924 925 926 /** 927 * Removes all duplicates from an array (retaining only the first 928 * occurrence of each array element). This function modifies the 929 * array in place and doesn't change the order of the non-duplicate items. 930 * 931 * For objects, duplicates are identified as having the same unique ID as 932 * defined by {@link goog.getUid}. 933 * 934 * Alternatively you can specify a custom hash function that returns a unique 935 * value for each item in the array it should consider unique. 936 * 937 * Runtime: N, 938 * Worstcase space: 2N (no dupes) 939 * 940 * @param {Array.<T>|goog.array.ArrayLike} arr The array from which to remove 941 * duplicates. 942 * @param {Array=} opt_rv An optional array in which to return the results, 943 * instead of performing the removal inplace. If specified, the original 944 * array will remain unchanged. 945 * @param {function(T):string=} opt_hashFn An optional function to use to 946 * apply to every item in the array. This function should return a unique 947 * value for each item in the array it should consider unique. 948 * @template T 949 */ 950 goog.array.removeDuplicates = function(arr, opt_rv, opt_hashFn) { 951 var returnArray = opt_rv || arr; 952 var defaultHashFn = function(item) { 953 // Prefix each type with a single character representing the type to 954 // prevent conflicting keys (e.g. true and 'true'). 955 return goog.isObject(current) ? 'o' + goog.getUid(current) : 956 (typeof current).charAt(0) + current; 957 }; 958 var hashFn = opt_hashFn || defaultHashFn; 959 960 var seen = {}, cursorInsert = 0, cursorRead = 0; 961 while (cursorRead < arr.length) { 962 var current = arr[cursorRead++]; 963 var key = hashFn(current); 964 if (!Object.prototype.hasOwnProperty.call(seen, key)) { 965 seen[key] = true; 966 returnArray[cursorInsert++] = current; 967 } 968 } 969 returnArray.length = cursorInsert; 970 }; 971 972 973 /** 974 * Searches the specified array for the specified target using the binary 975 * search algorithm. If no opt_compareFn is specified, elements are compared 976 * using <code>goog.array.defaultCompare</code>, which compares the elements 977 * using the built in < and > operators. This will produce the expected 978 * behavior for homogeneous arrays of String(s) and Number(s). The array 979 * specified <b>must</b> be sorted in ascending order (as defined by the 980 * comparison function). If the array is not sorted, results are undefined. 981 * If the array contains multiple instances of the specified target value, any 982 * of these instances may be found. 983 * 984 * Runtime: O(log n) 985 * 986 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched. 987 * @param {TARGET} target The sought value. 988 * @param {function(TARGET, VALUE): number=} opt_compareFn Optional comparison 989 * function by which the array is ordered. Should take 2 arguments to 990 * compare, and return a negative number, zero, or a positive number 991 * depending on whether the first argument is less than, equal to, or 992 * greater than the second. 993 * @return {number} Lowest index of the target value if found, otherwise 994 * (-(insertion point) - 1). The insertion point is where the value should 995 * be inserted into arr to preserve the sorted property. Return value >= 0 996 * iff target is found. 997 * @template TARGET, VALUE 998 */ 999 goog.array.binarySearch = function(arr, target, opt_compareFn) { 1000 return goog.array.binarySearch_(arr, 1001 opt_compareFn || goog.array.defaultCompare, false /* isEvaluator */, 1002 target); 1003 }; 1004 1005 1006 /** 1007 * Selects an index in the specified array using the binary search algorithm. 1008 * The evaluator receives an element and determines whether the desired index 1009 * is before, at, or after it. The evaluator must be consistent (formally, 1010 * goog.array.map(goog.array.map(arr, evaluator, opt_obj), goog.math.sign) 1011 * must be monotonically non-increasing). 1012 * 1013 * Runtime: O(log n) 1014 * 1015 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched. 1016 * @param {function(this:THIS, VALUE, number, ?): number} evaluator 1017 * Evaluator function that receives 3 arguments (the element, the index and 1018 * the array). Should return a negative number, zero, or a positive number 1019 * depending on whether the desired index is before, at, or after the 1020 * element passed to it. 1021 * @param {THIS=} opt_obj The object to be used as the value of 'this' 1022 * within evaluator. 1023 * @return {number} Index of the leftmost element matched by the evaluator, if 1024 * such exists; otherwise (-(insertion point) - 1). The insertion point is 1025 * the index of the first element for which the evaluator returns negative, 1026 * or arr.length if no such element exists. The return value is non-negative 1027 * iff a match is found. 1028 * @template THIS, VALUE 1029 */ 1030 goog.array.binarySelect = function(arr, evaluator, opt_obj) { 1031 return goog.array.binarySearch_(arr, evaluator, true /* isEvaluator */, 1032 undefined /* opt_target */, opt_obj); 1033 }; 1034 1035 1036 /** 1037 * Implementation of a binary search algorithm which knows how to use both 1038 * comparison functions and evaluators. If an evaluator is provided, will call 1039 * the evaluator with the given optional data object, conforming to the 1040 * interface defined in binarySelect. Otherwise, if a comparison function is 1041 * provided, will call the comparison function against the given data object. 1042 * 1043 * This implementation purposefully does not use goog.bind or goog.partial for 1044 * performance reasons. 1045 * 1046 * Runtime: O(log n) 1047 * 1048 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to be searched. 1049 * @param {function(TARGET, VALUE): number| 1050 * function(this:THIS, VALUE, number, ?): number} compareFn Either an 1051 * evaluator or a comparison function, as defined by binarySearch 1052 * and binarySelect above. 1053 * @param {boolean} isEvaluator Whether the function is an evaluator or a 1054 * comparison function. 1055 * @param {TARGET=} opt_target If the function is a comparison function, then 1056 * this is the target to binary search for. 1057 * @param {THIS=} opt_selfObj If the function is an evaluator, this is an 1058 * optional this object for the evaluator. 1059 * @return {number} Lowest index of the target value if found, otherwise 1060 * (-(insertion point) - 1). The insertion point is where the value should 1061 * be inserted into arr to preserve the sorted property. Return value >= 0 1062 * iff target is found. 1063 * @template THIS, VALUE, TARGET 1064 * @private 1065 */ 1066 goog.array.binarySearch_ = function(arr, compareFn, isEvaluator, opt_target, 1067 opt_selfObj) { 1068 var left = 0; // inclusive 1069 var right = arr.length; // exclusive 1070 var found; 1071 while (left < right) { 1072 var middle = (left + right) >> 1; 1073 var compareResult; 1074 if (isEvaluator) { 1075 compareResult = compareFn.call(opt_selfObj, arr[middle], middle, arr); 1076 } else { 1077 compareResult = compareFn(opt_target, arr[middle]); 1078 } 1079 if (compareResult > 0) { 1080 left = middle + 1; 1081 } else { 1082 right = middle; 1083 // We are looking for the lowest index so we can't return immediately. 1084 found = !compareResult; 1085 } 1086 } 1087 // left is the index if found, or the insertion point otherwise. 1088 // ~left is a shorthand for -left - 1. 1089 return found ? left : ~left; 1090 }; 1091 1092 1093 /** 1094 * Sorts the specified array into ascending order. If no opt_compareFn is 1095 * specified, elements are compared using 1096 * <code>goog.array.defaultCompare</code>, which compares the elements using 1097 * the built in < and > operators. This will produce the expected behavior 1098 * for homogeneous arrays of String(s) and Number(s), unlike the native sort, 1099 * but will give unpredictable results for heterogenous lists of strings and 1100 * numbers with different numbers of digits. 1101 * 1102 * This sort is not guaranteed to be stable. 1103 * 1104 * Runtime: Same as <code>Array.prototype.sort</code> 1105 * 1106 * @param {Array.<T>} arr The array to be sorted. 1107 * @param {?function(T,T):number=} opt_compareFn Optional comparison 1108 * function by which the 1109 * array is to be ordered. Should take 2 arguments to compare, and return a 1110 * negative number, zero, or a positive number depending on whether the 1111 * first argument is less than, equal to, or greater than the second. 1112 * @template T 1113 */ 1114 goog.array.sort = function(arr, opt_compareFn) { 1115 // TODO(arv): Update type annotation since null is not accepted. 1116 arr.sort(opt_compareFn || goog.array.defaultCompare); 1117 }; 1118 1119 1120 /** 1121 * Sorts the specified array into ascending order in a stable way. If no 1122 * opt_compareFn is specified, elements are compared using 1123 * <code>goog.array.defaultCompare</code>, which compares the elements using 1124 * the built in < and > operators. This will produce the expected behavior 1125 * for homogeneous arrays of String(s) and Number(s). 1126 * 1127 * Runtime: Same as <code>Array.prototype.sort</code>, plus an additional 1128 * O(n) overhead of copying the array twice. 1129 * 1130 * @param {Array.<T>} arr The array to be sorted. 1131 * @param {?function(T, T): number=} opt_compareFn Optional comparison function 1132 * by which the array is to be ordered. Should take 2 arguments to compare, 1133 * and return a negative number, zero, or a positive number depending on 1134 * whether the first argument is less than, equal to, or greater than the 1135 * second. 1136 * @template T 1137 */ 1138 goog.array.stableSort = function(arr, opt_compareFn) { 1139 for (var i = 0; i < arr.length; i++) { 1140 arr[i] = {index: i, value: arr[i]}; 1141 } 1142 var valueCompareFn = opt_compareFn || goog.array.defaultCompare; 1143 function stableCompareFn(obj1, obj2) { 1144 return valueCompareFn(obj1.value, obj2.value) || obj1.index - obj2.index; 1145 }; 1146 goog.array.sort(arr, stableCompareFn); 1147 for (var i = 0; i < arr.length; i++) { 1148 arr[i] = arr[i].value; 1149 } 1150 }; 1151 1152 1153 /** 1154 * Sorts an array of objects by the specified object key and compare 1155 * function. If no compare function is provided, the key values are 1156 * compared in ascending order using <code>goog.array.defaultCompare</code>. 1157 * This won't work for keys that get renamed by the compiler. So use 1158 * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}. 1159 * @param {Array.<Object>} arr An array of objects to sort. 1160 * @param {string} key The object key to sort by. 1161 * @param {Function=} opt_compareFn The function to use to compare key 1162 * values. 1163 */ 1164 goog.array.sortObjectsByKey = function(arr, key, opt_compareFn) { 1165 var compare = opt_compareFn || goog.array.defaultCompare; 1166 goog.array.sort(arr, function(a, b) { 1167 return compare(a[key], b[key]); 1168 }); 1169 }; 1170 1171 1172 /** 1173 * Tells if the array is sorted. 1174 * @param {!Array.<T>} arr The array. 1175 * @param {?function(T,T):number=} opt_compareFn Function to compare the 1176 * array elements. 1177 * Should take 2 arguments to compare, and return a negative number, zero, 1178 * or a positive number depending on whether the first argument is less 1179 * than, equal to, or greater than the second. 1180 * @param {boolean=} opt_strict If true no equal elements are allowed. 1181 * @return {boolean} Whether the array is sorted. 1182 * @template T 1183 */ 1184 goog.array.isSorted = function(arr, opt_compareFn, opt_strict) { 1185 var compare = opt_compareFn || goog.array.defaultCompare; 1186 for (var i = 1; i < arr.length; i++) { 1187 var compareResult = compare(arr[i - 1], arr[i]); 1188 if (compareResult > 0 || compareResult == 0 && opt_strict) { 1189 return false; 1190 } 1191 } 1192 return true; 1193 }; 1194 1195 1196 /** 1197 * Compares two arrays for equality. Two arrays are considered equal if they 1198 * have the same length and their corresponding elements are equal according to 1199 * the comparison function. 1200 * 1201 * @param {goog.array.ArrayLike} arr1 The first array to compare. 1202 * @param {goog.array.ArrayLike} arr2 The second array to compare. 1203 * @param {Function=} opt_equalsFn Optional comparison function. 1204 * Should take 2 arguments to compare, and return true if the arguments 1205 * are equal. Defaults to {@link goog.array.defaultCompareEquality} which 1206 * compares the elements using the built-in '===' operator. 1207 * @return {boolean} Whether the two arrays are equal. 1208 */ 1209 goog.array.equals = function(arr1, arr2, opt_equalsFn) { 1210 if (!goog.isArrayLike(arr1) || !goog.isArrayLike(arr2) || 1211 arr1.length != arr2.length) { 1212 return false; 1213 } 1214 var l = arr1.length; 1215 var equalsFn = opt_equalsFn || goog.array.defaultCompareEquality; 1216 for (var i = 0; i < l; i++) { 1217 if (!equalsFn(arr1[i], arr2[i])) { 1218 return false; 1219 } 1220 } 1221 return true; 1222 }; 1223 1224 1225 /** 1226 * 3-way array compare function. 1227 * @param {!Array.<VALUE>|!goog.array.ArrayLike} arr1 The first array to 1228 * compare. 1229 * @param {!Array.<VALUE>|!goog.array.ArrayLike} arr2 The second array to 1230 * compare. 1231 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison 1232 * function by which the array is to be ordered. Should take 2 arguments to 1233 * compare, and return a negative number, zero, or a positive number 1234 * depending on whether the first argument is less than, equal to, or 1235 * greater than the second. 1236 * @return {number} Negative number, zero, or a positive number depending on 1237 * whether the first argument is less than, equal to, or greater than the 1238 * second. 1239 * @template VALUE 1240 */ 1241 goog.array.compare3 = function(arr1, arr2, opt_compareFn) { 1242 var compare = opt_compareFn || goog.array.defaultCompare; 1243 var l = Math.min(arr1.length, arr2.length); 1244 for (var i = 0; i < l; i++) { 1245 var result = compare(arr1[i], arr2[i]); 1246 if (result != 0) { 1247 return result; 1248 } 1249 } 1250 return goog.array.defaultCompare(arr1.length, arr2.length); 1251 }; 1252 1253 1254 /** 1255 * Compares its two arguments for order, using the built in < and > 1256 * operators. 1257 * @param {VALUE} a The first object to be compared. 1258 * @param {VALUE} b The second object to be compared. 1259 * @return {number} A negative number, zero, or a positive number as the first 1260 * argument is less than, equal to, or greater than the second. 1261 * @template VALUE 1262 */ 1263 goog.array.defaultCompare = function(a, b) { 1264 return a > b ? 1 : a < b ? -1 : 0; 1265 }; 1266 1267 1268 /** 1269 * Compares its two arguments for equality, using the built in === operator. 1270 * @param {*} a The first object to compare. 1271 * @param {*} b The second object to compare. 1272 * @return {boolean} True if the two arguments are equal, false otherwise. 1273 */ 1274 goog.array.defaultCompareEquality = function(a, b) { 1275 return a === b; 1276 }; 1277 1278 1279 /** 1280 * Inserts a value into a sorted array. The array is not modified if the 1281 * value is already present. 1282 * @param {Array.<VALUE>|goog.array.ArrayLike} array The array to modify. 1283 * @param {VALUE} value The object to insert. 1284 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison 1285 * function by which the array is ordered. Should take 2 arguments to 1286 * compare, and return a negative number, zero, or a positive number 1287 * depending on whether the first argument is less than, equal to, or 1288 * greater than the second. 1289 * @return {boolean} True if an element was inserted. 1290 * @template VALUE 1291 */ 1292 goog.array.binaryInsert = function(array, value, opt_compareFn) { 1293 var index = goog.array.binarySearch(array, value, opt_compareFn); 1294 if (index < 0) { 1295 goog.array.insertAt(array, value, -(index + 1)); 1296 return true; 1297 } 1298 return false; 1299 }; 1300 1301 1302 /** 1303 * Removes a value from a sorted array. 1304 * @param {!Array.<VALUE>|!goog.array.ArrayLike} array The array to modify. 1305 * @param {VALUE} value The object to remove. 1306 * @param {function(VALUE, VALUE): number=} opt_compareFn Optional comparison 1307 * function by which the array is ordered. Should take 2 arguments to 1308 * compare, and return a negative number, zero, or a positive number 1309 * depending on whether the first argument is less than, equal to, or 1310 * greater than the second. 1311 * @return {boolean} True if an element was removed. 1312 * @template VALUE 1313 */ 1314 goog.array.binaryRemove = function(array, value, opt_compareFn) { 1315 var index = goog.array.binarySearch(array, value, opt_compareFn); 1316 return (index >= 0) ? goog.array.removeAt(array, index) : false; 1317 }; 1318 1319 1320 /** 1321 * Splits an array into disjoint buckets according to a splitting function. 1322 * @param {Array.<T>} array The array. 1323 * @param {function(this:S, T,number,Array.<T>):?} sorter Function to call for 1324 * every element. This takes 3 arguments (the element, the index and the 1325 * array) and must return a valid object key (a string, number, etc), or 1326 * undefined, if that object should not be placed in a bucket. 1327 * @param {S=} opt_obj The object to be used as the value of 'this' within 1328 * sorter. 1329 * @return {!Object} An object, with keys being all of the unique return values 1330 * of sorter, and values being arrays containing the items for 1331 * which the splitter returned that key. 1332 * @template T,S 1333 */ 1334 goog.array.bucket = function(array, sorter, opt_obj) { 1335 var buckets = {}; 1336 1337 for (var i = 0; i < array.length; i++) { 1338 var value = array[i]; 1339 var key = sorter.call(opt_obj, value, i, array); 1340 if (goog.isDef(key)) { 1341 // Push the value to the right bucket, creating it if necessary. 1342 var bucket = buckets[key] || (buckets[key] = []); 1343 bucket.push(value); 1344 } 1345 } 1346 1347 return buckets; 1348 }; 1349 1350 1351 /** 1352 * Creates a new object built from the provided array and the key-generation 1353 * function. 1354 * @param {Array.<T>|goog.array.ArrayLike} arr Array or array like object over 1355 * which to iterate whose elements will be the values in the new object. 1356 * @param {?function(this:S, T, number, ?) : string} keyFunc The function to 1357 * call for every element. This function takes 3 arguments (the element, the 1358 * index and the array) and should return a string that will be used as the 1359 * key for the element in the new object. If the function returns the same 1360 * key for more than one element, the value for that key is 1361 * implementation-defined. 1362 * @param {S=} opt_obj The object to be used as the value of 'this' 1363 * within keyFunc. 1364 * @return {!Object.<T>} The new object. 1365 * @template T,S 1366 */ 1367 goog.array.toObject = function(arr, keyFunc, opt_obj) { 1368 var ret = {}; 1369 goog.array.forEach(arr, function(element, index) { 1370 ret[keyFunc.call(opt_obj, element, index, arr)] = element; 1371 }); 1372 return ret; 1373 }; 1374 1375 1376 /** 1377 * Creates a range of numbers in an arithmetic progression. 1378 * 1379 * Range takes 1, 2, or 3 arguments: 1380 * <pre> 1381 * range(5) is the same as range(0, 5, 1) and produces [0, 1, 2, 3, 4] 1382 * range(2, 5) is the same as range(2, 5, 1) and produces [2, 3, 4] 1383 * range(-2, -5, -1) produces [-2, -3, -4] 1384 * range(-2, -5, 1) produces [], since stepping by 1 wouldn't ever reach -5. 1385 * </pre> 1386 * 1387 * @param {number} startOrEnd The starting value of the range if an end argument 1388 * is provided. Otherwise, the start value is 0, and this is the end value. 1389 * @param {number=} opt_end The optional end value of the range. 1390 * @param {number=} opt_step The step size between range values. Defaults to 1 1391 * if opt_step is undefined or 0. 1392 * @return {!Array.<number>} An array of numbers for the requested range. May be 1393 * an empty array if adding the step would not converge toward the end 1394 * value. 1395 */ 1396 goog.array.range = function(startOrEnd, opt_end, opt_step) { 1397 var array = []; 1398 var start = 0; 1399 var end = startOrEnd; 1400 var step = opt_step || 1; 1401 if (opt_end !== undefined) { 1402 start = startOrEnd; 1403 end = opt_end; 1404 } 1405 1406 if (step * (end - start) < 0) { 1407 // Sign mismatch: start + step will never reach the end value. 1408 return []; 1409 } 1410 1411 if (step > 0) { 1412 for (var i = start; i < end; i += step) { 1413 array.push(i); 1414 } 1415 } else { 1416 for (var i = start; i > end; i += step) { 1417 array.push(i); 1418 } 1419 } 1420 return array; 1421 }; 1422 1423 1424 /** 1425 * Returns an array consisting of the given value repeated N times. 1426 * 1427 * @param {VALUE} value The value to repeat. 1428 * @param {number} n The repeat count. 1429 * @return {!Array.<VALUE>} An array with the repeated value. 1430 * @template VALUE 1431 */ 1432 goog.array.repeat = function(value, n) { 1433 var array = []; 1434 for (var i = 0; i < n; i++) { 1435 array[i] = value; 1436 } 1437 return array; 1438 }; 1439 1440 1441 /** 1442 * Returns an array consisting of every argument with all arrays 1443 * expanded in-place recursively. 1444 * 1445 * @param {...*} var_args The values to flatten. 1446 * @return {!Array} An array containing the flattened values. 1447 */ 1448 goog.array.flatten = function(var_args) { 1449 var result = []; 1450 for (var i = 0; i < arguments.length; i++) { 1451 var element = arguments[i]; 1452 if (goog.isArray(element)) { 1453 result.push.apply(result, goog.array.flatten.apply(null, element)); 1454 } else { 1455 result.push(element); 1456 } 1457 } 1458 return result; 1459 }; 1460 1461 1462 /** 1463 * Rotates an array in-place. After calling this method, the element at 1464 * index i will be the element previously at index (i - n) % 1465 * array.length, for all values of i between 0 and array.length - 1, 1466 * inclusive. 1467 * 1468 * For example, suppose list comprises [t, a, n, k, s]. After invoking 1469 * rotate(array, 1) (or rotate(array, -4)), array will comprise [s, t, a, n, k]. 1470 * 1471 * @param {!Array.<T>} array The array to rotate. 1472 * @param {number} n The amount to rotate. 1473 * @return {!Array.<T>} The array. 1474 * @template T 1475 */ 1476 goog.array.rotate = function(array, n) { 1477 goog.asserts.assert(array.length != null); 1478 1479 if (array.length) { 1480 n %= array.length; 1481 if (n > 0) { 1482 goog.array.ARRAY_PROTOTYPE_.unshift.apply(array, array.splice(-n, n)); 1483 } else if (n < 0) { 1484 goog.array.ARRAY_PROTOTYPE_.push.apply(array, array.splice(0, -n)); 1485 } 1486 } 1487 return array; 1488 }; 1489 1490 1491 /** 1492 * Moves one item of an array to a new position keeping the order of the rest 1493 * of the items. Example use case: keeping a list of JavaScript objects 1494 * synchronized with the corresponding list of DOM elements after one of the 1495 * elements has been dragged to a new position. 1496 * @param {!(Array|Arguments|{length:number})} arr The array to modify. 1497 * @param {number} fromIndex Index of the item to move between 0 and 1498 * {@code arr.length - 1}. 1499 * @param {number} toIndex Target index between 0 and {@code arr.length - 1}. 1500 */ 1501 goog.array.moveItem = function(arr, fromIndex, toIndex) { 1502 goog.asserts.assert(fromIndex >= 0 && fromIndex < arr.length); 1503 goog.asserts.assert(toIndex >= 0 && toIndex < arr.length); 1504 // Remove 1 item at fromIndex. 1505 var removedItems = goog.array.ARRAY_PROTOTYPE_.splice.call(arr, fromIndex, 1); 1506 // Insert the removed item at toIndex. 1507 goog.array.ARRAY_PROTOTYPE_.splice.call(arr, toIndex, 0, removedItems[0]); 1508 // We don't use goog.array.insertAt and goog.array.removeAt, because they're 1509 // significantly slower than splice. 1510 }; 1511 1512 1513 /** 1514 * Creates a new array for which the element at position i is an array of the 1515 * ith element of the provided arrays. The returned array will only be as long 1516 * as the shortest array provided; additional values are ignored. For example, 1517 * the result of zipping [1, 2] and [3, 4, 5] is [[1,3], [2, 4]]. 1518 * 1519 * This is similar to the zip() function in Python. See {@link 1520 * http://docs.python.org/library/functions.html#zip} 1521 * 1522 * @param {...!goog.array.ArrayLike} var_args Arrays to be combined. 1523 * @return {!Array.<!Array>} A new array of arrays created from provided arrays. 1524 */ 1525 goog.array.zip = function(var_args) { 1526 if (!arguments.length) { 1527 return []; 1528 } 1529 var result = []; 1530 for (var i = 0; true; i++) { 1531 var value = []; 1532 for (var j = 0; j < arguments.length; j++) { 1533 var arr = arguments[j]; 1534 // If i is larger than the array length, this is the shortest array. 1535 if (i >= arr.length) { 1536 return result; 1537 } 1538 value.push(arr[i]); 1539 } 1540 result.push(value); 1541 } 1542 }; 1543 1544 1545 /** 1546 * Shuffles the values in the specified array using the Fisher-Yates in-place 1547 * shuffle (also known as the Knuth Shuffle). By default, calls Math.random() 1548 * and so resets the state of that random number generator. Similarly, may reset 1549 * the state of the any other specified random number generator. 1550 * 1551 * Runtime: O(n) 1552 * 1553 * @param {!Array} arr The array to be shuffled. 1554 * @param {function():number=} opt_randFn Optional random function to use for 1555 * shuffling. 1556 * Takes no arguments, and returns a random number on the interval [0, 1). 1557 * Defaults to Math.random() using JavaScript's built-in Math library. 1558 */ 1559 goog.array.shuffle = function(arr, opt_randFn) { 1560 var randFn = opt_randFn || Math.random; 1561 1562 for (var i = arr.length - 1; i > 0; i--) { 1563 // Choose a random array index in [0, i] (inclusive with i). 1564 var j = Math.floor(randFn() * (i + 1)); 1565 1566 var tmp = arr[i]; 1567 arr[i] = arr[j]; 1568 arr[j] = tmp; 1569 } 1570 }; lib/goog/asserts/asserts.js
1 // Copyright 2008 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities to check the preconditions, postconditions and 17 * invariants runtime. 18 * 19 * Methods in this package should be given special treatment by the compiler 20 * for type-inference. For example, <code>goog.asserts.assert(foo)</code> 21 * will restrict <code>foo</code> to a truthy value. 22 * 23 * The compiler has an option to disable asserts. So code like: 24 * <code> 25 * var x = goog.asserts.assert(foo()); goog.asserts.assert(bar()); 26 * </code> 27 * will be transformed into: 28 * <code> 29 * var x = foo(); 30 * </code> 31 * The compiler will leave in foo() (because its return value is used), 32 * but it will remove bar() because it assumes it does not have side-effects. 33 * 34 */ 35 36 goog.provide('goog.asserts'); 37 goog.provide('goog.asserts.AssertionError'); 38 39 goog.require('goog.debug.Error'); 40 goog.require('goog.dom.NodeType'); 41 goog.require('goog.string'); 42 43 44 /** 45 * @define {boolean} Whether to strip out asserts or to leave them in. 46 */ 47 goog.define('goog.asserts.ENABLE_ASSERTS', goog.DEBUG); 48 49 50 51 /** 52 * Error object for failed assertions. 53 * @param {string} messagePattern The pattern that was used to form message. 54 * @param {!Array.<*>} messageArgs The items to substitute into the pattern. 55 * @constructor 56 * @extends {goog.debug.Error} 57 * @final 58 */ 59 goog.asserts.AssertionError = function(messagePattern, messageArgs) { 60 messageArgs.unshift(messagePattern); 61 goog.debug.Error.call(this, goog.string.subs.apply(null, messageArgs)); 62 // Remove the messagePattern afterwards to avoid permenantly modifying the 63 // passed in array. 64 messageArgs.shift(); 65 66 /** 67 * The message pattern used to format the error message. Error handlers can 68 * use this to uniquely identify the assertion. 69 * @type {string} 70 */ 71 this.messagePattern = messagePattern; 72 }; 73 goog.inherits(goog.asserts.AssertionError, goog.debug.Error); 74 75 76 /** @override */ 77 goog.asserts.AssertionError.prototype.name = 'AssertionError'; 78 79 80 /** 81 * Throws an exception with the given message and "Assertion failed" prefixed 82 * onto it. 83 * @param {string} defaultMessage The message to use if givenMessage is empty. 84 * @param {Array.<*>} defaultArgs The substitution arguments for defaultMessage. 85 * @param {string|undefined} givenMessage Message supplied by the caller. 86 * @param {Array.<*>} givenArgs The substitution arguments for givenMessage. 87 * @throws {goog.asserts.AssertionError} When the value is not a number. 88 * @private 89 */ 90 goog.asserts.doAssertFailure_ = 91 function(defaultMessage, defaultArgs, givenMessage, givenArgs) { 92 var message = 'Assertion failed'; 93 if (givenMessage) { 94 message += ': ' + givenMessage; 95 var args = givenArgs; 96 } else if (defaultMessage) { 97 message += ': ' + defaultMessage; 98 args = defaultArgs; 99 } 100 // The '' + works around an Opera 10 bug in the unit tests. Without it, 101 // a stack trace is added to var message above. With this, a stack trace is 102 // not added until this line (it causes the extra garbage to be added after 103 // the assertion message instead of in the middle of it). 104 throw new goog.asserts.AssertionError('' + message, args || []); 105 }; 106 107 108 /** 109 * Checks if the condition evaluates to true if goog.asserts.ENABLE_ASSERTS is 110 * true. 111 * @template T 112 * @param {T} condition The condition to check. 113 * @param {string=} opt_message Error message in case of failure. 114 * @param {...*} var_args The items to substitute into the failure message. 115 * @return {T} The value of the condition. 116 * @throws {goog.asserts.AssertionError} When the condition evaluates to false. 117 */ 118 goog.asserts.assert = function(condition, opt_message, var_args) { 119 if (goog.asserts.ENABLE_ASSERTS && !condition) { 120 goog.asserts.doAssertFailure_('', null, opt_message, 121 Array.prototype.slice.call(arguments, 2)); 122 } 123 return condition; 124 }; 125 126 127 /** 128 * Fails if goog.asserts.ENABLE_ASSERTS is true. This function is useful in case 129 * when we want to add a check in the unreachable area like switch-case 130 * statement: 131 * 132 * <pre> 133 * switch(type) { 134 * case FOO: doSomething(); break; 135 * case BAR: doSomethingElse(); break; 136 * default: goog.assert.fail('Unrecognized type: ' + type); 137 * // We have only 2 types - "default:" section is unreachable code. 138 * } 139 * </pre> 140 * 141 * @param {string=} opt_message Error message in case of failure. 142 * @param {...*} var_args The items to substitute into the failure message. 143 * @throws {goog.asserts.AssertionError} Failure. 144 */ 145 goog.asserts.fail = function(opt_message, var_args) { 146 if (goog.asserts.ENABLE_ASSERTS) { 147 throw new goog.asserts.AssertionError( 148 'Failure' + (opt_message ? ': ' + opt_message : ''), 149 Array.prototype.slice.call(arguments, 1)); 150 } 151 }; 152 153 154 /** 155 * Checks if the value is a number if goog.asserts.ENABLE_ASSERTS is true. 156 * @param {*} value The value to check. 157 * @param {string=} opt_message Error message in case of failure. 158 * @param {...*} var_args The items to substitute into the failure message. 159 * @return {number} The value, guaranteed to be a number when asserts enabled. 160 * @throws {goog.asserts.AssertionError} When the value is not a number. 161 */ 162 goog.asserts.assertNumber = function(value, opt_message, var_args) { 163 if (goog.asserts.ENABLE_ASSERTS && !goog.isNumber(value)) { 164 goog.asserts.doAssertFailure_('Expected number but got %s: %s.', 165 [goog.typeOf(value), value], opt_message, 166 Array.prototype.slice.call(arguments, 2)); 167 } 168 return /** @type {number} */ (value); 169 }; 170 171 172 /** 173 * Checks if the value is a string if goog.asserts.ENABLE_ASSERTS is true. 174 * @param {*} value The value to check. 175 * @param {string=} opt_message Error message in case of failure. 176 * @param {...*} var_args The items to substitute into the failure message. 177 * @return {string} The value, guaranteed to be a string when asserts enabled. 178 * @throws {goog.asserts.AssertionError} When the value is not a string. 179 */ 180 goog.asserts.assertString = function(value, opt_message, var_args) { 181 if (goog.asserts.ENABLE_ASSERTS && !goog.isString(value)) { 182 goog.asserts.doAssertFailure_('Expected string but got %s: %s.', 183 [goog.typeOf(value), value], opt_message, 184 Array.prototype.slice.call(arguments, 2)); 185 } 186 return /** @type {string} */ (value); 187 }; 188 189 190 /** 191 * Checks if the value is a function if goog.asserts.ENABLE_ASSERTS is true. 192 * @param {*} value The value to check. 193 * @param {string=} opt_message Error message in case of failure. 194 * @param {...*} var_args The items to substitute into the failure message. 195 * @return {!Function} The value, guaranteed to be a function when asserts 196 * enabled. 197 * @throws {goog.asserts.AssertionError} When the value is not a function. 198 */ 199 goog.asserts.assertFunction = function(value, opt_message, var_args) { 200 if (goog.asserts.ENABLE_ASSERTS && !goog.isFunction(value)) { 201 goog.asserts.doAssertFailure_('Expected function but got %s: %s.', 202 [goog.typeOf(value), value], opt_message, 203 Array.prototype.slice.call(arguments, 2)); 204 } 205 return /** @type {!Function} */ (value); 206 }; 207 208 209 /** 210 * Checks if the value is an Object if goog.asserts.ENABLE_ASSERTS is true. 211 * @param {*} value The value to check. 212 * @param {string=} opt_message Error message in case of failure. 213 * @param {...*} var_args The items to substitute into the failure message. 214 * @return {!Object} The value, guaranteed to be a non-null object. 215 * @throws {goog.asserts.AssertionError} When the value is not an object. 216 */ 217 goog.asserts.assertObject = function(value, opt_message, var_args) { 218 if (goog.asserts.ENABLE_ASSERTS && !goog.isObject(value)) { 219 goog.asserts.doAssertFailure_('Expected object but got %s: %s.', 220 [goog.typeOf(value), value], 221 opt_message, Array.prototype.slice.call(arguments, 2)); 222 } 223 return /** @type {!Object} */ (value); 224 }; 225 226 227 /** 228 * Checks if the value is an Array if goog.asserts.ENABLE_ASSERTS is true. 229 * @param {*} value The value to check. 230 * @param {string=} opt_message Error message in case of failure. 231 * @param {...*} var_args The items to substitute into the failure message. 232 * @return {!Array} The value, guaranteed to be a non-null array. 233 * @throws {goog.asserts.AssertionError} When the value is not an array. 234 */ 235 goog.asserts.assertArray = function(value, opt_message, var_args) { 236 if (goog.asserts.ENABLE_ASSERTS && !goog.isArray(value)) { 237 goog.asserts.doAssertFailure_('Expected array but got %s: %s.', 238 [goog.typeOf(value), value], opt_message, 239 Array.prototype.slice.call(arguments, 2)); 240 } 241 return /** @type {!Array} */ (value); 242 }; 243 244 245 /** 246 * Checks if the value is a boolean if goog.asserts.ENABLE_ASSERTS is true. 247 * @param {*} value The value to check. 248 * @param {string=} opt_message Error message in case of failure. 249 * @param {...*} var_args The items to substitute into the failure message. 250 * @return {boolean} The value, guaranteed to be a boolean when asserts are 251 * enabled. 252 * @throws {goog.asserts.AssertionError} When the value is not a boolean. 253 */ 254 goog.asserts.assertBoolean = function(value, opt_message, var_args) { 255 if (goog.asserts.ENABLE_ASSERTS && !goog.isBoolean(value)) { 256 goog.asserts.doAssertFailure_('Expected boolean but got %s: %s.', 257 [goog.typeOf(value), value], opt_message, 258 Array.prototype.slice.call(arguments, 2)); 259 } 260 return /** @type {boolean} */ (value); 261 }; 262 263 264 /** 265 * Checks if the value is a DOM Element if goog.asserts.ENABLE_ASSERTS is true. 266 * @param {*} value The value to check. 267 * @param {string=} opt_message Error message in case of failure. 268 * @param {...*} var_args The items to substitute into the failure message. 269 * @return {!Element} The value, likely to be a DOM Element when asserts are 270 * enabled. 271 * @throws {goog.asserts.AssertionError} When the value is not a boolean. 272 */ 273 goog.asserts.assertElement = function(value, opt_message, var_args) { 274 if (goog.asserts.ENABLE_ASSERTS && (!goog.isObject(value) || 275 value.nodeType != goog.dom.NodeType.ELEMENT)) { 276 goog.asserts.doAssertFailure_('Expected Element but got %s: %s.', 277 [goog.typeOf(value), value], opt_message, 278 Array.prototype.slice.call(arguments, 2)); 279 } 280 return /** @type {!Element} */ (value); 281 }; 282 283 284 /** 285 * Checks if the value is an instance of the user-defined type if 286 * goog.asserts.ENABLE_ASSERTS is true. 287 * 288 * The compiler may tighten the type returned by this function. 289 * 290 * @param {*} value The value to check. 291 * @param {function(new: T, ...)} type A user-defined constructor. 292 * @param {string=} opt_message Error message in case of failure. 293 * @param {...*} var_args The items to substitute into the failure message. 294 * @throws {goog.asserts.AssertionError} When the value is not an instance of 295 * type. 296 * @return {!T} 297 * @template T 298 */ 299 goog.asserts.assertInstanceof = function(value, type, opt_message, var_args) { 300 if (goog.asserts.ENABLE_ASSERTS && !(value instanceof type)) { 301 goog.asserts.doAssertFailure_('instanceof check failed.', null, 302 opt_message, Array.prototype.slice.call(arguments, 3)); 303 } 304 return value; 305 }; 306 307 308 /** 309 * Checks that no enumerable keys are present in Object.prototype. Such keys 310 * would break most code that use {@code for (var ... in ...)} loops. 311 */ 312 goog.asserts.assertObjectPrototypeIsIntact = function() { 313 for (var key in Object.prototype) { 314 goog.asserts.fail(key + ' should not be enumerable in Object.prototype.'); 315 } 316 }; lib/goog/base.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Bootstrap for the Google JS Library (Closure). 17 * 18 * In uncompiled mode base.js will write out Closure's deps file, unless the 19 * global <code>CLOSURE_NO_DEPS</code> is set to true. This allows projects to 20 * include their own deps file(s) from different locations. 21 * 22 * 23 * @provideGoog 24 */ 25 26 27 /** 28 * @define {boolean} Overridden to true by the compiler when --closure_pass 29 * or --mark_as_compiled is specified. 30 */ 31 var COMPILED = false; 32 33 34 /** 35 * Base namespace for the Closure library. Checks to see goog is already 36 * defined in the current scope before assigning to prevent clobbering if 37 * base.js is loaded more than once. 38 * 39 * @const 40 */ 41 var goog = goog || {}; 42 43 44 /** 45 * Reference to the global context. In most cases this will be 'window'. 46 */ 47 goog.global = this; 48 49 50 /** 51 * A hook for overriding the define values in uncompiled mode. 52 * 53 * In uncompiled mode, {@code CLOSURE_UNCOMPILED_DEFINES} may be defined before 54 * loading base.js. If a key is defined in {@code CLOSURE_UNCOMPILED_DEFINES}, 55 * {@code goog.define} will use the value instead of the default value. This 56 * allows flags to be overwritten without compilation (this is normally 57 * accomplished with the compiler's "define" flag). 58 * 59 * Example: 60 * <pre> 61 * var CLOSURE_UNCOMPILED_DEFINES = {'goog.DEBUG': false}; 62 * </pre> 63 * 64 * @type {Object.<string, (string|number|boolean)>|undefined} 65 */ 66 goog.global.CLOSURE_UNCOMPILED_DEFINES; 67 68 69 /** 70 * A hook for overriding the define values in uncompiled or compiled mode, 71 * like CLOSURE_UNCOMPILED_DEFINES but effective in compiled code. In 72 * uncompiled code CLOSURE_UNCOMPILED_DEFINES takes precedence. 73 * 74 * Also unlike CLOSURE_UNCOMPILED_DEFINES the values must be number, boolean or 75 * string literals or the compiler will emit an error. 76 * 77 * While any @define value may be set, only those set with goog.define will be 78 * effective for uncompiled code. 79 * 80 * Example: 81 * <pre> 82 * var CLOSURE_DEFINES = {'goog.DEBUG': false}; 83 * </pre> 84 * 85 * @type {Object.<string, (string|number|boolean)>|undefined} 86 */ 87 goog.global.CLOSURE_DEFINES; 88 89 90 /** 91 * Returns true if the specified value is not undefined. 92 * WARNING: Do not use this to test if an object has a property. Use the in 93 * operator instead. 94 * 95 * @param {?} val Variable to test. 96 * @return {boolean} Whether variable is defined. 97 */ 98 goog.isDef = function(val) { 99 // void 0 always evaluates to undefined and hence we do not need to depend on 100 // the definition of the global variable named 'undefined'. 101 return val !== void 0; 102 }; 103 104 105 /** 106 * Builds an object structure for the provided namespace path, ensuring that 107 * names that already exist are not overwritten. For example: 108 * "a.b.c" -> a = {};a.b={};a.b.c={}; 109 * Used by goog.provide and goog.exportSymbol. 110 * @param {string} name name of the object that this file defines. 111 * @param {*=} opt_object the object to expose at the end of the path. 112 * @param {Object=} opt_objectToExportTo The object to add the path to; default 113 * is |goog.global|. 114 * @private 115 */ 116 goog.exportPath_ = function(name, opt_object, opt_objectToExportTo) { 117 var parts = name.split('.'); 118 var cur = opt_objectToExportTo || goog.global; 119 120 // Internet Explorer exhibits strange behavior when throwing errors from 121 // methods externed in this manner. See the testExportSymbolExceptions in 122 // base_test.html for an example. 123 if (!(parts[0] in cur) && cur.execScript) { 124 cur.execScript('var ' + parts[0]); 125 } 126 127 // Certain browsers cannot parse code in the form for((a in b); c;); 128 // This pattern is produced by the JSCompiler when it collapses the 129 // statement above into the conditional loop below. To prevent this from 130 // happening, use a for-loop and reserve the init logic as below. 131 132 // Parentheses added to eliminate strict JS warning in Firefox. 133 for (var part; parts.length && (part = parts.shift());) { 134 if (!parts.length && goog.isDef(opt_object)) { 135 // last part and we have an object; use it 136 cur[part] = opt_object; 137 } else if (cur[part]) { 138 cur = cur[part]; 139 } else { 140 cur = cur[part] = {}; 141 } 142 } 143 }; 144 145 146 /** 147 * Defines a named value. In uncompiled mode, the value is retreived from 148 * CLOSURE_DEFINES or CLOSURE_UNCOMPILED_DEFINES if the object is defined and 149 * has the property specified, and otherwise used the defined defaultValue. 150 * When compiled, the default can be overridden using compiler command-line 151 * options. 152 * 153 * @param {string} name The distinguished name to provide. 154 * @param {string|number|boolean} defaultValue 155 */ 156 goog.define = function(name, defaultValue) { 157 var value = defaultValue; 158 if (!COMPILED) { 159 if (goog.global.CLOSURE_UNCOMPILED_DEFINES && 160 Object.prototype.hasOwnProperty.call( 161 goog.global.CLOSURE_UNCOMPILED_DEFINES, name)) { 162 value = goog.global.CLOSURE_UNCOMPILED_DEFINES[name]; 163 } else if (goog.global.CLOSURE_DEFINES && 164 Object.prototype.hasOwnProperty.call( 165 goog.global.CLOSURE_DEFINES, name)) { 166 value = goog.global.CLOSURE_DEFINES[name]; 167 } 168 } 169 goog.exportPath_(name, value); 170 }; 171 172 173 /** 174 * @define {boolean} DEBUG is provided as a convenience so that debugging code 175 * that should not be included in a production js_binary can be easily stripped 176 * by specifying --define goog.DEBUG=false to the JSCompiler. For example, most 177 * toString() methods should be declared inside an "if (goog.DEBUG)" conditional 178 * because they are generally used for debugging purposes and it is difficult 179 * for the JSCompiler to statically determine whether they are used. 180 */ 181 goog.DEBUG = true; 182 183 184 /** 185 * @define {string} LOCALE defines the locale being used for compilation. It is 186 * used to select locale specific data to be compiled in js binary. BUILD rule 187 * can specify this value by "--define goog.LOCALE=<locale_name>" as JSCompiler 188 * option. 189 * 190 * Take into account that the locale code format is important. You should use 191 * the canonical Unicode format with hyphen as a delimiter. Language must be 192 * lowercase, Language Script - Capitalized, Region - UPPERCASE. 193 * There are few examples: pt-BR, en, en-US, sr-Latin-BO, zh-Hans-CN. 194 * 195 * See more info about locale codes here: 196 * http://www.unicode.org/reports/tr35/#Unicode_Language_and_Locale_Identifiers 197 * 198 * For language codes you should use values defined by ISO 693-1. See it here 199 * http://www.w3.org/WAI/ER/IG/ert/iso639.htm. There is only one exception from 200 * this rule: the Hebrew language. For legacy reasons the old code (iw) should 201 * be used instead of the new code (he), see http://wiki/Main/IIISynonyms. 202 */ 203 goog.define('goog.LOCALE', 'en'); // default to en 204 205 206 /** 207 * @define {boolean} Whether this code is running on trusted sites. 208 * 209 * On untrusted sites, several native functions can be defined or overridden by 210 * external libraries like Prototype, Datejs, and JQuery and setting this flag 211 * to false forces closure to use its own implementations when possible. 212 * 213 * If your JavaScript can be loaded by a third party site and you are wary about 214 * relying on non-standard implementations, specify 215 * "--define goog.TRUSTED_SITE=false" to the JSCompiler. 216 */ 217 goog.define('goog.TRUSTED_SITE', true); 218 219 220 /** 221 * @define {boolean} Whether a project is expected to be running in strict mode. 222 * 223 * This define can be used to trigger alternate implementations compatible with 224 * running in EcmaScript Strict mode or warn about unavailable functionality. 225 * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions_and_function_scope/Strict_mode 226 */ 227 goog.define('goog.STRICT_MODE_COMPATIBLE', false); 228 229 230 /** 231 * Creates object stubs for a namespace. The presence of one or more 232 * goog.provide() calls indicate that the file defines the given 233 * objects/namespaces. Provided objects must not be null or undefined. 234 * Build tools also scan for provide/require statements 235 * to discern dependencies, build dependency files (see deps.js), etc. 236 * @see goog.require 237 * @param {string} name Namespace provided by this file in the form 238 * "goog.package.part". 239 */ 240 goog.provide = function(name) { 241 if (!COMPILED) { 242 // Ensure that the same namespace isn't provided twice. This is intended 243 // to teach new developers that 'goog.provide' is effectively a variable 244 // declaration. And when JSCompiler transforms goog.provide into a real 245 // variable declaration, the compiled JS should work the same as the raw 246 // JS--even when the raw JS uses goog.provide incorrectly. 247 if (goog.isProvided_(name)) { 248 throw Error('Namespace "' + name + '" already declared.'); 249 } 250 delete goog.implicitNamespaces_[name]; 251 252 var namespace = name; 253 while ((namespace = namespace.substring(0, namespace.lastIndexOf('.')))) { 254 if (goog.getObjectByName(namespace)) { 255 break; 256 } 257 goog.implicitNamespaces_[namespace] = true; 258 } 259 } 260 261 goog.exportPath_(name); 262 }; 263 264 265 /** 266 * Marks that the current file should only be used for testing, and never for 267 * live code in production. 268 * 269 * In the case of unit tests, the message may optionally be an exact namespace 270 * for the test (e.g. 'goog.stringTest'). The linter will then ignore the extra 271 * provide (if not explicitly defined in the code). 272 * 273 * @param {string=} opt_message Optional message to add to the error that's 274 * raised when used in production code. 275 */ 276 goog.setTestOnly = function(opt_message) { 277 if (COMPILED && !goog.DEBUG) { 278 opt_message = opt_message || ''; 279 throw Error('Importing test-only code into non-debug environment' + 280 opt_message ? ': ' + opt_message : '.'); 281 } 282 }; 283 284 285 /** 286 * Forward declares a symbol. This is an indication to the compiler that the 287 * symbol may be used in the source yet is not required and may not be provided 288 * in compilation. 289 * 290 * The most common usage of forward declaration is code that takes a type as a 291 * function parameter but does not need to require it. By forward declaring 292 * instead of requiring, no hard dependency is made, and (if not required 293 * elsewhere) the namespace may never be required and thus, not be pulled 294 * into the JavaScript binary. If it is required elsewhere, it will be type 295 * checked as normal. 296 * 297 * 298 * @param {string} name The namespace to forward declare in the form of 299 * "goog.package.part". 300 */ 301 goog.forwardDeclare = function(name) {}; 302 303 304 if (!COMPILED) { 305 306 /** 307 * Check if the given name has been goog.provided. This will return false for 308 * names that are available only as implicit namespaces. 309 * @param {string} name name of the object to look for. 310 * @return {boolean} Whether the name has been provided. 311 * @private 312 */ 313 goog.isProvided_ = function(name) { 314 return !goog.implicitNamespaces_[name] && 315 goog.isDefAndNotNull(goog.getObjectByName(name)); 316 }; 317 318 /** 319 * Namespaces implicitly defined by goog.provide. For example, 320 * goog.provide('goog.events.Event') implicitly declares that 'goog' and 321 * 'goog.events' must be namespaces. 322 * 323 * @type {Object} 324 * @private 325 */ 326 goog.implicitNamespaces_ = {}; 327 } 328 329 330 /** 331 * Returns an object based on its fully qualified external name. The object 332 * is not found if null or undefined. If you are using a compilation pass that 333 * renames property names beware that using this function will not find renamed 334 * properties. 335 * 336 * @param {string} name The fully qualified name. 337 * @param {Object=} opt_obj The object within which to look; default is 338 * |goog.global|. 339 * @return {?} The value (object or primitive) or, if not found, null. 340 */ 341 goog.getObjectByName = function(name, opt_obj) { 342 var parts = name.split('.'); 343 var cur = opt_obj || goog.global; 344 for (var part; part = parts.shift(); ) { 345 if (goog.isDefAndNotNull(cur[part])) { 346 cur = cur[part]; 347 } else { 348 return null; 349 } 350 } 351 return cur; 352 }; 353 354 355 /** 356 * Globalizes a whole namespace, such as goog or goog.lang. 357 * 358 * @param {Object} obj The namespace to globalize. 359 * @param {Object=} opt_global The object to add the properties to. 360 * @deprecated Properties may be explicitly exported to the global scope, but 361 * this should no longer be done in bulk. 362 */ 363 goog.globalize = function(obj, opt_global) { 364 var global = opt_global || goog.global; 365 for (var x in obj) { 366 global[x] = obj[x]; 367 } 368 }; 369 370 371 /** 372 * Adds a dependency from a file to the files it requires. 373 * @param {string} relPath The path to the js file. 374 * @param {Array} provides An array of strings with the names of the objects 375 * this file provides. 376 * @param {Array} requires An array of strings with the names of the objects 377 * this file requires. 378 */ 379 goog.addDependency = function(relPath, provides, requires) { 380 if (goog.DEPENDENCIES_ENABLED) { 381 var provide, require; 382 var path = relPath.replace(/\\/g, '/'); 383 var deps = goog.dependencies_; 384 for (var i = 0; provide = provides[i]; i++) { 385 deps.nameToPath[provide] = path; 386 if (!(path in deps.pathToNames)) { 387 deps.pathToNames[path] = {}; 388 } 389 deps.pathToNames[path][provide] = true; 390 } 391 for (var j = 0; require = requires[j]; j++) { 392 if (!(path in deps.requires)) { 393 deps.requires[path] = {}; 394 } 395 deps.requires[path][require] = true; 396 } 397 } 398 }; 399 400 401 402 403 // NOTE(nnaze): The debug DOM loader was included in base.js as an original way 404 // to do "debug-mode" development. The dependency system can sometimes be 405 // confusing, as can the debug DOM loader's asynchronous nature. 406 // 407 // With the DOM loader, a call to goog.require() is not blocking -- the script 408 // will not load until some point after the current script. If a namespace is 409 // needed at runtime, it needs to be defined in a previous script, or loaded via 410 // require() with its registered dependencies. 411 // User-defined namespaces may need their own deps file. See http://go/js_deps, 412 // http://go/genjsdeps, or, externally, DepsWriter. 413 // http://code.google.com/closure/library/docs/depswriter.html 414 // 415 // Because of legacy clients, the DOM loader can't be easily removed from 416 // base.js. Work is being done to make it disableable or replaceable for 417 // different environments (DOM-less JavaScript interpreters like Rhino or V8, 418 // for example). See bootstrap/ for more information. 419 420 421 /** 422 * @define {boolean} Whether to enable the debug loader. 423 * 424 * If enabled, a call to goog.require() will attempt to load the namespace by 425 * appending a script tag to the DOM (if the namespace has been registered). 426 * 427 * If disabled, goog.require() will simply assert that the namespace has been 428 * provided (and depend on the fact that some outside tool correctly ordered 429 * the script). 430 */ 431 goog.define('goog.ENABLE_DEBUG_LOADER', true); 432 433 434 /** 435 * Implements a system for the dynamic resolution of dependencies that works in 436 * parallel with the BUILD system. Note that all calls to goog.require will be 437 * stripped by the JSCompiler when the --closure_pass option is used. 438 * @see goog.provide 439 * @param {string} name Namespace to include (as was given in goog.provide()) in 440 * the form "goog.package.part". 441 */ 442 goog.require = function(name) { 443 444 // If the object already exists we do not need do do anything. 445 // TODO(arv): If we start to support require based on file name this has to 446 // change. 447 // TODO(arv): If we allow goog.foo.* this has to change. 448 // TODO(arv): If we implement dynamic load after page load we should probably 449 // not remove this code for the compiled output. 450 if (!COMPILED) { 451 if (goog.isProvided_(name)) { 452 return; 453 } 454 455 if (goog.ENABLE_DEBUG_LOADER) { 456 var path = goog.getPathFromDeps_(name); 457 if (path) { 458 goog.included_[path] = true; 459 goog.writeScripts_(); 460 return; 461 } 462 } 463 464 var errorMessage = 'goog.require could not find: ' + name; 465 if (goog.global.console) { 466 goog.global.console['error'](errorMessage); 467 } 468 469 470 throw Error(errorMessage); 471 472 } 473 }; 474 475 476 /** 477 * Path for included scripts. 478 * @type {string} 479 */ 480 goog.basePath = ''; 481 482 483 /** 484 * A hook for overriding the base path. 485 * @type {string|undefined} 486 */ 487 goog.global.CLOSURE_BASE_PATH; 488 489 490 /** 491 * Whether to write out Closure's deps file. By default, the deps are written. 492 * @type {boolean|undefined} 493 */ 494 goog.global.CLOSURE_NO_DEPS; 495 496 497 /** 498 * A function to import a single script. This is meant to be overridden when 499 * Closure is being run in non-HTML contexts, such as web workers. It's defined 500 * in the global scope so that it can be set before base.js is loaded, which 501 * allows deps.js to be imported properly. 502 * 503 * The function is passed the script source, which is a relative URI. It should 504 * return true if the script was imported, false otherwise. 505 * @type {(function(string): boolean)|undefined} 506 */ 507 goog.global.CLOSURE_IMPORT_SCRIPT; 508 509 510 /** 511 * Null function used for default values of callbacks, etc. 512 * @return {void} Nothing. 513 */ 514 goog.nullFunction = function() {}; 515 516 517 /** 518 * The identity function. Returns its first argument. 519 * 520 * @param {*=} opt_returnValue The single value that will be returned. 521 * @param {...*} var_args Optional trailing arguments. These are ignored. 522 * @return {?} The first argument. We can't know the type -- just pass it along 523 * without type. 524 * @deprecated Use goog.functions.identity instead. 525 */ 526 goog.identityFunction = function(opt_returnValue, var_args) { 527 return opt_returnValue; 528 }; 529 530 531 /** 532 * When defining a class Foo with an abstract method bar(), you can do: 533 * Foo.prototype.bar = goog.abstractMethod 534 * 535 * Now if a subclass of Foo fails to override bar(), an error will be thrown 536 * when bar() is invoked. 537 * 538 * Note: This does not take the name of the function to override as an argument 539 * because that would make it more difficult to obfuscate our JavaScript code. 540 * 541 * @type {!Function} 542 * @throws {Error} when invoked to indicate the method should be overridden. 543 */ 544 goog.abstractMethod = function() { 545 throw Error('unimplemented abstract method'); 546 }; 547 548 549 /** 550 * Adds a {@code getInstance} static method that always returns the same 551 * instance object. 552 * @param {!Function} ctor The constructor for the class to add the static 553 * method to. 554 */ 555 goog.addSingletonGetter = function(ctor) { 556 ctor.getInstance = function() { 557 if (ctor.instance_) { 558 return ctor.instance_; 559 } 560 if (goog.DEBUG) { 561 // NOTE: JSCompiler can't optimize away Array#push. 562 goog.instantiatedSingletons_[goog.instantiatedSingletons_.length] = ctor; 563 } 564 return ctor.instance_ = new ctor; 565 }; 566 }; 567 568 569 /** 570 * All singleton classes that have been instantiated, for testing. Don't read 571 * it directly, use the {@code goog.testing.singleton} module. The compiler 572 * removes this variable if unused. 573 * @type {!Array.<!Function>} 574 * @private 575 */ 576 goog.instantiatedSingletons_ = []; 577 578 579 /** 580 * True if goog.dependencies_ is available. 581 * @const {boolean} 582 */ 583 goog.DEPENDENCIES_ENABLED = !COMPILED && goog.ENABLE_DEBUG_LOADER; 584 585 586 if (goog.DEPENDENCIES_ENABLED) { 587 /** 588 * Object used to keep track of urls that have already been added. This record 589 * allows the prevention of circular dependencies. 590 * @type {Object} 591 * @private 592 */ 593 goog.included_ = {}; 594 595 596 /** 597 * This object is used to keep track of dependencies and other data that is 598 * used for loading scripts. 599 * @private 600 * @type {Object} 601 */ 602 goog.dependencies_ = { 603 pathToNames: {}, // 1 to many 604 nameToPath: {}, // 1 to 1 605 requires: {}, // 1 to many 606 // Used when resolving dependencies to prevent us from visiting file twice. 607 visited: {}, 608 written: {} // Used to keep track of script files we have written. 609 }; 610 611 612 /** 613 * Tries to detect whether is in the context of an HTML document. 614 * @return {boolean} True if it looks like HTML document. 615 * @private 616 */ 617 goog.inHtmlDocument_ = function() { 618 var doc = goog.global.document; 619 return typeof doc != 'undefined' && 620 'write' in doc; // XULDocument misses write. 621 }; 622 623 624 /** 625 * Tries to detect the base path of base.js script that bootstraps Closure. 626 * @private 627 */ 628 goog.findBasePath_ = function() { 629 if (goog.global.CLOSURE_BASE_PATH) { 630 goog.basePath = goog.global.CLOSURE_BASE_PATH; 631 return; 632 } else if (!goog.inHtmlDocument_()) { 633 return; 634 } 635 var doc = goog.global.document; 636 var scripts = doc.getElementsByTagName('script'); 637 // Search backwards since the current script is in almost all cases the one 638 // that has base.js. 639 for (var i = scripts.length - 1; i >= 0; --i) { 640 var src = scripts[i].src; 641 var qmark = src.lastIndexOf('?'); 642 var l = qmark == -1 ? src.length : qmark; 643 if (src.substr(l - 7, 7) == 'base.js') { 644 goog.basePath = src.substr(0, l - 7); 645 return; 646 } 647 } 648 }; 649 650 651 /** 652 * Imports a script if, and only if, that script hasn't already been imported. 653 * (Must be called at execution time) 654 * @param {string} src Script source. 655 * @private 656 */ 657 goog.importScript_ = function(src) { 658 var importScript = goog.global.CLOSURE_IMPORT_SCRIPT || 659 goog.writeScriptTag_; 660 if (!goog.dependencies_.written[src] && importScript(src)) { 661 goog.dependencies_.written[src] = true; 662 } 663 }; 664 665 666 /** 667 * The default implementation of the import function. Writes a script tag to 668 * import the script. 669 * 670 * @param {string} src The script source. 671 * @return {boolean} True if the script was imported, false otherwise. 672 * @private 673 */ 674 goog.writeScriptTag_ = function(src) { 675 if (goog.inHtmlDocument_()) { 676 var doc = goog.global.document; 677 678 // If the user tries to require a new symbol after document load, 679 // something has gone terribly wrong. Doing a document.write would 680 // wipe out the page. 681 if (doc.readyState == 'complete') { 682 // Certain test frameworks load base.js multiple times, which tries 683 // to write deps.js each time. If that happens, just fail silently. 684 // These frameworks wipe the page between each load of base.js, so this 685 // is OK. 686 var isDeps = /\bdeps.js$/.test(src); 687 if (isDeps) { 688 return false; 689 } else { 690 throw Error('Cannot write "' + src + '" after document load'); 691 } 692 } 693 694 doc.write( 695 '<script type="text/javascript" src="' + src + '"></' + 'script>'); 696 return true; 697 } else { 698 return false; 699 } 700 }; 701 702 703 /** 704 * Resolves dependencies based on the dependencies added using addDependency 705 * and calls importScript_ in the correct order. 706 * @private 707 */ 708 goog.writeScripts_ = function() { 709 // The scripts we need to write this time. 710 var scripts = []; 711 var seenScript = {}; 712 var deps = goog.dependencies_; 713 714 function visitNode(path) { 715 if (path in deps.written) { 716 return; 717 } 718 719 // We have already visited this one. We can get here if we have cyclic 720 // dependencies. 721 if (path in deps.visited) { 722 if (!(path in seenScript)) { 723 seenScript[path] = true; 724 scripts.push(path); 725 } 726 return; 727 } 728 729 deps.visited[path] = true; 730 731 if (path in deps.requires) { 732 for (var requireName in deps.requires[path]) { 733 // If the required name is defined, we assume that it was already 734 // bootstrapped by other means. 735 if (!goog.isProvided_(requireName)) { 736 if (requireName in deps.nameToPath) { 737 visitNode(deps.nameToPath[requireName]); 738 } else { 739 throw Error('Undefined nameToPath for ' + requireName); 740 } 741 } 742 } 743 } 744 745 if (!(path in seenScript)) { 746 seenScript[path] = true; 747 scripts.push(path); 748 } 749 } 750 751 for (var path in goog.included_) { 752 if (!deps.written[path]) { 753 visitNode(path); 754 } 755 } 756 757 for (var i = 0; i < scripts.length; i++) { 758 if (scripts[i]) { 759 goog.importScript_(goog.basePath + scripts[i]); 760 } else { 761 throw Error('Undefined script input'); 762 } 763 } 764 }; 765 766 767 /** 768 * Looks at the dependency rules and tries to determine the script file that 769 * fulfills a particular rule. 770 * @param {string} rule In the form goog.namespace.Class or project.script. 771 * @return {?string} Url corresponding to the rule, or null. 772 * @private 773 */ 774 goog.getPathFromDeps_ = function(rule) { 775 if (rule in goog.dependencies_.nameToPath) { 776 return goog.dependencies_.nameToPath[rule]; 777 } else { 778 return null; 779 } 780 }; 781 782 goog.findBasePath_(); 783 784 // Allow projects to manage the deps files themselves. 785 if (!goog.global.CLOSURE_NO_DEPS) { 786 goog.importScript_(goog.basePath + 'deps.js'); 787 } 788 } 789 790 791 792 //============================================================================== 793 // Language Enhancements 794 //============================================================================== 795 796 797 /** 798 * This is a "fixed" version of the typeof operator. It differs from the typeof 799 * operator in such a way that null returns 'null' and arrays return 'array'. 800 * @param {*} value The value to get the type of. 801 * @return {string} The name of the type. 802 */ 803 goog.typeOf = function(value) { 804 var s = typeof value; 805 if (s == 'object') { 806 if (value) { 807 // Check these first, so we can avoid calling Object.prototype.toString if 808 // possible. 809 // 810 // IE improperly marshals tyepof across execution contexts, but a 811 // cross-context object will still return false for "instanceof Object". 812 if (value instanceof Array) { 813 return 'array'; 814 } else if (value instanceof Object) { 815 return s; 816 } 817 818 // HACK: In order to use an Object prototype method on the arbitrary 819 // value, the compiler requires the value be cast to type Object, 820 // even though the ECMA spec explicitly allows it. 821 var className = Object.prototype.toString.call( 822 /** @type {Object} */ (value)); 823 // In Firefox 3.6, attempting to access iframe window objects' length 824 // property throws an NS_ERROR_FAILURE, so we need to special-case it 825 // here. 826 if (className == '[object Window]') { 827 return 'object'; 828 } 829 830 // We cannot always use constructor == Array or instanceof Array because 831 // different frames have different Array objects. In IE6, if the iframe 832 // where the array was created is destroyed, the array loses its 833 // prototype. Then dereferencing val.splice here throws an exception, so 834 // we can't use goog.isFunction. Calling typeof directly returns 'unknown' 835 // so that will work. In this case, this function will return false and 836 // most array functions will still work because the array is still 837 // array-like (supports length and []) even though it has lost its 838 // prototype. 839 // Mark Miller noticed that Object.prototype.toString 840 // allows access to the unforgeable [[Class]] property. 841 // 15.2.4.2 Object.prototype.toString ( ) 842 // When the toString method is called, the following steps are taken: 843 // 1. Get the [[Class]] property of this object. 844 // 2. Compute a string value by concatenating the three strings 845 // "[object ", Result(1), and "]". 846 // 3. Return Result(2). 847 // and this behavior survives the destruction of the execution context. 848 if ((className == '[object Array]' || 849 // In IE all non value types are wrapped as objects across window 850 // boundaries (not iframe though) so we have to do object detection 851 // for this edge case. 852 typeof value.length == 'number' && 853 typeof value.splice != 'undefined' && 854 typeof value.propertyIsEnumerable != 'undefined' && 855 !value.propertyIsEnumerable('splice') 856 857 )) { 858 return 'array'; 859 } 860 // HACK: There is still an array case that fails. 861 // function ArrayImpostor() {} 862 // ArrayImpostor.prototype = []; 863 // var impostor = new ArrayImpostor; 864 // this can be fixed by getting rid of the fast path 865 // (value instanceof Array) and solely relying on 866 // (value && Object.prototype.toString.vall(value) === '[object Array]') 867 // but that would require many more function calls and is not warranted 868 // unless closure code is receiving objects from untrusted sources. 869 870 // IE in cross-window calls does not correctly marshal the function type 871 // (it appears just as an object) so we cannot use just typeof val == 872 // 'function'. However, if the object has a call property, it is a 873 // function. 874 if ((className == '[object Function]' || 875 typeof value.call != 'undefined' && 876 typeof value.propertyIsEnumerable != 'undefined' && 877 !value.propertyIsEnumerable('call'))) { 878 return 'function'; 879 } 880 881 } else { 882 return 'null'; 883 } 884 885 } else if (s == 'function' && typeof value.call == 'undefined') { 886 // In Safari typeof nodeList returns 'function', and on Firefox typeof 887 // behaves similarly for HTML{Applet,Embed,Object}, Elements and RegExps. We 888 // would like to return object for those and we can detect an invalid 889 // function by making sure that the function object has a call method. 890 return 'object'; 891 } 892 return s; 893 }; 894 895 896 /** 897 * Returns true if the specified value is null. 898 * @param {?} val Variable to test. 899 * @return {boolean} Whether variable is null. 900 */ 901 goog.isNull = function(val) { 902 return val === null; 903 }; 904 905 906 /** 907 * Returns true if the specified value is defined and not null. 908 * @param {?} val Variable to test. 909 * @return {boolean} Whether variable is defined and not null. 910 */ 911 goog.isDefAndNotNull = function(val) { 912 // Note that undefined == null. 913 return val != null; 914 }; 915 916 917 /** 918 * Returns true if the specified value is an array. 919 * @param {?} val Variable to test. 920 * @return {boolean} Whether variable is an array. 921 */ 922 goog.isArray = function(val) { 923 return goog.typeOf(val) == 'array'; 924 }; 925 926 927 /** 928 * Returns true if the object looks like an array. To qualify as array like 929 * the value needs to be either a NodeList or an object with a Number length 930 * property. 931 * @param {?} val Variable to test. 932 * @return {boolean} Whether variable is an array. 933 */ 934 goog.isArrayLike = function(val) { 935 var type = goog.typeOf(val); 936 return type == 'array' || type == 'object' && typeof val.length == 'number'; 937 }; 938 939 940 /** 941 * Returns true if the object looks like a Date. To qualify as Date-like the 942 * value needs to be an object and have a getFullYear() function. 943 * @param {?} val Variable to test. 944 * @return {boolean} Whether variable is a like a Date. 945 */ 946 goog.isDateLike = function(val) { 947 return goog.isObject(val) && typeof val.getFullYear == 'function'; 948 }; 949 950 951 /** 952 * Returns true if the specified value is a string. 953 * @param {?} val Variable to test. 954 * @return {boolean} Whether variable is a string. 955 */ 956 goog.isString = function(val) { 957 return typeof val == 'string'; 958 }; 959 960 961 /** 962 * Returns true if the specified value is a boolean. 963 * @param {?} val Variable to test. 964 * @return {boolean} Whether variable is boolean. 965 */ 966 goog.isBoolean = function(val) { 967 return typeof val == 'boolean'; 968 }; 969 970 971 /** 972 * Returns true if the specified value is a number. 973 * @param {?} val Variable to test. 974 * @return {boolean} Whether variable is a number. 975 */ 976 goog.isNumber = function(val) { 977 return typeof val == 'number'; 978 }; 979 980 981 /** 982 * Returns true if the specified value is a function. 983 * @param {?} val Variable to test. 984 * @return {boolean} Whether variable is a function. 985 */ 986 goog.isFunction = function(val) { 987 return goog.typeOf(val) == 'function'; 988 }; 989 990 991 /** 992 * Returns true if the specified value is an object. This includes arrays and 993 * functions. 994 * @param {?} val Variable to test. 995 * @return {boolean} Whether variable is an object. 996 */ 997 goog.isObject = function(val) { 998 var type = typeof val; 999 return type == 'object' && val != null || type == 'function'; 1000 // return Object(val) === val also works, but is slower, especially if val is 1001 // not an object. 1002 }; 1003 1004 1005 /** 1006 * Gets a unique ID for an object. This mutates the object so that further calls 1007 * with the same object as a parameter returns the same value. The unique ID is 1008 * guaranteed to be unique across the current session amongst objects that are 1009 * passed into {@code getUid}. There is no guarantee that the ID is unique or 1010 * consistent across sessions. It is unsafe to generate unique ID for function 1011 * prototypes. 1012 * 1013 * @param {Object} obj The object to get the unique ID for. 1014 * @return {number} The unique ID for the object. 1015 */ 1016 goog.getUid = function(obj) { 1017 // TODO(arv): Make the type stricter, do not accept null. 1018 1019 // In Opera window.hasOwnProperty exists but always returns false so we avoid 1020 // using it. As a consequence the unique ID generated for BaseClass.prototype 1021 // and SubClass.prototype will be the same. 1022 return obj[goog.UID_PROPERTY_] || 1023 (obj[goog.UID_PROPERTY_] = ++goog.uidCounter_); 1024 }; 1025 1026 1027 /** 1028 * Whether the given object is alreay assigned a unique ID. 1029 * 1030 * This does not modify the object. 1031 * 1032 * @param {Object} obj The object to check. 1033 * @return {boolean} Whether there an assigned unique id for the object. 1034 */ 1035 goog.hasUid = function(obj) { 1036 return !!obj[goog.UID_PROPERTY_]; 1037 }; 1038 1039 1040 /** 1041 * Removes the unique ID from an object. This is useful if the object was 1042 * previously mutated using {@code goog.getUid} in which case the mutation is 1043 * undone. 1044 * @param {Object} obj The object to remove the unique ID field from. 1045 */ 1046 goog.removeUid = function(obj) { 1047 // TODO(arv): Make the type stricter, do not accept null. 1048 1049 // In IE, DOM nodes are not instances of Object and throw an exception if we 1050 // try to delete. Instead we try to use removeAttribute. 1051 if ('removeAttribute' in obj) { 1052 obj.removeAttribute(goog.UID_PROPERTY_); 1053 } 1054 /** @preserveTry */ 1055 try { 1056 delete obj[goog.UID_PROPERTY_]; 1057 } catch (ex) { 1058 } 1059 }; 1060 1061 1062 /** 1063 * Name for unique ID property. Initialized in a way to help avoid collisions 1064 * with other closure JavaScript on the same page. 1065 * @type {string} 1066 * @private 1067 */ 1068 goog.UID_PROPERTY_ = 'closure_uid_' + ((Math.random() * 1e9) >>> 0); 1069 1070 1071 /** 1072 * Counter for UID. 1073 * @type {number} 1074 * @private 1075 */ 1076 goog.uidCounter_ = 0; 1077 1078 1079 /** 1080 * Adds a hash code field to an object. The hash code is unique for the 1081 * given object. 1082 * @param {Object} obj The object to get the hash code for. 1083 * @return {number} The hash code for the object. 1084 * @deprecated Use goog.getUid instead. 1085 */ 1086 goog.getHashCode = goog.getUid; 1087 1088 1089 /** 1090 * Removes the hash code field from an object. 1091 * @param {Object} obj The object to remove the field from. 1092 * @deprecated Use goog.removeUid instead. 1093 */ 1094 goog.removeHashCode = goog.removeUid; 1095 1096 1097 /** 1098 * Clones a value. The input may be an Object, Array, or basic type. Objects and 1099 * arrays will be cloned recursively. 1100 * 1101 * WARNINGS: 1102 * <code>goog.cloneObject</code> does not detect reference loops. Objects that 1103 * refer to themselves will cause infinite recursion. 1104 * 1105 * <code>goog.cloneObject</code> is unaware of unique identifiers, and copies 1106 * UIDs created by <code>getUid</code> into cloned results. 1107 * 1108 * @param {*} obj The value to clone. 1109 * @return {*} A clone of the input value. 1110 * @deprecated goog.cloneObject is unsafe. Prefer the goog.object methods. 1111 */ 1112 goog.cloneObject = function(obj) { 1113 var type = goog.typeOf(obj); 1114 if (type == 'object' || type == 'array') { 1115 if (obj.clone) { 1116 return obj.clone(); 1117 } 1118 var clone = type == 'array' ? [] : {}; 1119 for (var key in obj) { 1120 clone[key] = goog.cloneObject(obj[key]); 1121 } 1122 return clone; 1123 } 1124 1125 return obj; 1126 }; 1127 1128 1129 /** 1130 * A native implementation of goog.bind. 1131 * @param {Function} fn A function to partially apply. 1132 * @param {Object|undefined} selfObj Specifies the object which this should 1133 * point to when the function is run. 1134 * @param {...*} var_args Additional arguments that are partially applied to the 1135 * function. 1136 * @return {!Function} A partially-applied form of the function bind() was 1137 * invoked as a method of. 1138 * @private 1139 * @suppress {deprecated} The compiler thinks that Function.prototype.bind is 1140 * deprecated because some people have declared a pure-JS version. 1141 * Only the pure-JS version is truly deprecated. 1142 */ 1143 goog.bindNative_ = function(fn, selfObj, var_args) { 1144 return /** @type {!Function} */ (fn.call.apply(fn.bind, arguments)); 1145 }; 1146 1147 1148 /** 1149 * A pure-JS implementation of goog.bind. 1150 * @param {Function} fn A function to partially apply. 1151 * @param {Object|undefined} selfObj Specifies the object which this should 1152 * point to when the function is run. 1153 * @param {...*} var_args Additional arguments that are partially applied to the 1154 * function. 1155 * @return {!Function} A partially-applied form of the function bind() was 1156 * invoked as a method of. 1157 * @private 1158 */ 1159 goog.bindJs_ = function(fn, selfObj, var_args) { 1160 if (!fn) { 1161 throw new Error(); 1162 } 1163 1164 if (arguments.length > 2) { 1165 var boundArgs = Array.prototype.slice.call(arguments, 2); 1166 return function() { 1167 // Prepend the bound arguments to the current arguments. 1168 var newArgs = Array.prototype.slice.call(arguments); 1169 Array.prototype.unshift.apply(newArgs, boundArgs); 1170 return fn.apply(selfObj, newArgs); 1171 }; 1172 1173 } else { 1174 return function() { 1175 return fn.apply(selfObj, arguments); 1176 }; 1177 } 1178 }; 1179 1180 1181 /** 1182 * Partially applies this function to a particular 'this object' and zero or 1183 * more arguments. The result is a new function with some arguments of the first 1184 * function pre-filled and the value of this 'pre-specified'. 1185 * 1186 * Remaining arguments specified at call-time are appended to the pre-specified 1187 * ones. 1188 * 1189 * Also see: {@link #partial}. 1190 * 1191 * Usage: 1192 * <pre>var barMethBound = bind(myFunction, myObj, 'arg1', 'arg2'); 1193 * barMethBound('arg3', 'arg4');</pre> 1194 * 1195 * @param {?function(this:T, ...)} fn A function to partially apply. 1196 * @param {T} selfObj Specifies the object which this should point to when the 1197 * function is run. 1198 * @param {...*} var_args Additional arguments that are partially applied to the 1199 * function. 1200 * @return {!Function} A partially-applied form of the function bind() was 1201 * invoked as a method of. 1202 * @template T 1203 * @suppress {deprecated} See above. 1204 */ 1205 goog.bind = function(fn, selfObj, var_args) { 1206 // TODO(nicksantos): narrow the type signature. 1207 if (Function.prototype.bind && 1208 // NOTE(nicksantos): Somebody pulled base.js into the default Chrome 1209 // extension environment. This means that for Chrome extensions, they get 1210 // the implementation of Function.prototype.bind that calls goog.bind 1211 // instead of the native one. Even worse, we don't want to introduce a 1212 // circular dependency between goog.bind and Function.prototype.bind, so 1213 // we have to hack this to make sure it works correctly. 1214 Function.prototype.bind.toString().indexOf('native code') != -1) { 1215 goog.bind = goog.bindNative_; 1216 } else { 1217 goog.bind = goog.bindJs_; 1218 } 1219 return goog.bind.apply(null, arguments); 1220 }; 1221 1222 1223 /** 1224 * Like bind(), except that a 'this object' is not required. Useful when the 1225 * target function is already bound. 1226 * 1227 * Usage: 1228 * var g = partial(f, arg1, arg2); 1229 * g(arg3, arg4); 1230 * 1231 * @param {Function} fn A function to partially apply. 1232 * @param {...*} var_args Additional arguments that are partially applied to fn. 1233 * @return {!Function} A partially-applied form of the function bind() was 1234 * invoked as a method of. 1235 */ 1236 goog.partial = function(fn, var_args) { 1237 var args = Array.prototype.slice.call(arguments, 1); 1238 return function() { 1239 // Clone the array (with slice()) and append additional arguments 1240 // to the existing arguments. 1241 var newArgs = args.slice(); 1242 newArgs.push.apply(newArgs, arguments); 1243 return fn.apply(this, newArgs); 1244 }; 1245 }; 1246 1247 1248 /** 1249 * Copies all the members of a source object to a target object. This method 1250 * does not work on all browsers for all objects that contain keys such as 1251 * toString or hasOwnProperty. Use goog.object.extend for this purpose. 1252 * @param {Object} target Target. 1253 * @param {Object} source Source. 1254 */ 1255 goog.mixin = function(target, source) { 1256 for (var x in source) { 1257 target[x] = source[x]; 1258 } 1259 1260 // For IE7 or lower, the for-in-loop does not contain any properties that are 1261 // not enumerable on the prototype object (for example, isPrototypeOf from 1262 // Object.prototype) but also it will not include 'replace' on objects that 1263 // extend String and change 'replace' (not that it is common for anyone to 1264 // extend anything except Object). 1265 }; 1266 1267 1268 /** 1269 * @return {number} An integer value representing the number of milliseconds 1270 * between midnight, January 1, 1970 and the current time. 1271 */ 1272 goog.now = (goog.TRUSTED_SITE && Date.now) || (function() { 1273 // Unary plus operator converts its operand to a number which in the case of 1274 // a date is done by calling getTime(). 1275 return +new Date(); 1276 }); 1277 1278 1279 /** 1280 * Evals JavaScript in the global scope. In IE this uses execScript, other 1281 * browsers use goog.global.eval. If goog.global.eval does not evaluate in the 1282 * global scope (for example, in Safari), appends a script tag instead. 1283 * Throws an exception if neither execScript or eval is defined. 1284 * @param {string} script JavaScript string. 1285 */ 1286 goog.globalEval = function(script) { 1287 if (goog.global.execScript) { 1288 goog.global.execScript(script, 'JavaScript'); 1289 } else if (goog.global.eval) { 1290 // Test to see if eval works 1291 if (goog.evalWorksForGlobals_ == null) { 1292 goog.global.eval('var _et_ = 1;'); 1293 if (typeof goog.global['_et_'] != 'undefined') { 1294 delete goog.global['_et_']; 1295 goog.evalWorksForGlobals_ = true; 1296 } else { 1297 goog.evalWorksForGlobals_ = false; 1298 } 1299 } 1300 1301 if (goog.evalWorksForGlobals_) { 1302 goog.global.eval(script); 1303 } else { 1304 var doc = goog.global.document; 1305 var scriptElt = doc.createElement('script'); 1306 scriptElt.type = 'text/javascript'; 1307 scriptElt.defer = false; 1308 // Note(user): can't use .innerHTML since "t('<test>')" will fail and 1309 // .text doesn't work in Safari 2. Therefore we append a text node. 1310 scriptElt.appendChild(doc.createTextNode(script)); 1311 doc.body.appendChild(scriptElt); 1312 doc.body.removeChild(scriptElt); 1313 } 1314 } else { 1315 throw Error('goog.globalEval not available'); 1316 } 1317 }; 1318 1319 1320 /** 1321 * Indicates whether or not we can call 'eval' directly to eval code in the 1322 * global scope. Set to a Boolean by the first call to goog.globalEval (which 1323 * empirically tests whether eval works for globals). @see goog.globalEval 1324 * @type {?boolean} 1325 * @private 1326 */ 1327 goog.evalWorksForGlobals_ = null; 1328 1329 1330 /** 1331 * Optional map of CSS class names to obfuscated names used with 1332 * goog.getCssName(). 1333 * @type {Object|undefined} 1334 * @private 1335 * @see goog.setCssNameMapping 1336 */ 1337 goog.cssNameMapping_; 1338 1339 1340 /** 1341 * Optional obfuscation style for CSS class names. Should be set to either 1342 * 'BY_WHOLE' or 'BY_PART' if defined. 1343 * @type {string|undefined} 1344 * @private 1345 * @see goog.setCssNameMapping 1346 */ 1347 goog.cssNameMappingStyle_; 1348 1349 1350 /** 1351 * Handles strings that are intended to be used as CSS class names. 1352 * 1353 * This function works in tandem with @see goog.setCssNameMapping. 1354 * 1355 * Without any mapping set, the arguments are simple joined with a hyphen and 1356 * passed through unaltered. 1357 * 1358 * When there is a mapping, there are two possible styles in which these 1359 * mappings are used. In the BY_PART style, each part (i.e. in between hyphens) 1360 * of the passed in css name is rewritten according to the map. In the BY_WHOLE 1361 * style, the full css name is looked up in the map directly. If a rewrite is 1362 * not specified by the map, the compiler will output a warning. 1363 * 1364 * When the mapping is passed to the compiler, it will replace calls to 1365 * goog.getCssName with the strings from the mapping, e.g. 1366 * var x = goog.getCssName('foo'); 1367 * var y = goog.getCssName(this.baseClass, 'active'); 1368 * becomes: 1369 * var x= 'foo'; 1370 * var y = this.baseClass + '-active'; 1371 * 1372 * If one argument is passed it will be processed, if two are passed only the 1373 * modifier will be processed, as it is assumed the first argument was generated 1374 * as a result of calling goog.getCssName. 1375 * 1376 * @param {string} className The class name. 1377 * @param {string=} opt_modifier A modifier to be appended to the class name. 1378 * @return {string} The class name or the concatenation of the class name and 1379 * the modifier. 1380 */ 1381 goog.getCssName = function(className, opt_modifier) { 1382 var getMapping = function(cssName) { 1383 return goog.cssNameMapping_[cssName] || cssName; 1384 }; 1385 1386 var renameByParts = function(cssName) { 1387 // Remap all the parts individually. 1388 var parts = cssName.split('-'); 1389 var mapped = []; 1390 for (var i = 0; i < parts.length; i++) { 1391 mapped.push(getMapping(parts[i])); 1392 } 1393 return mapped.join('-'); 1394 }; 1395 1396 var rename; 1397 if (goog.cssNameMapping_) { 1398 rename = goog.cssNameMappingStyle_ == 'BY_WHOLE' ? 1399 getMapping : renameByParts; 1400 } else { 1401 rename = function(a) { 1402 return a; 1403 }; 1404 } 1405 1406 if (opt_modifier) { 1407 return className + '-' + rename(opt_modifier); 1408 } else { 1409 return rename(className); 1410 } 1411 }; 1412 1413 1414 /** 1415 * Sets the map to check when returning a value from goog.getCssName(). Example: 1416 * <pre> 1417 * goog.setCssNameMapping({ 1418 * "goog": "a", 1419 * "disabled": "b", 1420 * }); 1421 * 1422 * var x = goog.getCssName('goog'); 1423 * // The following evaluates to: "a a-b". 1424 * goog.getCssName('goog') + ' ' + goog.getCssName(x, 'disabled') 1425 * </pre> 1426 * When declared as a map of string literals to string literals, the JSCompiler 1427 * will replace all calls to goog.getCssName() using the supplied map if the 1428 * --closure_pass flag is set. 1429 * 1430 * @param {!Object} mapping A map of strings to strings where keys are possible 1431 * arguments to goog.getCssName() and values are the corresponding values 1432 * that should be returned. 1433 * @param {string=} opt_style The style of css name mapping. There are two valid 1434 * options: 'BY_PART', and 'BY_WHOLE'. 1435 * @see goog.getCssName for a description. 1436 */ 1437 goog.setCssNameMapping = function(mapping, opt_style) { 1438 goog.cssNameMapping_ = mapping; 1439 goog.cssNameMappingStyle_ = opt_style; 1440 }; 1441 1442 1443 /** 1444 * To use CSS renaming in compiled mode, one of the input files should have a 1445 * call to goog.setCssNameMapping() with an object literal that the JSCompiler 1446 * can extract and use to replace all calls to goog.getCssName(). In uncompiled 1447 * mode, JavaScript code should be loaded before this base.js file that declares 1448 * a global variable, CLOSURE_CSS_NAME_MAPPING, which is used below. This is 1449 * to ensure that the mapping is loaded before any calls to goog.getCssName() 1450 * are made in uncompiled mode. 1451 * 1452 * A hook for overriding the CSS name mapping. 1453 * @type {Object|undefined} 1454 */ 1455 goog.global.CLOSURE_CSS_NAME_MAPPING; 1456 1457 1458 if (!COMPILED && goog.global.CLOSURE_CSS_NAME_MAPPING) { 1459 // This does not call goog.setCssNameMapping() because the JSCompiler 1460 // requires that goog.setCssNameMapping() be called with an object literal. 1461 goog.cssNameMapping_ = goog.global.CLOSURE_CSS_NAME_MAPPING; 1462 } 1463 1464 1465 /** 1466 * Gets a localized message. 1467 * 1468 * This function is a compiler primitive. If you give the compiler a localized 1469 * message bundle, it will replace the string at compile-time with a localized 1470 * version, and expand goog.getMsg call to a concatenated string. 1471 * 1472 * Messages must be initialized in the form: 1473 * <code> 1474 * var MSG_NAME = goog.getMsg('Hello {$placeholder}', {'placeholder': 'world'}); 1475 * </code> 1476 * 1477 * @param {string} str Translatable string, places holders in the form {$foo}. 1478 * @param {Object=} opt_values Map of place holder name to value. 1479 * @return {string} message with placeholders filled. 1480 */ 1481 goog.getMsg = function(str, opt_values) { 1482 var values = opt_values || {}; 1483 for (var key in values) { 1484 var value = ('' + values[key]).replace(/\$/g, '$$$$'); 1485 str = str.replace(new RegExp('\\{\\$' + key + '\\}', 'gi'), value); 1486 } 1487 return str; 1488 }; 1489 1490 1491 /** 1492 * Gets a localized message. If the message does not have a translation, gives a 1493 * fallback message. 1494 * 1495 * This is useful when introducing a new message that has not yet been 1496 * translated into all languages. 1497 * 1498 * This function is a compiler primitive. Must be used in the form: 1499 * <code>var x = goog.getMsgWithFallback(MSG_A, MSG_B);</code> 1500 * where MSG_A and MSG_B were initialized with goog.getMsg. 1501 * 1502 * @param {string} a The preferred message. 1503 * @param {string} b The fallback message. 1504 * @return {string} The best translated message. 1505 */ 1506 goog.getMsgWithFallback = function(a, b) { 1507 return a; 1508 }; 1509 1510 1511 /** 1512 * Exposes an unobfuscated global namespace path for the given object. 1513 * Note that fields of the exported object *will* be obfuscated, unless they are 1514 * exported in turn via this function or goog.exportProperty. 1515 * 1516 * Also handy for making public items that are defined in anonymous closures. 1517 * 1518 * ex. goog.exportSymbol('public.path.Foo', Foo); 1519 * 1520 * ex. goog.exportSymbol('public.path.Foo.staticFunction', Foo.staticFunction); 1521 * public.path.Foo.staticFunction(); 1522 * 1523 * ex. goog.exportSymbol('public.path.Foo.prototype.myMethod', 1524 * Foo.prototype.myMethod); 1525 * new public.path.Foo().myMethod(); 1526 * 1527 * @param {string} publicPath Unobfuscated name to export. 1528 * @param {*} object Object the name should point to. 1529 * @param {Object=} opt_objectToExportTo The object to add the path to; default 1530 * is goog.global. 1531 */ 1532 goog.exportSymbol = function(publicPath, object, opt_objectToExportTo) { 1533 goog.exportPath_(publicPath, object, opt_objectToExportTo); 1534 }; 1535 1536 1537 /** 1538 * Exports a property unobfuscated into the object's namespace. 1539 * ex. goog.exportProperty(Foo, 'staticFunction', Foo.staticFunction); 1540 * ex. goog.exportProperty(Foo.prototype, 'myMethod', Foo.prototype.myMethod); 1541 * @param {Object} object Object whose static property is being exported. 1542 * @param {string} publicName Unobfuscated name to export. 1543 * @param {*} symbol Object the name should point to. 1544 */ 1545 goog.exportProperty = function(object, publicName, symbol) { 1546 object[publicName] = symbol; 1547 }; 1548 1549 1550 /** 1551 * Inherit the prototype methods from one constructor into another. 1552 * 1553 * Usage: 1554 * <pre> 1555 * function ParentClass(a, b) { } 1556 * ParentClass.prototype.foo = function(a) { } 1557 * 1558 * function ChildClass(a, b, c) { 1559 * goog.base(this, a, b); 1560 * } 1561 * goog.inherits(ChildClass, ParentClass); 1562 * 1563 * var child = new ChildClass('a', 'b', 'see'); 1564 * child.foo(); // This works. 1565 * </pre> 1566 * 1567 * In addition, a superclass' implementation of a method can be invoked as 1568 * follows: 1569 * 1570 * <pre> 1571 * ChildClass.prototype.foo = function(a) { 1572 * ChildClass.superClass_.foo.call(this, a); 1573 * // Other code here. 1574 * }; 1575 * </pre> 1576 * 1577 * @param {Function} childCtor Child class. 1578 * @param {Function} parentCtor Parent class. 1579 */ 1580 goog.inherits = function(childCtor, parentCtor) { 1581 /** @constructor */ 1582 function tempCtor() {}; 1583 tempCtor.prototype = parentCtor.prototype; 1584 childCtor.superClass_ = parentCtor.prototype; 1585 childCtor.prototype = new tempCtor(); 1586 /** @override */ 1587 childCtor.prototype.constructor = childCtor; 1588 1589 /** 1590 * Calls superclass constructor/method. 1591 * 1592 * This function is only available if you use goog.inherits to 1593 * express inheritance relationships between classes. 1594 * 1595 * NOTE: This is a replacement for goog.base and for superClass_ 1596 * property defined in childCtor. 1597 * 1598 * @param {!Object} me Should always be "this". 1599 * @param {string} methodName The method name to call. Calling 1600 * superclass constructor can be done with the special string 1601 * 'constructor'. 1602 * @param {...*} var_args The arguments to pass to superclass 1603 * method/constructor. 1604 * @return {*} The return value of the superclass method/constructor. 1605 */ 1606 childCtor.base = function(me, methodName, var_args) { 1607 var args = Array.prototype.slice.call(arguments, 2); 1608 return parentCtor.prototype[methodName].apply(me, args); 1609 }; 1610 }; 1611 1612 1613 /** 1614 * Call up to the superclass. 1615 * 1616 * If this is called from a constructor, then this calls the superclass 1617 * constructor with arguments 1-N. 1618 * 1619 * If this is called from a prototype method, then you must pass the name of the 1620 * method as the second argument to this function. If you do not, you will get a 1621 * runtime error. This calls the superclass' method with arguments 2-N. 1622 * 1623 * This function only works if you use goog.inherits to express inheritance 1624 * relationships between your classes. 1625 * 1626 * This function is a compiler primitive. At compile-time, the compiler will do 1627 * macro expansion to remove a lot of the extra overhead that this function 1628 * introduces. The compiler will also enforce a lot of the assumptions that this 1629 * function makes, and treat it as a compiler error if you break them. 1630 * 1631 * @param {!Object} me Should always be "this". 1632 * @param {*=} opt_methodName The method name if calling a super method. 1633 * @param {...*} var_args The rest of the arguments. 1634 * @return {*} The return value of the superclass method. 1635 * @suppress {es5Strict} This method can not be used in strict mode, but 1636 * all Closure Library consumers must depend on this file. 1637 */ 1638 goog.base = function(me, opt_methodName, var_args) { 1639 var caller = arguments.callee.caller; 1640 1641 if (goog.STRICT_MODE_COMPATIBLE || (goog.DEBUG && !caller)) { 1642 throw Error('arguments.caller not defined. goog.base() cannot be used ' + 1643 'with strict mode code. See ' + 1644 'http://www.ecma-international.org/ecma-262/5.1/#sec-C'); 1645 } 1646 1647 if (caller.superClass_) { 1648 // This is a constructor. Call the superclass constructor. 1649 return caller.superClass_.constructor.apply( 1650 me, Array.prototype.slice.call(arguments, 1)); 1651 } 1652 1653 var args = Array.prototype.slice.call(arguments, 2); 1654 var foundCaller = false; 1655 for (var ctor = me.constructor; 1656 ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { 1657 if (ctor.prototype[opt_methodName] === caller) { 1658 foundCaller = true; 1659 } else if (foundCaller) { 1660 return ctor.prototype[opt_methodName].apply(me, args); 1661 } 1662 } 1663 1664 // If we did not find the caller in the prototype chain, then one of two 1665 // things happened: 1666 // 1) The caller is an instance method. 1667 // 2) This method was not called by the right caller. 1668 if (me[opt_methodName] === caller) { 1669 return me.constructor.prototype[opt_methodName].apply(me, args); 1670 } else { 1671 throw Error( 1672 'goog.base called from a method of one name ' + 1673 'to a method of a different name'); 1674 } 1675 }; 1676 1677 1678 /** 1679 * Allow for aliasing within scope functions. This function exists for 1680 * uncompiled code - in compiled code the calls will be inlined and the aliases 1681 * applied. In uncompiled code the function is simply run since the aliases as 1682 * written are valid JavaScript. 1683 * @param {function()} fn Function to call. This function can contain aliases 1684 * to namespaces (e.g. "var dom = goog.dom") or classes 1685 * (e.g. "var Timer = goog.Timer"). 1686 */ 1687 goog.scope = function(fn) { 1688 fn.call(goog.global); 1689 }; 1690 1691 1692 /* 1693 * To support uncompiled, strict mode bundles that use eval to divide source 1694 * like so: 1695 * eval('someSource;//# sourceUrl sourcefile.js'); 1696 * We need to export the globally defined symbols "goog" and "COMPILED". 1697 * Exporting "goog" breaks the compiler optimizations, so we required that 1698 * be defined externally. 1699 * NOTE: We don't use goog.exportSymbol here because we don't want to trigger 1700 * extern generation when that compiler option is enabled. 1701 */ 1702 if (!COMPILED) { 1703 goog.global['COMPILED'] = COMPILED; 1704 } 1705 1706 lib/goog/debug/error.js
1 // Copyright 2009 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides a base class for custom Error objects such that the 17 * stack is correctly maintained. 18 * 19 * You should never need to throw goog.debug.Error(msg) directly, Error(msg) is 20 * sufficient. 21 * 22 */ 23 24 goog.provide('goog.debug.Error'); 25 26 27 28 /** 29 * Base class for custom error objects. 30 * @param {*=} opt_msg The message associated with the error. 31 * @constructor 32 * @extends {Error} 33 */ 34 goog.debug.Error = function(opt_msg) { 35 36 // Attempt to ensure there is a stack trace. 37 if (Error.captureStackTrace) { 38 Error.captureStackTrace(this, goog.debug.Error); 39 } else { 40 var stack = new Error().stack; 41 if (stack) { 42 this.stack = stack; 43 } 44 } 45 46 if (opt_msg) { 47 this.message = String(opt_msg); 48 } 49 }; 50 goog.inherits(goog.debug.Error, Error); 51 52 53 /** @override */ 54 goog.debug.Error.prototype.name = 'CustomError'; lib/goog/deps.js
1 // This file has been auto-generated; do not edit by hand 2 goog.addDependency("../atoms/error.js", ["bot.Error","bot.ErrorCode"], []); 3 goog.addDependency("../atoms/response.js", ["bot.response","bot.response.ResponseObject"], ["bot.Error","bot.ErrorCode"]); 4 goog.addDependency("../atoms/json.js", ["bot.json"], ["bot.userAgent","goog.json","goog.userAgent"]); 5 goog.addDependency("../atoms/userAgent.js", ["bot.userAgent"], ["goog.string","goog.userAgent","goog.userAgent.product","goog.userAgent.product.isVersion"]); 6 goog.addDependency("string/string.js", ["goog.string","goog.string.Unicode"], []); 7 goog.addDependency("useragent/useragent.js", ["goog.userAgent"], ["goog.labs.userAgent.browser","goog.labs.userAgent.engine","goog.labs.userAgent.util","goog.string"]); 8 goog.addDependency("labs/useragent/browser.js", ["goog.labs.userAgent.browser"], ["goog.array","goog.asserts","goog.labs.userAgent.util","goog.string"]); 9 goog.addDependency("array/array.js", ["goog.array","goog.array.ArrayLike"], ["goog.asserts"]); 10 goog.addDependency("asserts/asserts.js", ["goog.asserts","goog.asserts.AssertionError"], ["goog.debug.Error","goog.dom.NodeType","goog.string"]); 11 goog.addDependency("debug/error.js", ["goog.debug.Error"], []); 12 goog.addDependency("dom/nodetype.js", ["goog.dom.NodeType"], []); 13 goog.addDependency("labs/useragent/util.js", ["goog.labs.userAgent.util"], ["goog.string"]); 14 goog.addDependency("labs/useragent/engine.js", ["goog.labs.userAgent.engine"], ["goog.array","goog.labs.userAgent.util","goog.string"]); 15 goog.addDependency("useragent/product.js", ["goog.userAgent.product"], ["goog.userAgent"]); 16 goog.addDependency("useragent/product_isversion.js", ["goog.userAgent.product.isVersion"], ["goog.userAgent.product"]); 17 goog.addDependency("json/json.js", ["goog.json","goog.json.Replacer","goog.json.Reviver","goog.json.Serializer"], []); 18 goog.addDependency("../webdriver/capabilities.js", ["webdriver.Browser","webdriver.Capabilities","webdriver.Capability"], []); 19 goog.addDependency("../webdriver/logging.js", ["webdriver.logging","webdriver.logging.Preferences"], ["goog.object"]); 20 goog.addDependency("object/object.js", ["goog.object"], []); 21 goog.addDependency("../webdriver/actionsequence.js", ["webdriver.ActionSequence"], ["goog.array","webdriver.Button","webdriver.Command","webdriver.CommandName","webdriver.Key"]); 22 goog.addDependency("../webdriver/button.js", ["webdriver.Button"], []); 23 goog.addDependency("../webdriver/command.js", ["webdriver.Command","webdriver.CommandExecutor","webdriver.CommandName"], []); 24 goog.addDependency("../webdriver/key.js", ["webdriver.Key"], []); 25 goog.addDependency("../webdriver/stacktrace.js", ["webdriver.stacktrace","webdriver.stacktrace.Snapshot"], ["goog.array","goog.string","goog.userAgent"]); 26 goog.addDependency("../webdriver/locators.js", ["webdriver.By","webdriver.Locator","webdriver.Locator.Strategy"], ["goog.array","goog.object","goog.string"]); 27 goog.addDependency("../webdriver/promise.js", ["webdriver.promise","webdriver.promise.ControlFlow","webdriver.promise.ControlFlow.Timer","webdriver.promise.Deferred","webdriver.promise.Promise"], ["goog.array","goog.debug.Error","goog.object","webdriver.EventEmitter","webdriver.stacktrace.Snapshot"]); 28 goog.addDependency("../webdriver/events.js", ["webdriver.EventEmitter"], []); 29 goog.addDependency("../webdriver/process.js", ["webdriver.process"], ["goog.Uri","goog.array","goog.json"]); 30 goog.addDependency("uri/uri.js", ["goog.Uri","goog.Uri.QueryData"], ["goog.array","goog.string","goog.structs","goog.structs.Map","goog.uri.utils","goog.uri.utils.ComponentIndex","goog.uri.utils.StandardQueryParam"]); 31 goog.addDependency("structs/structs.js", ["goog.structs"], ["goog.array","goog.object"]); 32 goog.addDependency("structs/map.js", ["goog.structs.Map"], ["goog.iter.Iterator","goog.iter.StopIteration","goog.object"]); 33 goog.addDependency("iter/iter.js", ["goog.iter","goog.iter.Iterable","goog.iter.Iterator","goog.iter.StopIteration"], ["goog.array","goog.asserts","goog.functions","goog.math"]); 34 goog.addDependency("functions/functions.js", ["goog.functions"], []); 35 goog.addDependency("math/math.js", ["goog.math"], ["goog.array","goog.asserts"]); 36 goog.addDependency("uri/utils.js", ["goog.uri.utils","goog.uri.utils.ComponentIndex","goog.uri.utils.QueryArray","goog.uri.utils.QueryValue","goog.uri.utils.StandardQueryParam"], ["goog.asserts","goog.string","goog.userAgent"]); 37 goog.addDependency("../webdriver/webdriver.js", ["webdriver.Alert","webdriver.UnhandledAlertError","webdriver.WebDriver","webdriver.WebElement"], ["bot.Error","bot.ErrorCode","bot.response","goog.array","goog.object","webdriver.ActionSequence","webdriver.Command","webdriver.CommandName","webdriver.Key","webdriver.Locator","webdriver.Session","webdriver.logging","webdriver.promise"]); 38 goog.addDependency("../webdriver/session.js", ["webdriver.Session"], ["webdriver.Capabilities"]); 39 goog.addDependency("../webdriver/builder.js", ["webdriver.Builder"], ["goog.userAgent","webdriver.AbstractBuilder","webdriver.FirefoxDomExecutor","webdriver.WebDriver","webdriver.http.CorsClient","webdriver.http.Executor","webdriver.http.XhrClient","webdriver.process"]); 40 goog.addDependency("../webdriver/abstractbuilder.js", ["webdriver.AbstractBuilder"], ["webdriver.Capabilities","webdriver.process"]); 41 goog.addDependency("../webdriver/firefoxdomexecutor.js", ["webdriver.FirefoxDomExecutor"], ["bot.response","goog.json","goog.userAgent.product","webdriver.Command","webdriver.CommandName"]); 42 goog.addDependency("../webdriver/http/corsclient.js", ["webdriver.http.CorsClient"], ["goog.json","webdriver.http.Response"]); 43 goog.addDependency("../webdriver/http/http.js", ["webdriver.http.Client","webdriver.http.Executor","webdriver.http.Request","webdriver.http.Response"], ["bot.ErrorCode","goog.array","goog.json","webdriver.CommandName","webdriver.promise.Deferred"]); 44 goog.addDependency("../webdriver/http/xhrclient.js", ["webdriver.http.XhrClient"], ["goog.json","goog.net.XmlHttp","webdriver.http.Response"]); 45 goog.addDependency("net/xmlhttp.js", ["goog.net.DefaultXmlHttpFactory","goog.net.XmlHttp","goog.net.XmlHttp.OptionType","goog.net.XmlHttp.ReadyState","goog.net.XmlHttpDefines"], ["goog.asserts","goog.net.WrapperXmlHttpFactory","goog.net.XmlHttpFactory"]); 46 goog.addDependency("net/wrapperxmlhttpfactory.js", ["goog.net.WrapperXmlHttpFactory"], ["goog.net.XhrLike","goog.net.XmlHttpFactory"]); 47 goog.addDependency("net/xhrlike.js", ["goog.net.XhrLike"], []); 48 goog.addDependency("net/xmlhttpfactory.js", ["goog.net.XmlHttpFactory"], ["goog.net.XhrLike"]); 49 goog.addDependency("../webdriver/testing/asserts.js", ["webdriver.testing.Assertion","webdriver.testing.ContainsMatcher","webdriver.testing.NegatedAssertion","webdriver.testing.assert","webdriver.testing.asserts"], ["goog.array","goog.labs.testing.CloseToMatcher","goog.labs.testing.EndsWithMatcher","goog.labs.testing.EqualToMatcher","goog.labs.testing.EqualsMatcher","goog.labs.testing.GreaterThanEqualToMatcher","goog.labs.testing.GreaterThanMatcher","goog.labs.testing.LessThanEqualToMatcher","goog.labs.testing.LessThanMatcher","goog.labs.testing.InstanceOfMatcher","goog.labs.testing.IsNotMatcher","goog.labs.testing.IsNullMatcher","goog.labs.testing.IsNullOrUndefinedMatcher","goog.labs.testing.IsUndefinedMatcher","goog.labs.testing.Matcher","goog.labs.testing.ObjectEqualsMatcher","goog.labs.testing.RegexMatcher","goog.labs.testing.StartsWithMatcher","goog.labs.testing.assertThat","goog.string","webdriver.promise"]); 50 goog.addDependency("labs/testing/numbermatcher.js", ["goog.labs.testing.CloseToMatcher","goog.labs.testing.EqualToMatcher","goog.labs.testing.GreaterThanEqualToMatcher","goog.labs.testing.GreaterThanMatcher","goog.labs.testing.LessThanEqualToMatcher","goog.labs.testing.LessThanMatcher"], ["goog.asserts","goog.labs.testing.Matcher"]); 51 goog.addDependency("labs/testing/matcher.js", ["goog.labs.testing.Matcher"], []); 52 goog.addDependency("labs/testing/stringmatcher.js", ["goog.labs.testing.ContainsStringMatcher","goog.labs.testing.EndsWithMatcher","goog.labs.testing.EqualToIgnoringCaseMatcher","goog.labs.testing.EqualToIgnoringWhitespaceMatcher","goog.labs.testing.EqualsMatcher","goog.labs.testing.RegexMatcher","goog.labs.testing.StartsWithMatcher","goog.labs.testing.StringContainsInOrderMatcher"], ["goog.asserts","goog.labs.testing.Matcher","goog.string"]); 53 goog.addDependency("labs/testing/objectmatcher.js", ["goog.labs.testing.HasPropertyMatcher","goog.labs.testing.InstanceOfMatcher","goog.labs.testing.IsNullMatcher","goog.labs.testing.IsNullOrUndefinedMatcher","goog.labs.testing.IsUndefinedMatcher","goog.labs.testing.ObjectEqualsMatcher"], ["goog.labs.testing.Matcher","goog.string"]); 54 goog.addDependency("labs/testing/logicmatcher.js", ["goog.labs.testing.AllOfMatcher","goog.labs.testing.AnyOfMatcher","goog.labs.testing.IsNotMatcher"], ["goog.array","goog.labs.testing.Matcher"]); 55 goog.addDependency("labs/testing/assertthat.js", ["goog.labs.testing.MatcherError","goog.labs.testing.assertThat"], ["goog.asserts","goog.debug.Error","goog.labs.testing.Matcher"]); lib/goog/dom/nodetype.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Definition of goog.dom.NodeType. 17 */ 18 19 goog.provide('goog.dom.NodeType'); 20 21 22 /** 23 * Constants for the nodeType attribute in the Node interface. 24 * 25 * These constants match those specified in the Node interface. These are 26 * usually present on the Node object in recent browsers, but not in older 27 * browsers (specifically, early IEs) and thus are given here. 28 * 29 * In some browsers (early IEs), these are not defined on the Node object, 30 * so they are provided here. 31 * 32 * See http://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-1950641247 33 * @enum {number} 34 */ 35 goog.dom.NodeType = { 36 ELEMENT: 1, 37 ATTRIBUTE: 2, 38 TEXT: 3, 39 CDATA_SECTION: 4, 40 ENTITY_REFERENCE: 5, 41 ENTITY: 6, 42 PROCESSING_INSTRUCTION: 7, 43 COMMENT: 8, 44 DOCUMENT: 9, 45 DOCUMENT_TYPE: 10, 46 DOCUMENT_FRAGMENT: 11, 47 NOTATION: 12 48 }; lib/goog/functions/functions.js
1 // Copyright 2008 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities for creating functions. Loosely inspired by the 17 * java classes: http://goo.gl/GM0Hmu and http://goo.gl/6k7nI8. 18 * 19 * @author nicksantos@google.com (Nick Santos) 20 */ 21 22 23 goog.provide('goog.functions'); 24 25 26 /** 27 * Creates a function that always returns the same value. 28 * @param {T} retValue The value to return. 29 * @return {function():T} The new function. 30 * @template T 31 */ 32 goog.functions.constant = function(retValue) { 33 return function() { 34 return retValue; 35 }; 36 }; 37 38 39 /** 40 * Always returns false. 41 * @type {function(...): boolean} 42 */ 43 goog.functions.FALSE = goog.functions.constant(false); 44 45 46 /** 47 * Always returns true. 48 * @type {function(...): boolean} 49 */ 50 goog.functions.TRUE = goog.functions.constant(true); 51 52 53 /** 54 * Always returns NULL. 55 * @type {function(...): null} 56 */ 57 goog.functions.NULL = goog.functions.constant(null); 58 59 60 /** 61 * A simple function that returns the first argument of whatever is passed 62 * into it. 63 * @param {T=} opt_returnValue The single value that will be returned. 64 * @param {...*} var_args Optional trailing arguments. These are ignored. 65 * @return {T} The first argument passed in, or undefined if nothing was passed. 66 * @template T 67 */ 68 goog.functions.identity = function(opt_returnValue, var_args) { 69 return opt_returnValue; 70 }; 71 72 73 /** 74 * Creates a function that always throws an error with the given message. 75 * @param {string} message The error message. 76 * @return {!Function} The error-throwing function. 77 */ 78 goog.functions.error = function(message) { 79 return function() { 80 throw Error(message); 81 }; 82 }; 83 84 85 /** 86 * Creates a function that throws the given object. 87 * @param {*} err An object to be thrown. 88 * @return {!Function} The error-throwing function. 89 */ 90 goog.functions.fail = function(err) { 91 return function() { 92 throw err; 93 } 94 }; 95 96 97 /** 98 * Given a function, create a function that keeps opt_numArgs arguments and 99 * silently discards all additional arguments. 100 * @param {Function} f The original function. 101 * @param {number=} opt_numArgs The number of arguments to keep. Defaults to 0. 102 * @return {!Function} A version of f that only keeps the first opt_numArgs 103 * arguments. 104 */ 105 goog.functions.lock = function(f, opt_numArgs) { 106 opt_numArgs = opt_numArgs || 0; 107 return function() { 108 return f.apply(this, Array.prototype.slice.call(arguments, 0, opt_numArgs)); 109 }; 110 }; 111 112 113 /** 114 * Creates a function that returns its nth argument. 115 * @param {number} n The position of the return argument. 116 * @return {!Function} A new function. 117 */ 118 goog.functions.nth = function(n) { 119 return function() { 120 return arguments[n]; 121 }; 122 }; 123 124 125 /** 126 * Given a function, create a new function that swallows its return value 127 * and replaces it with a new one. 128 * @param {Function} f A function. 129 * @param {T} retValue A new return value. 130 * @return {function(...[?]):T} A new function. 131 * @template T 132 */ 133 goog.functions.withReturnValue = function(f, retValue) { 134 return goog.functions.sequence(f, goog.functions.constant(retValue)); 135 }; 136 137 138 /** 139 * Creates the composition of the functions passed in. 140 * For example, (goog.functions.compose(f, g))(a) is equivalent to f(g(a)). 141 * @param {function(...[?]):T} fn The final function. 142 * @param {...Function} var_args A list of functions. 143 * @return {function(...[?]):T} The composition of all inputs. 144 * @template T 145 */ 146 goog.functions.compose = function(fn, var_args) { 147 var functions = arguments; 148 var length = functions.length; 149 return function() { 150 var result; 151 if (length) { 152 result = functions[length - 1].apply(this, arguments); 153 } 154 155 for (var i = length - 2; i >= 0; i--) { 156 result = functions[i].call(this, result); 157 } 158 return result; 159 }; 160 }; 161 162 163 /** 164 * Creates a function that calls the functions passed in in sequence, and 165 * returns the value of the last function. For example, 166 * (goog.functions.sequence(f, g))(x) is equivalent to f(x),g(x). 167 * @param {...Function} var_args A list of functions. 168 * @return {!Function} A function that calls all inputs in sequence. 169 */ 170 goog.functions.sequence = function(var_args) { 171 var functions = arguments; 172 var length = functions.length; 173 return function() { 174 var result; 175 for (var i = 0; i < length; i++) { 176 result = functions[i].apply(this, arguments); 177 } 178 return result; 179 }; 180 }; 181 182 183 /** 184 * Creates a function that returns true if each of its components evaluates 185 * to true. The components are evaluated in order, and the evaluation will be 186 * short-circuited as soon as a function returns false. 187 * For example, (goog.functions.and(f, g))(x) is equivalent to f(x) && g(x). 188 * @param {...Function} var_args A list of functions. 189 * @return {function(...[?]):boolean} A function that ANDs its component 190 * functions. 191 */ 192 goog.functions.and = function(var_args) { 193 var functions = arguments; 194 var length = functions.length; 195 return function() { 196 for (var i = 0; i < length; i++) { 197 if (!functions[i].apply(this, arguments)) { 198 return false; 199 } 200 } 201 return true; 202 }; 203 }; 204 205 206 /** 207 * Creates a function that returns true if any of its components evaluates 208 * to true. The components are evaluated in order, and the evaluation will be 209 * short-circuited as soon as a function returns true. 210 * For example, (goog.functions.or(f, g))(x) is equivalent to f(x) || g(x). 211 * @param {...Function} var_args A list of functions. 212 * @return {function(...[?]):boolean} A function that ORs its component 213 * functions. 214 */ 215 goog.functions.or = function(var_args) { 216 var functions = arguments; 217 var length = functions.length; 218 return function() { 219 for (var i = 0; i < length; i++) { 220 if (functions[i].apply(this, arguments)) { 221 return true; 222 } 223 } 224 return false; 225 }; 226 }; 227 228 229 /** 230 * Creates a function that returns the Boolean opposite of a provided function. 231 * For example, (goog.functions.not(f))(x) is equivalent to !f(x). 232 * @param {!Function} f The original function. 233 * @return {function(...[?]):boolean} A function that delegates to f and returns 234 * opposite. 235 */ 236 goog.functions.not = function(f) { 237 return function() { 238 return !f.apply(this, arguments); 239 }; 240 }; 241 242 243 /** 244 * Generic factory function to construct an object given the constructor 245 * and the arguments. Intended to be bound to create object factories. 246 * 247 * Callers should cast the result to the appropriate type for proper type 248 * checking by the compiler. 249 * @param {!Function} constructor The constructor for the Object. 250 * @param {...*} var_args The arguments to be passed to the constructor. 251 * @return {!Object} A new instance of the class given in {@code constructor}. 252 */ 253 goog.functions.create = function(constructor, var_args) { 254 /** 255 * @constructor 256 * @final 257 */ 258 var temp = function() {}; 259 temp.prototype = constructor.prototype; 260 261 // obj will have constructor's prototype in its chain and 262 // 'obj instanceof constructor' will be true. 263 var obj = new temp(); 264 265 // obj is initialized by constructor. 266 // arguments is only array-like so lacks shift(), but can be used with 267 // the Array prototype function. 268 constructor.apply(obj, Array.prototype.slice.call(arguments, 1)); 269 return obj; 270 }; 271 272 273 /** 274 * @define {boolean} Whether the return value cache should be used. 275 * This should only be used to disable caches when testing. 276 */ 277 goog.define('goog.functions.CACHE_RETURN_VALUE', true); 278 279 280 /** 281 * Gives a wrapper function that caches the return value of a parameterless 282 * function when first called. 283 * 284 * When called for the first time, the given function is called and its 285 * return value is cached (thus this is only appropriate for idempotent 286 * functions). Subsequent calls will return the cached return value. This 287 * allows the evaluation of expensive functions to be delayed until first used. 288 * 289 * To cache the return values of functions with parameters, see goog.memoize. 290 * 291 * @param {!function():T} fn A function to lazily evaluate. 292 * @return {!function():T} A wrapped version the function. 293 * @template T 294 */ 295 goog.functions.cacheReturnValue = function(fn) { 296 var called = false; 297 var value; 298 299 return function() { 300 if (!goog.functions.CACHE_RETURN_VALUE) { 301 return fn(); 302 } 303 304 if (!called) { 305 value = fn(); 306 called = true; 307 } 308 309 return value; 310 } 311 }; lib/goog/iter/iter.js
1 // Copyright 2007 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Python style iteration utilities. 17 * @author arv@google.com (Erik Arvidsson) 18 */ 19 20 21 goog.provide('goog.iter'); 22 goog.provide('goog.iter.Iterable'); 23 goog.provide('goog.iter.Iterator'); 24 goog.provide('goog.iter.StopIteration'); 25 26 goog.require('goog.array'); 27 goog.require('goog.asserts'); 28 goog.require('goog.functions'); 29 goog.require('goog.math'); 30 31 32 /** 33 * @typedef {goog.iter.Iterator|{length:number}|{__iterator__}} 34 */ 35 goog.iter.Iterable; 36 37 38 // For script engines that already support iterators. 39 if ('StopIteration' in goog.global) { 40 /** 41 * Singleton Error object that is used to terminate iterations. 42 * @type {Error} 43 */ 44 goog.iter.StopIteration = goog.global['StopIteration']; 45 } else { 46 /** 47 * Singleton Error object that is used to terminate iterations. 48 * @type {Error} 49 * @suppress {duplicate} 50 */ 51 goog.iter.StopIteration = Error('StopIteration'); 52 } 53 54 55 56 /** 57 * Class/interface for iterators. An iterator needs to implement a {@code next} 58 * method and it needs to throw a {@code goog.iter.StopIteration} when the 59 * iteration passes beyond the end. Iterators have no {@code hasNext} method. 60 * It is recommended to always use the helper functions to iterate over the 61 * iterator or in case you are only targeting JavaScript 1.7 for in loops. 62 * @constructor 63 * @template VALUE 64 */ 65 goog.iter.Iterator = function() {}; 66 67 68 /** 69 * Returns the next value of the iteration. This will throw the object 70 * {@see goog.iter#StopIteration} when the iteration passes the end. 71 * @return {VALUE} Any object or value. 72 */ 73 goog.iter.Iterator.prototype.next = function() { 74 throw goog.iter.StopIteration; 75 }; 76 77 78 /** 79 * Returns the {@code Iterator} object itself. This is used to implement 80 * the iterator protocol in JavaScript 1.7 81 * @param {boolean=} opt_keys Whether to return the keys or values. Default is 82 * to only return the values. This is being used by the for-in loop (true) 83 * and the for-each-in loop (false). Even though the param gives a hint 84 * about what the iterator will return there is no guarantee that it will 85 * return the keys when true is passed. 86 * @return {!goog.iter.Iterator.<VALUE>} The object itself. 87 */ 88 goog.iter.Iterator.prototype.__iterator__ = function(opt_keys) { 89 return this; 90 }; 91 92 93 /** 94 * Returns an iterator that knows how to iterate over the values in the object. 95 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable If the 96 * object is an iterator it will be returned as is. If the object has an 97 * {@code __iterator__} method that will be called to get the value 98 * iterator. If the object is an array-like object we create an iterator 99 * for that. 100 * @return {!goog.iter.Iterator.<VALUE>} An iterator that knows how to iterate 101 * over the values in {@code iterable}. 102 * @template VALUE 103 */ 104 goog.iter.toIterator = function(iterable) { 105 if (iterable instanceof goog.iter.Iterator) { 106 return iterable; 107 } 108 if (typeof iterable.__iterator__ == 'function') { 109 return iterable.__iterator__(false); 110 } 111 if (goog.isArrayLike(iterable)) { 112 var i = 0; 113 var newIter = new goog.iter.Iterator; 114 newIter.next = function() { 115 while (true) { 116 if (i >= iterable.length) { 117 throw goog.iter.StopIteration; 118 } 119 // Don't include deleted elements. 120 if (!(i in iterable)) { 121 i++; 122 continue; 123 } 124 return iterable[i++]; 125 } 126 }; 127 return newIter; 128 } 129 130 131 // TODO(arv): Should we fall back on goog.structs.getValues()? 132 throw Error('Not implemented'); 133 }; 134 135 136 /** 137 * Calls a function for each element in the iterator with the element of the 138 * iterator passed as argument. 139 * 140 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 141 * to iterate over. If the iterable is an object {@code toIterator} will be 142 * called on it. 143 * @param {function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>)| 144 * function(this:THIS,number,undefined,goog.iter.Iterator.<VALUE>)} f 145 * The function to call for every element. This function takes 3 arguments 146 * (the element, undefined, and the iterator) and the return value is 147 * irrelevant. The reason for passing undefined as the second argument is 148 * so that the same function can be used in {@see goog.array#forEach} as 149 * well as others. 150 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 151 * {@code f}. 152 * @template THIS, VALUE 153 */ 154 goog.iter.forEach = function(iterable, f, opt_obj) { 155 if (goog.isArrayLike(iterable)) { 156 /** @preserveTry */ 157 try { 158 // NOTES: this passes the index number to the second parameter 159 // of the callback contrary to the documentation above. 160 goog.array.forEach(/** @type {goog.array.ArrayLike} */(iterable), f, 161 opt_obj); 162 } catch (ex) { 163 if (ex !== goog.iter.StopIteration) { 164 throw ex; 165 } 166 } 167 } else { 168 iterable = goog.iter.toIterator(iterable); 169 /** @preserveTry */ 170 try { 171 while (true) { 172 f.call(opt_obj, iterable.next(), undefined, iterable); 173 } 174 } catch (ex) { 175 if (ex !== goog.iter.StopIteration) { 176 throw ex; 177 } 178 } 179 } 180 }; 181 182 183 /** 184 * Calls a function for every element in the iterator, and if the function 185 * returns true adds the element to a new iterator. 186 * 187 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 188 * to iterate over. 189 * @param { 190 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 191 * The function to call for every element. This function takes 3 arguments 192 * (the element, undefined, and the iterator) and should return a boolean. 193 * If the return value is true the element will be included in the returned 194 * iterator. If it is false the element is not included. 195 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 196 * {@code f}. 197 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements 198 * that passed the test are present. 199 * @template THIS, VALUE 200 */ 201 goog.iter.filter = function(iterable, f, opt_obj) { 202 var iterator = goog.iter.toIterator(iterable); 203 var newIter = new goog.iter.Iterator; 204 newIter.next = function() { 205 while (true) { 206 var val = iterator.next(); 207 if (f.call(opt_obj, val, undefined, iterator)) { 208 return val; 209 } 210 } 211 }; 212 return newIter; 213 }; 214 215 216 /** 217 * Calls a function for every element in the iterator, and if the function 218 * returns false adds the element to a new iterator. 219 * 220 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 221 * to iterate over. 222 * @param { 223 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 224 * The function to call for every element. This function takes 3 arguments 225 * (the element, undefined, and the iterator) and should return a boolean. 226 * If the return value is false the element will be included in the returned 227 * iterator. If it is true the element is not included. 228 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 229 * {@code f}. 230 * @return {!goog.iter.Iterator.<VALUE>} A new iterator in which only elements 231 * that did not pass the test are present. 232 * @template THIS, VALUE 233 */ 234 goog.iter.filterFalse = function(iterable, f, opt_obj) { 235 return goog.iter.filter(iterable, goog.functions.not(f), opt_obj); 236 }; 237 238 239 /** 240 * Creates a new iterator that returns the values in a range. This function 241 * can take 1, 2 or 3 arguments: 242 * <pre> 243 * range(5) same as range(0, 5, 1) 244 * range(2, 5) same as range(2, 5, 1) 245 * </pre> 246 * 247 * @param {number} startOrStop The stop value if only one argument is provided. 248 * The start value if 2 or more arguments are provided. If only one 249 * argument is used the start value is 0. 250 * @param {number=} opt_stop The stop value. If left out then the first 251 * argument is used as the stop value. 252 * @param {number=} opt_step The number to increment with between each call to 253 * next. This can be negative. 254 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values 255 * in the range. 256 */ 257 goog.iter.range = function(startOrStop, opt_stop, opt_step) { 258 var start = 0; 259 var stop = startOrStop; 260 var step = opt_step || 1; 261 if (arguments.length > 1) { 262 start = startOrStop; 263 stop = opt_stop; 264 } 265 if (step == 0) { 266 throw Error('Range step argument must not be zero'); 267 } 268 269 var newIter = new goog.iter.Iterator; 270 newIter.next = function() { 271 if (step > 0 && start >= stop || step < 0 && start <= stop) { 272 throw goog.iter.StopIteration; 273 } 274 var rv = start; 275 start += step; 276 return rv; 277 }; 278 return newIter; 279 }; 280 281 282 /** 283 * Joins the values in a iterator with a delimiter. 284 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 285 * to get the values from. 286 * @param {string} deliminator The text to put between the values. 287 * @return {string} The joined value string. 288 * @template VALUE 289 */ 290 goog.iter.join = function(iterable, deliminator) { 291 return goog.iter.toArray(iterable).join(deliminator); 292 }; 293 294 295 /** 296 * For every element in the iterator call a function and return a new iterator 297 * with that value. 298 * 299 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 300 * iterator to iterate over. 301 * @param { 302 * function(this:THIS,VALUE,undefined,!goog.iter.Iterator.<VALUE>):RESULT} f 303 * The function to call for every element. This function takes 3 arguments 304 * (the element, undefined, and the iterator) and should return a new value. 305 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 306 * {@code f}. 307 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the 308 * results of applying the function to each element in the original 309 * iterator. 310 * @template THIS, VALUE, RESULT 311 */ 312 goog.iter.map = function(iterable, f, opt_obj) { 313 var iterator = goog.iter.toIterator(iterable); 314 var newIter = new goog.iter.Iterator; 315 newIter.next = function() { 316 var val = iterator.next(); 317 return f.call(opt_obj, val, undefined, iterator); 318 }; 319 return newIter; 320 }; 321 322 323 /** 324 * Passes every element of an iterator into a function and accumulates the 325 * result. 326 * 327 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 328 * to iterate over. 329 * @param {function(this:THIS,VALUE,VALUE):VALUE} f The function to call for 330 * every element. This function takes 2 arguments (the function's previous 331 * result or the initial value, and the value of the current element). 332 * function(previousValue, currentElement) : newValue. 333 * @param {VALUE} val The initial value to pass into the function on the first 334 * call. 335 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 336 * f. 337 * @return {VALUE} Result of evaluating f repeatedly across the values of 338 * the iterator. 339 * @template THIS, VALUE 340 */ 341 goog.iter.reduce = function(iterable, f, val, opt_obj) { 342 var rval = val; 343 goog.iter.forEach(iterable, function(val) { 344 rval = f.call(opt_obj, rval, val); 345 }); 346 return rval; 347 }; 348 349 350 /** 351 * Goes through the values in the iterator. Calls f for each of these, and if 352 * any of them returns true, this returns true (without checking the rest). If 353 * all return false this will return false. 354 * 355 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 356 * object. 357 * @param { 358 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 359 * The function to call for every value. This function takes 3 arguments 360 * (the value, undefined, and the iterator) and should return a boolean. 361 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 362 * {@code f}. 363 * @return {boolean} true if any value passes the test. 364 * @template THIS, VALUE 365 */ 366 goog.iter.some = function(iterable, f, opt_obj) { 367 iterable = goog.iter.toIterator(iterable); 368 /** @preserveTry */ 369 try { 370 while (true) { 371 if (f.call(opt_obj, iterable.next(), undefined, iterable)) { 372 return true; 373 } 374 } 375 } catch (ex) { 376 if (ex !== goog.iter.StopIteration) { 377 throw ex; 378 } 379 } 380 return false; 381 }; 382 383 384 /** 385 * Goes through the values in the iterator. Calls f for each of these and if any 386 * of them returns false this returns false (without checking the rest). If all 387 * return true this will return true. 388 * 389 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 390 * object. 391 * @param { 392 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 393 * The function to call for every value. This function takes 3 arguments 394 * (the value, undefined, and the iterator) and should return a boolean. 395 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 396 * {@code f}. 397 * @return {boolean} true if every value passes the test. 398 * @template THIS, VALUE 399 */ 400 goog.iter.every = function(iterable, f, opt_obj) { 401 iterable = goog.iter.toIterator(iterable); 402 /** @preserveTry */ 403 try { 404 while (true) { 405 if (!f.call(opt_obj, iterable.next(), undefined, iterable)) { 406 return false; 407 } 408 } 409 } catch (ex) { 410 if (ex !== goog.iter.StopIteration) { 411 throw ex; 412 } 413 } 414 return true; 415 }; 416 417 418 /** 419 * Takes zero or more iterables and returns one iterator that will iterate over 420 * them in the order chained. 421 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any 422 * number of iterable objects. 423 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will 424 * iterate over all the given iterables' contents. 425 * @template VALUE 426 */ 427 goog.iter.chain = function(var_args) { 428 var iterator = goog.iter.toIterator(arguments); 429 var iter = new goog.iter.Iterator(); 430 var current = null; 431 432 iter.next = function() { 433 while (true) { 434 if (current == null) { 435 var it = iterator.next(); 436 current = goog.iter.toIterator(it); 437 } 438 try { 439 return current.next(); 440 } catch (ex) { 441 if (ex !== goog.iter.StopIteration) { 442 throw ex; 443 } 444 current = null; 445 } 446 } 447 }; 448 449 return iter; 450 }; 451 452 453 /** 454 * Takes a single iterable containing zero or more iterables and returns one 455 * iterator that will iterate over each one in the order given. 456 * @see http://docs.python.org/2/library/itertools.html#itertools.chain.from_iterable 457 * @param {goog.iter.Iterable} iterable The iterable of iterables to chain. 458 * @return {!goog.iter.Iterator.<VALUE>} Returns a new iterator that will 459 * iterate over all the contents of the iterables contained within 460 * {@code iterable}. 461 * @template VALUE 462 */ 463 goog.iter.chainFromIterable = function(iterable) { 464 return goog.iter.chain.apply(undefined, iterable); 465 }; 466 467 468 /** 469 * Builds a new iterator that iterates over the original, but skips elements as 470 * long as a supplied function returns true. 471 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 472 * object. 473 * @param { 474 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 475 * The function to call for every value. This function takes 3 arguments 476 * (the value, undefined, and the iterator) and should return a boolean. 477 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 478 * {@code f}. 479 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that drops elements from 480 * the original iterator as long as {@code f} is true. 481 * @template THIS, VALUE 482 */ 483 goog.iter.dropWhile = function(iterable, f, opt_obj) { 484 var iterator = goog.iter.toIterator(iterable); 485 var newIter = new goog.iter.Iterator; 486 var dropping = true; 487 newIter.next = function() { 488 while (true) { 489 var val = iterator.next(); 490 if (dropping && f.call(opt_obj, val, undefined, iterator)) { 491 continue; 492 } else { 493 dropping = false; 494 } 495 return val; 496 } 497 }; 498 return newIter; 499 }; 500 501 502 /** 503 * Builds a new iterator that iterates over the original, but only as long as a 504 * supplied function returns true. 505 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 506 * object. 507 * @param { 508 * function(this:THIS,VALUE,undefined,goog.iter.Iterator.<VALUE>):boolean} f 509 * The function to call for every value. This function takes 3 arguments 510 * (the value, undefined, and the iterator) and should return a boolean. 511 * @param {THIS=} opt_obj This is used as the 'this' object in f when called. 512 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that keeps elements in 513 * the original iterator as long as the function is true. 514 * @template THIS, VALUE 515 */ 516 goog.iter.takeWhile = function(iterable, f, opt_obj) { 517 var iterator = goog.iter.toIterator(iterable); 518 var newIter = new goog.iter.Iterator; 519 var taking = true; 520 newIter.next = function() { 521 while (true) { 522 if (taking) { 523 var val = iterator.next(); 524 if (f.call(opt_obj, val, undefined, iterator)) { 525 return val; 526 } else { 527 taking = false; 528 } 529 } else { 530 throw goog.iter.StopIteration; 531 } 532 } 533 }; 534 return newIter; 535 }; 536 537 538 /** 539 * Converts the iterator to an array 540 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterator 541 * to convert to an array. 542 * @return {!Array.<VALUE>} An array of the elements the iterator iterates over. 543 * @template VALUE 544 */ 545 goog.iter.toArray = function(iterable) { 546 // Fast path for array-like. 547 if (goog.isArrayLike(iterable)) { 548 return goog.array.toArray(/** @type {!goog.array.ArrayLike} */(iterable)); 549 } 550 iterable = goog.iter.toIterator(iterable); 551 var array = []; 552 goog.iter.forEach(iterable, function(val) { 553 array.push(val); 554 }); 555 return array; 556 }; 557 558 559 /** 560 * Iterates over two iterables and returns true if they contain the same 561 * sequence of elements and have the same length. 562 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable1 The first 563 * iterable object. 564 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable2 The second 565 * iterable object. 566 * @return {boolean} true if the iterables contain the same sequence of elements 567 * and have the same length. 568 * @template VALUE 569 */ 570 goog.iter.equals = function(iterable1, iterable2) { 571 var fillValue = {}; 572 var pairs = goog.iter.zipLongest(fillValue, iterable1, iterable2); 573 return goog.iter.every(pairs, function(pair) { 574 return pair[0] == pair[1]; 575 }); 576 }; 577 578 579 /** 580 * Advances the iterator to the next position, returning the given default value 581 * instead of throwing an exception if the iterator has no more entries. 582 * @param {goog.iter.Iterator.<VALUE>|goog.iter.Iterable} iterable The iterable 583 * object. 584 * @param {VALUE} defaultValue The value to return if the iterator is empty. 585 * @return {VALUE} The next item in the iteration, or defaultValue if the 586 * iterator was empty. 587 * @template VALUE 588 */ 589 goog.iter.nextOrValue = function(iterable, defaultValue) { 590 try { 591 return goog.iter.toIterator(iterable).next(); 592 } catch (e) { 593 if (e != goog.iter.StopIteration) { 594 throw e; 595 } 596 return defaultValue; 597 } 598 }; 599 600 601 /** 602 * Cartesian product of zero or more sets. Gives an iterator that gives every 603 * combination of one element chosen from each set. For example, 604 * ([1, 2], [3, 4]) gives ([1, 3], [1, 4], [2, 3], [2, 4]). 605 * @see http://docs.python.org/library/itertools.html#itertools.product 606 * @param {...!goog.array.ArrayLike.<VALUE>} var_args Zero or more sets, as 607 * arrays. 608 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} An iterator that gives each 609 * n-tuple (as an array). 610 * @template VALUE 611 */ 612 goog.iter.product = function(var_args) { 613 var someArrayEmpty = goog.array.some(arguments, function(arr) { 614 return !arr.length; 615 }); 616 617 // An empty set in a cartesian product gives an empty set. 618 if (someArrayEmpty || !arguments.length) { 619 return new goog.iter.Iterator(); 620 } 621 622 var iter = new goog.iter.Iterator(); 623 var arrays = arguments; 624 625 // The first indices are [0, 0, ...] 626 var indicies = goog.array.repeat(0, arrays.length); 627 628 iter.next = function() { 629 630 if (indicies) { 631 var retVal = goog.array.map(indicies, function(valueIndex, arrayIndex) { 632 return arrays[arrayIndex][valueIndex]; 633 }); 634 635 // Generate the next-largest indices for the next call. 636 // Increase the rightmost index. If it goes over, increase the next 637 // rightmost (like carry-over addition). 638 for (var i = indicies.length - 1; i >= 0; i--) { 639 // Assertion prevents compiler warning below. 640 goog.asserts.assert(indicies); 641 if (indicies[i] < arrays[i].length - 1) { 642 indicies[i]++; 643 break; 644 } 645 646 // We're at the last indices (the last element of every array), so 647 // the iteration is over on the next call. 648 if (i == 0) { 649 indicies = null; 650 break; 651 } 652 // Reset the index in this column and loop back to increment the 653 // next one. 654 indicies[i] = 0; 655 } 656 return retVal; 657 } 658 659 throw goog.iter.StopIteration; 660 }; 661 662 return iter; 663 }; 664 665 666 /** 667 * Create an iterator to cycle over the iterable's elements indefinitely. 668 * For example, ([1, 2, 3]) would return : 1, 2, 3, 1, 2, 3, ... 669 * @see: http://docs.python.org/library/itertools.html#itertools.cycle. 670 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 671 * iterable object. 672 * @return {!goog.iter.Iterator.<VALUE>} An iterator that iterates indefinitely 673 * over the values in {@code iterable}. 674 * @template VALUE 675 */ 676 goog.iter.cycle = function(iterable) { 677 var baseIterator = goog.iter.toIterator(iterable); 678 679 // We maintain a cache to store the iterable elements as we iterate 680 // over them. The cache is used to return elements once we have 681 // iterated over the iterable once. 682 var cache = []; 683 var cacheIndex = 0; 684 685 var iter = new goog.iter.Iterator(); 686 687 // This flag is set after the iterable is iterated over once 688 var useCache = false; 689 690 iter.next = function() { 691 var returnElement = null; 692 693 // Pull elements off the original iterator if not using cache 694 if (!useCache) { 695 try { 696 // Return the element from the iterable 697 returnElement = baseIterator.next(); 698 cache.push(returnElement); 699 return returnElement; 700 } catch (e) { 701 // If an exception other than StopIteration is thrown 702 // or if there are no elements to iterate over (the iterable was empty) 703 // throw an exception 704 if (e != goog.iter.StopIteration || goog.array.isEmpty(cache)) { 705 throw e; 706 } 707 // set useCache to true after we know that a 'StopIteration' exception 708 // was thrown and the cache is not empty (to handle the 'empty iterable' 709 // use case) 710 useCache = true; 711 } 712 } 713 714 returnElement = cache[cacheIndex]; 715 cacheIndex = (cacheIndex + 1) % cache.length; 716 717 return returnElement; 718 }; 719 720 return iter; 721 }; 722 723 724 /** 725 * Creates an iterator that counts indefinitely from a starting value. 726 * @see http://docs.python.org/2/library/itertools.html#itertools.count 727 * @param {number=} opt_start The starting value. Default is 0. 728 * @param {number=} opt_step The number to increment with between each call to 729 * next. Negative and floating point numbers are allowed. Default is 1. 730 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the values 731 * in the series. 732 */ 733 goog.iter.count = function(opt_start, opt_step) { 734 var counter = opt_start || 0; 735 var step = goog.isDef(opt_step) ? opt_step : 1; 736 var iter = new goog.iter.Iterator(); 737 738 iter.next = function() { 739 var returnValue = counter; 740 counter += step; 741 return returnValue; 742 }; 743 744 return iter; 745 }; 746 747 748 /** 749 * Creates an iterator that returns the same object or value repeatedly. 750 * @param {VALUE} value Any object or value to repeat. 751 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that returns the 752 * repeated value. 753 * @template VALUE 754 */ 755 goog.iter.repeat = function(value) { 756 var iter = new goog.iter.Iterator(); 757 758 iter.next = goog.functions.constant(value); 759 760 return iter; 761 }; 762 763 764 /** 765 * Creates an iterator that returns running totals from the numbers in 766 * {@code iterable}. For example, the array {@code [1, 2, 3, 4, 5]} yields 767 * {@code 1 -> 3 -> 6 -> 10 -> 15}. 768 * @see http://docs.python.org/3.2/library/itertools.html#itertools.accumulate 769 * @param {!goog.iter.Iterable.<number>} iterable The iterable of numbers to 770 * accumulate. 771 * @return {!goog.iter.Iterator.<number>} A new iterator that returns the 772 * numbers in the series. 773 */ 774 goog.iter.accumulate = function(iterable) { 775 var iterator = goog.iter.toIterator(iterable); 776 var total = 0; 777 var iter = new goog.iter.Iterator(); 778 779 iter.next = function() { 780 total += iterator.next(); 781 return total; 782 }; 783 784 return iter; 785 }; 786 787 788 /** 789 * Creates an iterator that returns arrays containing the ith elements from the 790 * provided iterables. The returned arrays will be the same size as the number 791 * of iterables given in {@code var_args}. Once the shortest iterable is 792 * exhausted, subsequent calls to {@code next()} will throw 793 * {@code goog.iter.StopIteration}. 794 * @see http://docs.python.org/2/library/itertools.html#itertools.izip 795 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any 796 * number of iterable objects. 797 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns 798 * arrays of elements from the provided iterables. 799 * @template VALUE 800 */ 801 goog.iter.zip = function(var_args) { 802 var args = arguments; 803 var iter = new goog.iter.Iterator(); 804 805 if (args.length > 0) { 806 var iterators = goog.array.map(args, goog.iter.toIterator); 807 iter.next = function() { 808 var arr = goog.array.map(iterators, function(it) { 809 return it.next(); 810 }); 811 return arr; 812 }; 813 } 814 815 return iter; 816 }; 817 818 819 /** 820 * Creates an iterator that returns arrays containing the ith elements from the 821 * provided iterables. The returned arrays will be the same size as the number 822 * of iterables given in {@code var_args}. Shorter iterables will be extended 823 * with {@code fillValue}. Once the longest iterable is exhausted, subsequent 824 * calls to {@code next()} will throw {@code goog.iter.StopIteration}. 825 * @see http://docs.python.org/2/library/itertools.html#itertools.izip_longest 826 * @param {VALUE} fillValue The object or value used to fill shorter iterables. 827 * @param {...!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} var_args Any 828 * number of iterable objects. 829 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator that returns 830 * arrays of elements from the provided iterables. 831 * @template VALUE 832 */ 833 goog.iter.zipLongest = function(fillValue, var_args) { 834 var args = goog.array.slice(arguments, 1); 835 var iter = new goog.iter.Iterator(); 836 837 if (args.length > 0) { 838 var iterators = goog.array.map(args, goog.iter.toIterator); 839 840 iter.next = function() { 841 var iteratorsHaveValues = false; // false when all iterators are empty. 842 var arr = goog.array.map(iterators, function(it) { 843 var returnValue; 844 try { 845 returnValue = it.next(); 846 // Iterator had a value, so we've not exhausted the iterators. 847 // Set flag accordingly. 848 iteratorsHaveValues = true; 849 } catch (ex) { 850 if (ex !== goog.iter.StopIteration) { 851 throw ex; 852 } 853 returnValue = fillValue; 854 } 855 return returnValue; 856 }); 857 858 if (!iteratorsHaveValues) { 859 throw goog.iter.StopIteration; 860 } 861 return arr; 862 }; 863 } 864 865 return iter; 866 }; 867 868 869 /** 870 * Creates an iterator that filters {@code iterable} based on a series of 871 * {@code selectors}. On each call to {@code next()}, one item is taken from 872 * both the {@code iterable} and {@code selectors} iterators. If the item from 873 * {@code selectors} evaluates to true, the item from {@code iterable} is given. 874 * Otherwise, it is skipped. Once either {@code iterable} or {@code selectors} 875 * is exhausted, subsequent calls to {@code next()} will throw 876 * {@code goog.iter.StopIteration}. 877 * @see http://docs.python.org/2/library/itertools.html#itertools.compress 878 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 879 * iterable to filter. 880 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} selectors An 881 * iterable of items to be evaluated in a boolean context to determine if 882 * the corresponding element in {@code iterable} should be included in the 883 * result. 884 * @return {!goog.iter.Iterator.<VALUE>} A new iterator that returns the 885 * filtered values. 886 * @template VALUE 887 */ 888 goog.iter.compress = function(iterable, selectors) { 889 var selectorIterator = goog.iter.toIterator(selectors); 890 891 return goog.iter.filter(iterable, function() { 892 return !!selectorIterator.next(); 893 }); 894 }; 895 896 897 898 /** 899 * Implements the {@code goog.iter.groupBy} iterator. 900 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 901 * iterable to group. 902 * @param {function(...[VALUE]): KEY=} opt_keyFunc Optional function for 903 * determining the key value for each group in the {@code iterable}. Default 904 * is the identity function. 905 * @constructor 906 * @extends {goog.iter.Iterator.<!Array>} 907 * @template KEY, VALUE 908 * @private 909 */ 910 goog.iter.GroupByIterator_ = function(iterable, opt_keyFunc) { 911 912 /** 913 * The iterable to group, coerced to an iterator. 914 * @type {!goog.iter.Iterator} 915 */ 916 this.iterator = goog.iter.toIterator(iterable); 917 918 /** 919 * A function for determining the key value for each element in the iterable. 920 * If no function is provided, the identity function is used and returns the 921 * element unchanged. 922 * @type {function(...[VALUE]): KEY} 923 */ 924 this.keyFunc = opt_keyFunc || goog.functions.identity; 925 926 /** 927 * The target key for determining the start of a group. 928 * @type {KEY} 929 */ 930 this.targetKey; 931 932 /** 933 * The current key visited during iteration. 934 * @type {KEY} 935 */ 936 this.currentKey; 937 938 /** 939 * The current value being added to the group. 940 * @type {VALUE} 941 */ 942 this.currentValue; 943 }; 944 goog.inherits(goog.iter.GroupByIterator_, goog.iter.Iterator); 945 946 947 /** @override */ 948 goog.iter.GroupByIterator_.prototype.next = function() { 949 while (this.currentKey == this.targetKey) { 950 this.currentValue = this.iterator.next(); // Exits on StopIteration 951 this.currentKey = this.keyFunc(this.currentValue); 952 } 953 this.targetKey = this.currentKey; 954 return [this.currentKey, this.groupItems_(this.targetKey)]; 955 }; 956 957 958 /** 959 * Performs the grouping of objects using the given key. 960 * @param {KEY} targetKey The target key object for the group. 961 * @return {!Array.<VALUE>} An array of grouped objects. 962 * @private 963 */ 964 goog.iter.GroupByIterator_.prototype.groupItems_ = function(targetKey) { 965 var arr = []; 966 while (this.currentKey == targetKey) { 967 arr.push(this.currentValue); 968 try { 969 this.currentValue = this.iterator.next(); 970 } catch (ex) { 971 if (ex !== goog.iter.StopIteration) { 972 throw ex; 973 } 974 break; 975 } 976 this.currentKey = this.keyFunc(this.currentValue); 977 } 978 return arr; 979 }; 980 981 982 /** 983 * Creates an iterator that returns arrays containing elements from the 984 * {@code iterable} grouped by a key value. For iterables with repeated 985 * elements (i.e. sorted according to a particular key function), this function 986 * has a {@code uniq}-like effect. For example, grouping the array: 987 * {@code [A, B, B, C, C, A]} produces 988 * {@code [A, [A]], [B, [B, B]], [C, [C, C]], [A, [A]]}. 989 * @see http://docs.python.org/2/library/itertools.html#itertools.groupby 990 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 991 * iterable to group. 992 * @param {function(...[VALUE]): KEY=} opt_keyFunc Optional function for 993 * determining the key value for each group in the {@code iterable}. Default 994 * is the identity function. 995 * @return {!goog.iter.Iterator.<!Array>} A new iterator that returns arrays of 996 * consecutive key and groups. 997 * @template KEY, VALUE 998 */ 999 goog.iter.groupBy = function(iterable, opt_keyFunc) { 1000 return new goog.iter.GroupByIterator_(iterable, opt_keyFunc); 1001 }; 1002 1003 1004 /** 1005 * Gives an iterator that gives the result of calling the given function 1006 * <code>f</code> with the arguments taken from the next element from 1007 * <code>iterable</code> (the elements are expected to also be iterables). 1008 * 1009 * Similar to {@see goog.iter#map} but allows the function to accept multiple 1010 * arguments from the iterable. 1011 * 1012 * @param {!goog.iter.Iterable.<!goog.iter.Iterable>} iterable The iterable of 1013 * iterables to iterate over. 1014 * @param {function(this:THIS,...[*]):RESULT} f The function to call for every 1015 * element. This function takes N+2 arguments, where N represents the 1016 * number of items from the next element of the iterable. The two 1017 * additional arguments passed to the function are undefined and the 1018 * iterator itself. The function should return a new value. 1019 * @param {THIS=} opt_obj The object to be used as the value of 'this' within 1020 * {@code f}. 1021 * @return {!goog.iter.Iterator.<RESULT>} A new iterator that returns the 1022 * results of applying the function to each element in the original 1023 * iterator. 1024 * @template THIS, RESULT 1025 */ 1026 goog.iter.starMap = function(iterable, f, opt_obj) { 1027 var iterator = goog.iter.toIterator(iterable); 1028 var iter = new goog.iter.Iterator(); 1029 1030 iter.next = function() { 1031 var args = goog.iter.toArray(iterator.next()); 1032 return f.apply(opt_obj, goog.array.concat(args, undefined, iterator)); 1033 }; 1034 1035 return iter; 1036 }; 1037 1038 1039 /** 1040 * Returns an array of iterators each of which can iterate over the values in 1041 * {@code iterable} without advancing the others. 1042 * @see http://docs.python.org/2/library/itertools.html#itertools.tee 1043 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1044 * iterable to tee. 1045 * @param {number=} opt_num The number of iterators to create. Default is 2. 1046 * @return {!Array.<goog.iter.Iterator.<VALUE>>} An array of iterators. 1047 * @template VALUE 1048 */ 1049 goog.iter.tee = function(iterable, opt_num) { 1050 var iterator = goog.iter.toIterator(iterable); 1051 var num = goog.isNumber(opt_num) ? opt_num : 2; 1052 var buffers = goog.array.map(goog.array.range(num), function() { 1053 return []; 1054 }); 1055 1056 var addNextIteratorValueToBuffers = function() { 1057 var val = iterator.next(); 1058 goog.array.forEach(buffers, function(buffer) { 1059 buffer.push(val); 1060 }); 1061 }; 1062 1063 var createIterator = function(buffer) { 1064 // Each tee'd iterator has an associated buffer (initially empty). When a 1065 // tee'd iterator's buffer is empty, it calls 1066 // addNextIteratorValueToBuffers(), adding the next value to all tee'd 1067 // iterators' buffers, and then returns that value. This allows each 1068 // iterator to be advanced independently. 1069 var iter = new goog.iter.Iterator(); 1070 1071 iter.next = function() { 1072 if (goog.array.isEmpty(buffer)) { 1073 addNextIteratorValueToBuffers(); 1074 } 1075 goog.asserts.assert(!goog.array.isEmpty(buffer)); 1076 return buffer.shift(); 1077 }; 1078 1079 return iter; 1080 }; 1081 1082 return goog.array.map(buffers, createIterator); 1083 }; 1084 1085 1086 /** 1087 * Creates an iterator that returns arrays containing a count and an element 1088 * obtained from the given {@code iterable}. 1089 * @see http://docs.python.org/2/library/functions.html#enumerate 1090 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1091 * iterable to enumerate. 1092 * @param {number=} opt_start Optional starting value. Default is 0. 1093 * @return {!goog.iter.Iterator.<!Array>} A new iterator containing count/item 1094 * pairs. 1095 * @template VALUE 1096 */ 1097 goog.iter.enumerate = function(iterable, opt_start) { 1098 return goog.iter.zip(goog.iter.count(opt_start), iterable); 1099 }; 1100 1101 1102 /** 1103 * Creates an iterator that returns the first {@code limitSize} elements from an 1104 * iterable. If this number is greater than the number of elements in the 1105 * iterable, all the elements are returned. 1106 * @see http://goo.gl/V0sihp Inspired by the limit iterator in Guava. 1107 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1108 * iterable to limit. 1109 * @param {number} limitSize The maximum number of elements to return. 1110 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing 1111 * {@code limitSize} elements. 1112 * @template VALUE 1113 */ 1114 goog.iter.limit = function(iterable, limitSize) { 1115 goog.asserts.assert(goog.math.isInt(limitSize) && limitSize >= 0); 1116 1117 var iterator = goog.iter.toIterator(iterable); 1118 1119 var iter = new goog.iter.Iterator(); 1120 var remaining = limitSize; 1121 1122 iter.next = function() { 1123 if (remaining-- > 0) { 1124 return iterator.next(); 1125 } 1126 throw goog.iter.StopIteration; 1127 }; 1128 1129 return iter; 1130 }; 1131 1132 1133 /** 1134 * Creates an iterator that is advanced {@code count} steps ahead. Consumed 1135 * values are silently discarded. If {@code count} is greater than the number 1136 * of elements in {@code iterable}, an empty iterator is returned. Subsequent 1137 * calls to {@code next()} will throw {@code goog.iter.StopIteration}. 1138 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1139 * iterable to consume. 1140 * @param {number} count The number of elements to consume from the iterator. 1141 * @return {!goog.iter.Iterator.<VALUE>} An iterator advanced zero or more steps 1142 * ahead. 1143 * @template VALUE 1144 */ 1145 goog.iter.consume = function(iterable, count) { 1146 goog.asserts.assert(goog.math.isInt(count) && count >= 0); 1147 1148 var iterator = goog.iter.toIterator(iterable); 1149 1150 while (count-- > 0) { 1151 goog.iter.nextOrValue(iterator, null); 1152 } 1153 1154 return iterator; 1155 }; 1156 1157 1158 /** 1159 * Creates an iterator that returns a range of elements from an iterable. 1160 * Similar to {@see goog.array#slice} but does not support negative indexes. 1161 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1162 * iterable to slice. 1163 * @param {number} start The index of the first element to return. 1164 * @param {number=} opt_end The index after the last element to return. If 1165 * defined, must be greater than or equal to {@code start}. 1166 * @return {!goog.iter.Iterator.<VALUE>} A new iterator containing a slice of 1167 * the original. 1168 * @template VALUE 1169 */ 1170 goog.iter.slice = function(iterable, start, opt_end) { 1171 goog.asserts.assert(goog.math.isInt(start) && start >= 0); 1172 1173 var iterator = goog.iter.consume(iterable, start); 1174 1175 if (goog.isNumber(opt_end)) { 1176 goog.asserts.assert( 1177 goog.math.isInt(/** @type {number} */ (opt_end)) && opt_end >= start); 1178 iterator = goog.iter.limit(iterator, opt_end - start /* limitSize */); 1179 } 1180 1181 return iterator; 1182 }; 1183 1184 1185 /** 1186 * Checks an array for duplicate elements. 1187 * @param {Array.<VALUE>|goog.array.ArrayLike} arr The array to check for 1188 * duplicates. 1189 * @return {boolean} True, if the array contains duplicates, false otherwise. 1190 * @private 1191 * @template VALUE 1192 */ 1193 // TODO(user): Consider moving this into goog.array as a public function. 1194 goog.iter.hasDuplicates_ = function(arr) { 1195 var deduped = []; 1196 goog.array.removeDuplicates(arr, deduped); 1197 return arr.length != deduped.length; 1198 }; 1199 1200 1201 /** 1202 * Creates an iterator that returns permutations of elements in 1203 * {@code iterable}. 1204 * 1205 * Permutations are obtained by taking the Cartesian product of 1206 * {@code opt_length} iterables and filtering out those with repeated 1207 * elements. For example, the permutations of {@code [1,2,3]} are 1208 * {@code [[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1]]}. 1209 * @see http://docs.python.org/2/library/itertools.html#itertools.permutations 1210 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1211 * iterable from which to generate permutations. 1212 * @param {number=} opt_length Length of each permutation. If omitted, defaults 1213 * to the length of {@code iterable}. 1214 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing the 1215 * permutations of {@code iterable}. 1216 * @template VALUE 1217 */ 1218 goog.iter.permutations = function(iterable, opt_length) { 1219 var elements = goog.iter.toArray(iterable); 1220 var length = goog.isNumber(opt_length) ? opt_length : elements.length; 1221 1222 var sets = goog.array.repeat(elements, length); 1223 var product = goog.iter.product.apply(undefined, sets); 1224 1225 return goog.iter.filter(product, function(arr) { 1226 return !goog.iter.hasDuplicates_(arr); 1227 }); 1228 }; 1229 1230 1231 /** 1232 * Creates an iterator that returns combinations of elements from 1233 * {@code iterable}. 1234 * 1235 * Combinations are obtained by taking the {@see goog.iter#permutations} of 1236 * {@code iterable} and filtering those whose elements appear in the order they 1237 * are encountered in {@code iterable}. For example, the 3-length combinations 1238 * of {@code [0,1,2,3]} are {@code [[0,1,2], [0,1,3], [0,2,3], [1,2,3]]}. 1239 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations 1240 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1241 * iterable from which to generate combinations. 1242 * @param {number} length The length of each combination. 1243 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing 1244 * combinations from the {@code iterable}. 1245 * @template VALUE 1246 */ 1247 goog.iter.combinations = function(iterable, length) { 1248 var elements = goog.iter.toArray(iterable); 1249 var indexes = goog.iter.range(elements.length); 1250 var indexIterator = goog.iter.permutations(indexes, length); 1251 // sortedIndexIterator will now give arrays of with the given length that 1252 // indicate what indexes into "elements" should be returned on each iteration. 1253 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) { 1254 return goog.array.isSorted(arr); 1255 }); 1256 1257 var iter = new goog.iter.Iterator(); 1258 1259 function getIndexFromElements(index) { 1260 return elements[index]; 1261 } 1262 1263 iter.next = function() { 1264 return goog.array.map( 1265 /** @type {!Array.<number>} */ 1266 (sortedIndexIterator.next()), getIndexFromElements); 1267 }; 1268 1269 return iter; 1270 }; 1271 1272 1273 /** 1274 * Creates an iterator that returns combinations of elements from 1275 * {@code iterable}, with repeated elements possible. 1276 * 1277 * Combinations are obtained by taking the Cartesian product of {@code length} 1278 * iterables and filtering those whose elements appear in the order they are 1279 * encountered in {@code iterable}. For example, the 2-length combinations of 1280 * {@code [1,2,3]} are {@code [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]}. 1281 * @see http://docs.python.org/2/library/itertools.html#itertools.combinations_with_replacement 1282 * @see http://en.wikipedia.org/wiki/Combination#Number_of_combinations_with_repetition 1283 * @param {!goog.iter.Iterator.<VALUE>|!goog.iter.Iterable} iterable The 1284 * iterable to combine. 1285 * @param {number} length The length of each combination. 1286 * @return {!goog.iter.Iterator.<!Array.<VALUE>>} A new iterator containing 1287 * combinations from the {@code iterable}. 1288 * @template VALUE 1289 */ 1290 goog.iter.combinationsWithReplacement = function(iterable, length) { 1291 var elements = goog.iter.toArray(iterable); 1292 var indexes = goog.array.range(elements.length); 1293 var sets = goog.array.repeat(indexes, length); 1294 var indexIterator = goog.iter.product.apply(undefined, sets); 1295 // sortedIndexIterator will now give arrays of with the given length that 1296 // indicate what indexes into "elements" should be returned on each iteration. 1297 var sortedIndexIterator = goog.iter.filter(indexIterator, function(arr) { 1298 return goog.array.isSorted(arr); 1299 }); 1300 1301 var iter = new goog.iter.Iterator(); 1302 1303 function getIndexFromElements(index) { 1304 return elements[index]; 1305 } 1306 1307 iter.next = function() { 1308 return goog.array.map( 1309 /** @type {!Array.<number>} */ 1310 (sortedIndexIterator.next()), getIndexFromElements); 1311 }; 1312 1313 return iter; 1314 }; lib/goog/json/json.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview JSON utility functions. 17 * @author arv@google.com (Erik Arvidsson) 18 */ 19 20 21 goog.provide('goog.json'); 22 goog.provide('goog.json.Replacer'); 23 goog.provide('goog.json.Reviver'); 24 goog.provide('goog.json.Serializer'); 25 26 27 /** 28 * @define {boolean} If true, use the native JSON parsing API. 29 * NOTE(user): EXPERIMENTAL, handle with care. Setting this to true might 30 * break your code. The default {@code goog.json.parse} implementation is able 31 * to handle invalid JSON, such as JSPB. 32 */ 33 goog.define('goog.json.USE_NATIVE_JSON', false); 34 35 36 /** 37 * Tests if a string is an invalid JSON string. This only ensures that we are 38 * not using any invalid characters 39 * @param {string} s The string to test. 40 * @return {boolean} True if the input is a valid JSON string. 41 * @private 42 */ 43 goog.json.isValid_ = function(s) { 44 // All empty whitespace is not valid. 45 if (/^\s*$/.test(s)) { 46 return false; 47 } 48 49 // This is taken from http://www.json.org/json2.js which is released to the 50 // public domain. 51 // Changes: We dissallow \u2028 Line separator and \u2029 Paragraph separator 52 // inside strings. We also treat \u2028 and \u2029 as whitespace which they 53 // are in the RFC but IE and Safari does not match \s to these so we need to 54 // include them in the reg exps in all places where whitespace is allowed. 55 // We allowed \x7f inside strings because some tools don't escape it, 56 // e.g. http://www.json.org/java/org/json/JSONObject.java 57 58 // Parsing happens in three stages. In the first stage, we run the text 59 // against regular expressions that look for non-JSON patterns. We are 60 // especially concerned with '()' and 'new' because they can cause invocation, 61 // and '=' because it can cause mutation. But just to be safe, we want to 62 // reject all unexpected forms. 63 64 // We split the first stage into 4 regexp operations in order to work around 65 // crippling inefficiencies in IE's and Safari's regexp engines. First we 66 // replace all backslash pairs with '@' (a non-JSON character). Second, we 67 // replace all simple value tokens with ']' characters. Third, we delete all 68 // open brackets that follow a colon or comma or that begin the text. Finally, 69 // we look to see that the remaining characters are only whitespace or ']' or 70 // ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. 71 72 // Don't make these static since they have the global flag. 73 var backslashesRe = /\\["\\\/bfnrtu]/g; 74 var simpleValuesRe = 75 /"[^"\\\n\r\u2028\u2029\x00-\x08\x0a-\x1f]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 76 var openBracketsRe = /(?:^|:|,)(?:[\s\u2028\u2029]*\[)+/g; 77 var remainderRe = /^[\],:{}\s\u2028\u2029]*$/; 78 79 return remainderRe.test(s.replace(backslashesRe, '@'). 80 replace(simpleValuesRe, ']'). 81 replace(openBracketsRe, '')); 82 }; 83 84 85 /** 86 * Parses a JSON string and returns the result. This throws an exception if 87 * the string is an invalid JSON string. 88 * 89 * Note that this is very slow on large strings. If you trust the source of 90 * the string then you should use unsafeParse instead. 91 * 92 * @param {*} s The JSON string to parse. 93 * @throws Error if s is invalid JSON. 94 * @return {Object} The object generated from the JSON string, or null. 95 */ 96 goog.json.parse = goog.json.USE_NATIVE_JSON ? 97 /** @type {function(*):Object} */ (goog.global['JSON']['parse']) : 98 function(s) { 99 var o = String(s); 100 if (goog.json.isValid_(o)) { 101 /** @preserveTry */ 102 try { 103 return /** @type {Object} */ (eval('(' + o + ')')); 104 } catch (ex) { 105 } 106 } 107 throw Error('Invalid JSON string: ' + o); 108 }; 109 110 111 /** 112 * Parses a JSON string and returns the result. This uses eval so it is open 113 * to security issues and it should only be used if you trust the source. 114 * 115 * @param {string} s The JSON string to parse. 116 * @return {Object} The object generated from the JSON string. 117 */ 118 goog.json.unsafeParse = goog.json.USE_NATIVE_JSON ? 119 /** @type {function(string):Object} */ (goog.global['JSON']['parse']) : 120 function(s) { 121 return /** @type {Object} */ (eval('(' + s + ')')); 122 }; 123 124 125 /** 126 * JSON replacer, as defined in Section 15.12.3 of the ES5 spec. 127 * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3 128 * 129 * TODO(nicksantos): Array should also be a valid replacer. 130 * 131 * @typedef {function(this:Object, string, *): *} 132 */ 133 goog.json.Replacer; 134 135 136 /** 137 * JSON reviver, as defined in Section 15.12.2 of the ES5 spec. 138 * @see http://ecma-international.org/ecma-262/5.1/#sec-15.12.3 139 * 140 * @typedef {function(this:Object, string, *): *} 141 */ 142 goog.json.Reviver; 143 144 145 /** 146 * Serializes an object or a value to a JSON string. 147 * 148 * @param {*} object The object to serialize. 149 * @param {?goog.json.Replacer=} opt_replacer A replacer function 150 * called for each (key, value) pair that determines how the value 151 * should be serialized. By defult, this just returns the value 152 * and allows default serialization to kick in. 153 * @throws Error if there are loops in the object graph. 154 * @return {string} A JSON string representation of the input. 155 */ 156 goog.json.serialize = goog.json.USE_NATIVE_JSON ? 157 /** @type {function(*, ?goog.json.Replacer=):string} */ 158 (goog.global['JSON']['stringify']) : 159 function(object, opt_replacer) { 160 // NOTE(nicksantos): Currently, we never use JSON.stringify. 161 // 162 // The last time I evaluated this, JSON.stringify had subtle bugs and 163 // behavior differences on all browsers, and the performance win was not 164 // large enough to justify all the issues. This may change in the future 165 // as browser implementations get better. 166 // 167 // assertSerialize in json_test contains if branches for the cases 168 // that fail. 169 return new goog.json.Serializer(opt_replacer).serialize(object); 170 }; 171 172 173 174 /** 175 * Class that is used to serialize JSON objects to a string. 176 * @param {?goog.json.Replacer=} opt_replacer Replacer. 177 * @constructor 178 */ 179 goog.json.Serializer = function(opt_replacer) { 180 /** 181 * @type {goog.json.Replacer|null|undefined} 182 * @private 183 */ 184 this.replacer_ = opt_replacer; 185 }; 186 187 188 /** 189 * Serializes an object or a value to a JSON string. 190 * 191 * @param {*} object The object to serialize. 192 * @throws Error if there are loops in the object graph. 193 * @return {string} A JSON string representation of the input. 194 */ 195 goog.json.Serializer.prototype.serialize = function(object) { 196 var sb = []; 197 this.serializeInternal(object, sb); 198 return sb.join(''); 199 }; 200 201 202 /** 203 * Serializes a generic value to a JSON string 204 * @protected 205 * @param {*} object The object to serialize. 206 * @param {Array} sb Array used as a string builder. 207 * @throws Error if there are loops in the object graph. 208 */ 209 goog.json.Serializer.prototype.serializeInternal = function(object, sb) { 210 switch (typeof object) { 211 case 'string': 212 this.serializeString_(/** @type {string} */ (object), sb); 213 break; 214 case 'number': 215 this.serializeNumber_(/** @type {number} */ (object), sb); 216 break; 217 case 'boolean': 218 sb.push(object); 219 break; 220 case 'undefined': 221 sb.push('null'); 222 break; 223 case 'object': 224 if (object == null) { 225 sb.push('null'); 226 break; 227 } 228 if (goog.isArray(object)) { 229 this.serializeArray(/** @type {!Array} */ (object), sb); 230 break; 231 } 232 // should we allow new String, new Number and new Boolean to be treated 233 // as string, number and boolean? Most implementations do not and the 234 // need is not very big 235 this.serializeObject_(/** @type {Object} */ (object), sb); 236 break; 237 case 'function': 238 // Skip functions. 239 // TODO(user) Should we return something here? 240 break; 241 default: 242 throw Error('Unknown type: ' + typeof object); 243 } 244 }; 245 246 247 /** 248 * Character mappings used internally for goog.string.quote 249 * @private 250 * @type {!Object} 251 */ 252 goog.json.Serializer.charToJsonCharCache_ = { 253 '\"': '\\"', 254 '\\': '\\\\', 255 '/': '\\/', 256 '\b': '\\b', 257 '\f': '\\f', 258 '\n': '\\n', 259 '\r': '\\r', 260 '\t': '\\t', 261 262 '\x0B': '\\u000b' // '\v' is not supported in JScript 263 }; 264 265 266 /** 267 * Regular expression used to match characters that need to be replaced. 268 * The S60 browser has a bug where unicode characters are not matched by 269 * regular expressions. The condition below detects such behaviour and 270 * adjusts the regular expression accordingly. 271 * @private 272 * @type {!RegExp} 273 */ 274 goog.json.Serializer.charsToReplace_ = /\uffff/.test('\uffff') ? 275 /[\\\"\x00-\x1f\x7f-\uffff]/g : /[\\\"\x00-\x1f\x7f-\xff]/g; 276 277 278 /** 279 * Serializes a string to a JSON string 280 * @private 281 * @param {string} s The string to serialize. 282 * @param {Array} sb Array used as a string builder. 283 */ 284 goog.json.Serializer.prototype.serializeString_ = function(s, sb) { 285 // The official JSON implementation does not work with international 286 // characters. 287 sb.push('"', s.replace(goog.json.Serializer.charsToReplace_, function(c) { 288 // caching the result improves performance by a factor 2-3 289 if (c in goog.json.Serializer.charToJsonCharCache_) { 290 return goog.json.Serializer.charToJsonCharCache_[c]; 291 } 292 293 var cc = c.charCodeAt(0); 294 var rv = '\\u'; 295 if (cc < 16) { 296 rv += '000'; 297 } else if (cc < 256) { 298 rv += '00'; 299 } else if (cc < 4096) { // \u1000 300 rv += '0'; 301 } 302 return goog.json.Serializer.charToJsonCharCache_[c] = rv + cc.toString(16); 303 }), '"'); 304 }; 305 306 307 /** 308 * Serializes a number to a JSON string 309 * @private 310 * @param {number} n The number to serialize. 311 * @param {Array} sb Array used as a string builder. 312 */ 313 goog.json.Serializer.prototype.serializeNumber_ = function(n, sb) { 314 sb.push(isFinite(n) && !isNaN(n) ? n : 'null'); 315 }; 316 317 318 /** 319 * Serializes an array to a JSON string 320 * @param {Array} arr The array to serialize. 321 * @param {Array} sb Array used as a string builder. 322 * @protected 323 */ 324 goog.json.Serializer.prototype.serializeArray = function(arr, sb) { 325 var l = arr.length; 326 sb.push('['); 327 var sep = ''; 328 for (var i = 0; i < l; i++) { 329 sb.push(sep); 330 331 var value = arr[i]; 332 this.serializeInternal( 333 this.replacer_ ? this.replacer_.call(arr, String(i), value) : value, 334 sb); 335 336 sep = ','; 337 } 338 sb.push(']'); 339 }; 340 341 342 /** 343 * Serializes an object to a JSON string 344 * @private 345 * @param {Object} obj The object to serialize. 346 * @param {Array} sb Array used as a string builder. 347 */ 348 goog.json.Serializer.prototype.serializeObject_ = function(obj, sb) { 349 sb.push('{'); 350 var sep = ''; 351 for (var key in obj) { 352 if (Object.prototype.hasOwnProperty.call(obj, key)) { 353 var value = obj[key]; 354 // Skip functions. 355 // TODO(ptucker) Should we return something for function properties? 356 if (typeof value != 'function') { 357 sb.push(sep); 358 this.serializeString_(key, sb); 359 sb.push(':'); 360 361 this.serializeInternal( 362 this.replacer_ ? this.replacer_.call(obj, key, value) : value, 363 sb); 364 365 sep = ','; 366 } 367 } 368 } 369 sb.push('}'); 370 }; lib/goog/labs/testing/assertthat.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides main functionality of assertThat. assertThat calls the 17 * matcher's matches method to test if a matcher matches assertThat's arguments. 18 */ 19 20 21 goog.provide('goog.labs.testing.MatcherError'); 22 goog.provide('goog.labs.testing.assertThat'); 23 24 goog.require('goog.asserts'); 25 goog.require('goog.debug.Error'); 26 goog.require('goog.labs.testing.Matcher'); 27 28 29 /** 30 * Asserts that the actual value evaluated by the matcher is true. 31 * 32 * @param {*} actual The object to assert by the matcher. 33 * @param {!goog.labs.testing.Matcher} matcher A matcher to verify values. 34 * @param {string=} opt_reason Description of what is asserted. 35 * 36 */ 37 goog.labs.testing.assertThat = function(actual, matcher, opt_reason) { 38 if (!matcher.matches(actual)) { 39 // Prefix the error description with a reason from the assert ? 40 var prefix = opt_reason ? opt_reason + ': ' : ''; 41 var desc = prefix + matcher.describe(actual); 42 43 // some sort of failure here 44 throw new goog.labs.testing.MatcherError(desc); 45 } 46 }; 47 48 49 50 /** 51 * Error thrown when a Matcher fails to match the input value. 52 * @param {string=} opt_message The error message. 53 * @constructor 54 * @extends {goog.debug.Error} 55 * @final 56 */ 57 goog.labs.testing.MatcherError = function(opt_message) { 58 goog.labs.testing.MatcherError.base(this, 'constructor', opt_message); 59 }; 60 goog.inherits(goog.labs.testing.MatcherError, goog.debug.Error); lib/goog/labs/testing/logicmatcher.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides the built-in logic matchers: anyOf, allOf, and isNot. 17 * 18 */ 19 20 21 goog.provide('goog.labs.testing.AllOfMatcher'); 22 goog.provide('goog.labs.testing.AnyOfMatcher'); 23 goog.provide('goog.labs.testing.IsNotMatcher'); 24 25 26 goog.require('goog.array'); 27 goog.require('goog.labs.testing.Matcher'); 28 29 30 31 /** 32 * The AllOf matcher. 33 * 34 * @param {!Array.<!goog.labs.testing.Matcher>} matchers Input matchers. 35 * 36 * @constructor 37 * @struct 38 * @implements {goog.labs.testing.Matcher} 39 * @final 40 */ 41 goog.labs.testing.AllOfMatcher = function(matchers) { 42 /** 43 * @type {!Array.<!goog.labs.testing.Matcher>} 44 * @private 45 */ 46 this.matchers_ = matchers; 47 }; 48 49 50 /** 51 * Determines if all of the matchers match the input value. 52 * 53 * @override 54 */ 55 goog.labs.testing.AllOfMatcher.prototype.matches = function(actualValue) { 56 return goog.array.every(this.matchers_, function(matcher) { 57 return matcher.matches(actualValue); 58 }); 59 }; 60 61 62 /** 63 * Describes why the matcher failed. The returned string is a concatenation of 64 * all the failed matchers' error strings. 65 * 66 * @override 67 */ 68 goog.labs.testing.AllOfMatcher.prototype.describe = 69 function(actualValue) { 70 // TODO(user) : Optimize this to remove duplication with matches ? 71 var errorString = ''; 72 goog.array.forEach(this.matchers_, function(matcher) { 73 if (!matcher.matches(actualValue)) { 74 errorString += matcher.describe(actualValue) + '\n'; 75 } 76 }); 77 return errorString; 78 }; 79 80 81 82 /** 83 * The AnyOf matcher. 84 * 85 * @param {!Array.<!goog.labs.testing.Matcher>} matchers Input matchers. 86 * 87 * @constructor 88 * @struct 89 * @implements {goog.labs.testing.Matcher} 90 * @final 91 */ 92 goog.labs.testing.AnyOfMatcher = function(matchers) { 93 /** 94 * @type {!Array.<!goog.labs.testing.Matcher>} 95 * @private 96 */ 97 this.matchers_ = matchers; 98 }; 99 100 101 /** 102 * Determines if any of the matchers matches the input value. 103 * 104 * @override 105 */ 106 goog.labs.testing.AnyOfMatcher.prototype.matches = function(actualValue) { 107 return goog.array.some(this.matchers_, function(matcher) { 108 return matcher.matches(actualValue); 109 }); 110 }; 111 112 113 /** 114 * Describes why the matcher failed. 115 * 116 * @override 117 */ 118 goog.labs.testing.AnyOfMatcher.prototype.describe = 119 function(actualValue) { 120 // TODO(user) : Optimize this to remove duplication with matches ? 121 var errorString = ''; 122 goog.array.forEach(this.matchers_, function(matcher) { 123 if (!matcher.matches(actualValue)) { 124 errorString += matcher.describe(actualValue) + '\n'; 125 } 126 }); 127 return errorString; 128 }; 129 130 131 132 /** 133 * The IsNot matcher. 134 * 135 * @param {!goog.labs.testing.Matcher} matcher The matcher to negate. 136 * 137 * @constructor 138 * @struct 139 * @implements {goog.labs.testing.Matcher} 140 * @final 141 */ 142 goog.labs.testing.IsNotMatcher = function(matcher) { 143 /** 144 * @type {!goog.labs.testing.Matcher} 145 * @private 146 */ 147 this.matcher_ = matcher; 148 }; 149 150 151 /** 152 * Determines if the input value doesn't satisfy a matcher. 153 * 154 * @override 155 */ 156 goog.labs.testing.IsNotMatcher.prototype.matches = function(actualValue) { 157 return !this.matcher_.matches(actualValue); 158 }; 159 160 161 /** 162 * Describes why the matcher failed. 163 * 164 * @override 165 */ 166 goog.labs.testing.IsNotMatcher.prototype.describe = 167 function(actualValue) { 168 return 'The following is false: ' + this.matcher_.describe(actualValue); 169 }; 170 171 172 /** 173 * Creates a matcher that will succeed only if all of the given matchers 174 * succeed. 175 * 176 * @param {...goog.labs.testing.Matcher} var_args The matchers to test 177 * against. 178 * 179 * @return {!goog.labs.testing.AllOfMatcher} The AllOf matcher. 180 */ 181 function allOf(var_args) { 182 var matchers = goog.array.toArray(arguments); 183 return new goog.labs.testing.AllOfMatcher(matchers); 184 } 185 186 187 /** 188 * Accepts a set of matchers and returns a matcher which matches 189 * values which satisfy the constraints of any of the given matchers. 190 * 191 * @param {...goog.labs.testing.Matcher} var_args The matchers to test 192 * against. 193 * 194 * @return {!goog.labs.testing.AnyOfMatcher} The AnyOf matcher. 195 */ 196 function anyOf(var_args) { 197 var matchers = goog.array.toArray(arguments); 198 return new goog.labs.testing.AnyOfMatcher(matchers); 199 } 200 201 202 /** 203 * Returns a matcher that negates the input matcher. The returned 204 * matcher matches the values not matched by the input matcher and vice-versa. 205 * 206 * @param {!goog.labs.testing.Matcher} matcher The matcher to test against. 207 * 208 * @return {!goog.labs.testing.IsNotMatcher} The IsNot matcher. 209 */ 210 function isNot(matcher) { 211 return new goog.labs.testing.IsNotMatcher(matcher); 212 } lib/goog/labs/testing/matcher.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides the base Matcher interface. User code should use the 17 * matchers through assertThat statements and not directly. 18 */ 19 20 21 goog.provide('goog.labs.testing.Matcher'); 22 23 24 25 /** 26 * A matcher object to be used in assertThat statements. 27 * @interface 28 */ 29 goog.labs.testing.Matcher = function() {}; 30 31 32 /** 33 * Determines whether a value matches the constraints of the match. 34 * 35 * @param {*} value The object to match. 36 * @return {boolean} Whether the input value matches this matcher. 37 */ 38 goog.labs.testing.Matcher.prototype.matches = function(value) {}; 39 40 41 /** 42 * Describes why the matcher failed. 43 * 44 * @param {*} value The value that didn't match. 45 * @param {string=} opt_description A partial description to which the reason 46 * will be appended. 47 * 48 * @return {string} Description of why the matcher failed. 49 */ 50 goog.labs.testing.Matcher.prototype.describe = 51 function(value, opt_description) {}; 52 53 54 /** 55 * Generates a Matcher from the ‘matches’ and ‘describe’ functions passed in. 56 * 57 * @param {!Function} matchesFunction The ‘matches’ function. 58 * @param {Function=} opt_describeFunction The ‘describe’ function. 59 * @return {!Function} The custom matcher. 60 */ 61 goog.labs.testing.Matcher.makeMatcher = 62 function(matchesFunction, opt_describeFunction) { 63 64 /** 65 * @constructor 66 * @implements {goog.labs.testing.Matcher} 67 * @final 68 */ 69 var matcherConstructor = function() {}; 70 71 /** @override */ 72 matcherConstructor.prototype.matches = matchesFunction; 73 74 if (opt_describeFunction) { 75 /** @override */ 76 matcherConstructor.prototype.describe = opt_describeFunction; 77 } 78 79 return matcherConstructor; 80 }; lib/goog/labs/testing/numbermatcher.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides the built-in number matchers like lessThan, 17 * greaterThan, etc. 18 */ 19 20 21 goog.provide('goog.labs.testing.CloseToMatcher'); 22 goog.provide('goog.labs.testing.EqualToMatcher'); 23 goog.provide('goog.labs.testing.GreaterThanEqualToMatcher'); 24 goog.provide('goog.labs.testing.GreaterThanMatcher'); 25 goog.provide('goog.labs.testing.LessThanEqualToMatcher'); 26 goog.provide('goog.labs.testing.LessThanMatcher'); 27 28 29 goog.require('goog.asserts'); 30 goog.require('goog.labs.testing.Matcher'); 31 32 33 34 /** 35 * The GreaterThan matcher. 36 * 37 * @param {number} value The value to compare. 38 * 39 * @constructor 40 * @struct 41 * @implements {goog.labs.testing.Matcher} 42 * @final 43 */ 44 goog.labs.testing.GreaterThanMatcher = function(value) { 45 /** 46 * @type {number} 47 * @private 48 */ 49 this.value_ = value; 50 }; 51 52 53 /** 54 * Determines if input value is greater than the expected value. 55 * 56 * @override 57 */ 58 goog.labs.testing.GreaterThanMatcher.prototype.matches = function(actualValue) { 59 goog.asserts.assertNumber(actualValue); 60 return actualValue > this.value_; 61 }; 62 63 64 /** 65 * @override 66 */ 67 goog.labs.testing.GreaterThanMatcher.prototype.describe = 68 function(actualValue) { 69 goog.asserts.assertNumber(actualValue); 70 return actualValue + ' is not greater than ' + this.value_; 71 }; 72 73 74 75 /** 76 * The lessThan matcher. 77 * 78 * @param {number} value The value to compare. 79 * 80 * @constructor 81 * @struct 82 * @implements {goog.labs.testing.Matcher} 83 * @final 84 */ 85 goog.labs.testing.LessThanMatcher = function(value) { 86 /** 87 * @type {number} 88 * @private 89 */ 90 this.value_ = value; 91 }; 92 93 94 /** 95 * Determines if the input value is less than the expected value. 96 * 97 * @override 98 */ 99 goog.labs.testing.LessThanMatcher.prototype.matches = function(actualValue) { 100 goog.asserts.assertNumber(actualValue); 101 return actualValue < this.value_; 102 }; 103 104 105 /** 106 * @override 107 */ 108 goog.labs.testing.LessThanMatcher.prototype.describe = 109 function(actualValue) { 110 goog.asserts.assertNumber(actualValue); 111 return actualValue + ' is not less than ' + this.value_; 112 }; 113 114 115 116 /** 117 * The GreaterThanEqualTo matcher. 118 * 119 * @param {number} value The value to compare. 120 * 121 * @constructor 122 * @struct 123 * @implements {goog.labs.testing.Matcher} 124 * @final 125 */ 126 goog.labs.testing.GreaterThanEqualToMatcher = function(value) { 127 /** 128 * @type {number} 129 * @private 130 */ 131 this.value_ = value; 132 }; 133 134 135 /** 136 * Determines if the input value is greater than equal to the expected value. 137 * 138 * @override 139 */ 140 goog.labs.testing.GreaterThanEqualToMatcher.prototype.matches = 141 function(actualValue) { 142 goog.asserts.assertNumber(actualValue); 143 return actualValue >= this.value_; 144 }; 145 146 147 /** 148 * @override 149 */ 150 goog.labs.testing.GreaterThanEqualToMatcher.prototype.describe = 151 function(actualValue) { 152 goog.asserts.assertNumber(actualValue); 153 return actualValue + ' is not greater than equal to ' + this.value_; 154 }; 155 156 157 158 /** 159 * The LessThanEqualTo matcher. 160 * 161 * @param {number} value The value to compare. 162 * 163 * @constructor 164 * @struct 165 * @implements {goog.labs.testing.Matcher} 166 * @final 167 */ 168 goog.labs.testing.LessThanEqualToMatcher = function(value) { 169 /** 170 * @type {number} 171 * @private 172 */ 173 this.value_ = value; 174 }; 175 176 177 /** 178 * Determines if the input value is less than or equal to the expected value. 179 * 180 * @override 181 */ 182 goog.labs.testing.LessThanEqualToMatcher.prototype.matches = 183 function(actualValue) { 184 goog.asserts.assertNumber(actualValue); 185 return actualValue <= this.value_; 186 }; 187 188 189 /** 190 * @override 191 */ 192 goog.labs.testing.LessThanEqualToMatcher.prototype.describe = 193 function(actualValue) { 194 goog.asserts.assertNumber(actualValue); 195 return actualValue + ' is not less than equal to ' + this.value_; 196 }; 197 198 199 200 /** 201 * The EqualTo matcher. 202 * 203 * @param {number} value The value to compare. 204 * 205 * @constructor 206 * @struct 207 * @implements {goog.labs.testing.Matcher} 208 * @final 209 */ 210 goog.labs.testing.EqualToMatcher = function(value) { 211 /** 212 * @type {number} 213 * @private 214 */ 215 this.value_ = value; 216 }; 217 218 219 /** 220 * Determines if the input value is equal to the expected value. 221 * 222 * @override 223 */ 224 goog.labs.testing.EqualToMatcher.prototype.matches = function(actualValue) { 225 goog.asserts.assertNumber(actualValue); 226 return actualValue === this.value_; 227 }; 228 229 230 /** 231 * @override 232 */ 233 goog.labs.testing.EqualToMatcher.prototype.describe = 234 function(actualValue) { 235 goog.asserts.assertNumber(actualValue); 236 return actualValue + ' is not equal to ' + this.value_; 237 }; 238 239 240 241 /** 242 * The CloseTo matcher. 243 * 244 * @param {number} value The value to compare. 245 * @param {number} range The range to check within. 246 * 247 * @constructor 248 * @struct 249 * @implements {goog.labs.testing.Matcher} 250 * @final 251 */ 252 goog.labs.testing.CloseToMatcher = function(value, range) { 253 /** 254 * @type {number} 255 * @private 256 */ 257 this.value_ = value; 258 /** 259 * @type {number} 260 * @private 261 */ 262 this.range_ = range; 263 }; 264 265 266 /** 267 * Determines if input value is within a certain range of the expected value. 268 * 269 * @override 270 */ 271 goog.labs.testing.CloseToMatcher.prototype.matches = function(actualValue) { 272 goog.asserts.assertNumber(actualValue); 273 return Math.abs(this.value_ - actualValue) < this.range_; 274 }; 275 276 277 /** 278 * @override 279 */ 280 goog.labs.testing.CloseToMatcher.prototype.describe = 281 function(actualValue) { 282 goog.asserts.assertNumber(actualValue); 283 return actualValue + ' is not close to(' + this.range_ + ') ' + this.value_; 284 }; 285 286 287 /** 288 * @param {number} value The expected value. 289 * 290 * @return {!goog.labs.testing.GreaterThanMatcher} A GreaterThanMatcher. 291 */ 292 function greaterThan(value) { 293 return new goog.labs.testing.GreaterThanMatcher(value); 294 } 295 296 297 /** 298 * @param {number} value The expected value. 299 * 300 * @return {!goog.labs.testing.GreaterThanEqualToMatcher} A 301 * GreaterThanEqualToMatcher. 302 */ 303 function greaterThanEqualTo(value) { 304 return new goog.labs.testing.GreaterThanEqualToMatcher(value); 305 } 306 307 308 /** 309 * @param {number} value The expected value. 310 * 311 * @return {!goog.labs.testing.LessThanMatcher} A LessThanMatcher. 312 */ 313 function lessThan(value) { 314 return new goog.labs.testing.LessThanMatcher(value); 315 } 316 317 318 /** 319 * @param {number} value The expected value. 320 * 321 * @return {!goog.labs.testing.LessThanEqualToMatcher} A LessThanEqualToMatcher. 322 */ 323 function lessThanEqualTo(value) { 324 return new goog.labs.testing.LessThanEqualToMatcher(value); 325 } 326 327 328 /** 329 * @param {number} value The expected value. 330 * 331 * @return {!goog.labs.testing.EqualToMatcher} An EqualToMatcher. 332 */ 333 function equalTo(value) { 334 return new goog.labs.testing.EqualToMatcher(value); 335 } 336 337 338 /** 339 * @param {number} value The expected value. 340 * @param {number} range The maximum allowed difference from the expected value. 341 * 342 * @return {!goog.labs.testing.CloseToMatcher} A CloseToMatcher. 343 */ 344 function closeTo(value, range) { 345 return new goog.labs.testing.CloseToMatcher(value, range); 346 } lib/goog/labs/testing/objectmatcher.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides the built-in object matchers like equalsObject, 17 * hasProperty, instanceOf, etc. 18 */ 19 20 21 22 goog.provide('goog.labs.testing.HasPropertyMatcher'); 23 goog.provide('goog.labs.testing.InstanceOfMatcher'); 24 goog.provide('goog.labs.testing.IsNullMatcher'); 25 goog.provide('goog.labs.testing.IsNullOrUndefinedMatcher'); 26 goog.provide('goog.labs.testing.IsUndefinedMatcher'); 27 goog.provide('goog.labs.testing.ObjectEqualsMatcher'); 28 29 30 goog.require('goog.labs.testing.Matcher'); 31 goog.require('goog.string'); 32 33 34 35 /** 36 * The Equals matcher. 37 * 38 * @param {!Object} expectedObject The expected object. 39 * 40 * @constructor 41 * @struct 42 * @implements {goog.labs.testing.Matcher} 43 * @final 44 */ 45 goog.labs.testing.ObjectEqualsMatcher = function(expectedObject) { 46 /** 47 * @type {!Object} 48 * @private 49 */ 50 this.object_ = expectedObject; 51 }; 52 53 54 /** 55 * Determines if two objects are the same. 56 * 57 * @override 58 */ 59 goog.labs.testing.ObjectEqualsMatcher.prototype.matches = 60 function(actualObject) { 61 return actualObject === this.object_; 62 }; 63 64 65 /** 66 * @override 67 */ 68 goog.labs.testing.ObjectEqualsMatcher.prototype.describe = 69 function(actualObject) { 70 return 'Input object is not the same as the expected object.'; 71 }; 72 73 74 75 /** 76 * The HasProperty matcher. 77 * 78 * @param {string} property Name of the property to test. 79 * 80 * @constructor 81 * @struct 82 * @implements {goog.labs.testing.Matcher} 83 * @final 84 */ 85 goog.labs.testing.HasPropertyMatcher = function(property) { 86 /** 87 * @type {string} 88 * @private 89 */ 90 this.property_ = property; 91 }; 92 93 94 /** 95 * Determines if an object has a property. 96 * 97 * @override 98 */ 99 goog.labs.testing.HasPropertyMatcher.prototype.matches = 100 function(actualObject) { 101 return this.property_ in actualObject; 102 }; 103 104 105 /** 106 * @override 107 */ 108 goog.labs.testing.HasPropertyMatcher.prototype.describe = 109 function(actualObject) { 110 return 'Object does not have property: ' + this.property_; 111 }; 112 113 114 115 /** 116 * The InstanceOf matcher. 117 * 118 * @param {!Object} object The expected class object. 119 * 120 * @constructor 121 * @struct 122 * @implements {goog.labs.testing.Matcher} 123 * @final 124 */ 125 goog.labs.testing.InstanceOfMatcher = function(object) { 126 /** 127 * @type {!Object} 128 * @private 129 */ 130 this.object_ = object; 131 }; 132 133 134 /** 135 * Determines if an object is an instance of another object. 136 * 137 * @override 138 */ 139 goog.labs.testing.InstanceOfMatcher.prototype.matches = 140 function(actualObject) { 141 return actualObject instanceof this.object_; 142 }; 143 144 145 /** 146 * @override 147 */ 148 goog.labs.testing.InstanceOfMatcher.prototype.describe = 149 function(actualObject) { 150 return 'Input object is not an instance of the expected object'; 151 }; 152 153 154 155 /** 156 * The IsNullOrUndefined matcher. 157 * 158 * @constructor 159 * @struct 160 * @implements {goog.labs.testing.Matcher} 161 * @final 162 */ 163 goog.labs.testing.IsNullOrUndefinedMatcher = function() {}; 164 165 166 /** 167 * Determines if input value is null or undefined. 168 * 169 * @override 170 */ 171 goog.labs.testing.IsNullOrUndefinedMatcher.prototype.matches = 172 function(actualValue) { 173 return !goog.isDefAndNotNull(actualValue); 174 }; 175 176 177 /** 178 * @override 179 */ 180 goog.labs.testing.IsNullOrUndefinedMatcher.prototype.describe = 181 function(actualValue) { 182 return actualValue + ' is not null or undefined.'; 183 }; 184 185 186 187 /** 188 * The IsNull matcher. 189 * 190 * @constructor 191 * @struct 192 * @implements {goog.labs.testing.Matcher} 193 * @final 194 */ 195 goog.labs.testing.IsNullMatcher = function() {}; 196 197 198 /** 199 * Determines if input value is null. 200 * 201 * @override 202 */ 203 goog.labs.testing.IsNullMatcher.prototype.matches = 204 function(actualValue) { 205 return goog.isNull(actualValue); 206 }; 207 208 209 /** 210 * @override 211 */ 212 goog.labs.testing.IsNullMatcher.prototype.describe = 213 function(actualValue) { 214 return actualValue + ' is not null.'; 215 }; 216 217 218 219 /** 220 * The IsUndefined matcher. 221 * 222 * @constructor 223 * @struct 224 * @implements {goog.labs.testing.Matcher} 225 * @final 226 */ 227 goog.labs.testing.IsUndefinedMatcher = function() {}; 228 229 230 /** 231 * Determines if input value is undefined. 232 * 233 * @override 234 */ 235 goog.labs.testing.IsUndefinedMatcher.prototype.matches = 236 function(actualValue) { 237 return !goog.isDef(actualValue); 238 }; 239 240 241 /** 242 * @override 243 */ 244 goog.labs.testing.IsUndefinedMatcher.prototype.describe = 245 function(actualValue) { 246 return actualValue + ' is not undefined.'; 247 }; 248 249 250 /** 251 * Returns a matcher that matches objects that are equal to the input object. 252 * Equality in this case means the two objects are references to the same 253 * object. 254 * 255 * @param {!Object} object The expected object. 256 * 257 * @return {!goog.labs.testing.ObjectEqualsMatcher} A 258 * ObjectEqualsMatcher. 259 */ 260 function equalsObject(object) { 261 return new goog.labs.testing.ObjectEqualsMatcher(object); 262 } 263 264 265 /** 266 * Returns a matcher that matches objects that contain the input property. 267 * 268 * @param {string} property The property name to check. 269 * 270 * @return {!goog.labs.testing.HasPropertyMatcher} A HasPropertyMatcher. 271 */ 272 function hasProperty(property) { 273 return new goog.labs.testing.HasPropertyMatcher(property); 274 } 275 276 277 /** 278 * Returns a matcher that matches instances of the input class. 279 * 280 * @param {!Object} object The class object. 281 * 282 * @return {!goog.labs.testing.InstanceOfMatcher} A 283 * InstanceOfMatcher. 284 */ 285 function instanceOfClass(object) { 286 return new goog.labs.testing.InstanceOfMatcher(object); 287 } 288 289 290 /** 291 * Returns a matcher that matches all null values. 292 * 293 * @return {!goog.labs.testing.IsNullMatcher} A IsNullMatcher. 294 */ 295 function isNull() { 296 return new goog.labs.testing.IsNullMatcher(); 297 } 298 299 300 /** 301 * Returns a matcher that matches all null and undefined values. 302 * 303 * @return {!goog.labs.testing.IsNullOrUndefinedMatcher} A 304 * IsNullOrUndefinedMatcher. 305 */ 306 function isNullOrUndefined() { 307 return new goog.labs.testing.IsNullOrUndefinedMatcher(); 308 } 309 310 311 /** 312 * Returns a matcher that matches undefined values. 313 * 314 * @return {!goog.labs.testing.IsUndefinedMatcher} A IsUndefinedMatcher. 315 */ 316 function isUndefined() { 317 return new goog.labs.testing.IsUndefinedMatcher(); 318 } lib/goog/labs/testing/stringmatcher.js
1 // Copyright 2012 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides the built-in string matchers like containsString, 17 * startsWith, endsWith, etc. 18 */ 19 20 21 goog.provide('goog.labs.testing.ContainsStringMatcher'); 22 goog.provide('goog.labs.testing.EndsWithMatcher'); 23 goog.provide('goog.labs.testing.EqualToIgnoringCaseMatcher'); 24 goog.provide('goog.labs.testing.EqualToIgnoringWhitespaceMatcher'); 25 goog.provide('goog.labs.testing.EqualsMatcher'); 26 goog.provide('goog.labs.testing.RegexMatcher'); 27 goog.provide('goog.labs.testing.StartsWithMatcher'); 28 goog.provide('goog.labs.testing.StringContainsInOrderMatcher'); 29 30 31 goog.require('goog.asserts'); 32 goog.require('goog.labs.testing.Matcher'); 33 goog.require('goog.string'); 34 35 36 37 /** 38 * The ContainsString matcher. 39 * 40 * @param {string} value The expected string. 41 * 42 * @constructor 43 * @struct 44 * @implements {goog.labs.testing.Matcher} 45 * @final 46 */ 47 goog.labs.testing.ContainsStringMatcher = function(value) { 48 /** 49 * @type {string} 50 * @private 51 */ 52 this.value_ = value; 53 }; 54 55 56 /** 57 * Determines if input string contains the expected string. 58 * 59 * @override 60 */ 61 goog.labs.testing.ContainsStringMatcher.prototype.matches = 62 function(actualValue) { 63 goog.asserts.assertString(actualValue); 64 return goog.string.contains(actualValue, this.value_); 65 }; 66 67 68 /** 69 * @override 70 */ 71 goog.labs.testing.ContainsStringMatcher.prototype.describe = 72 function(actualValue) { 73 return actualValue + ' does not contain ' + this.value_; 74 }; 75 76 77 78 /** 79 * The EndsWith matcher. 80 * 81 * @param {string} value The expected string. 82 * 83 * @constructor 84 * @struct 85 * @implements {goog.labs.testing.Matcher} 86 * @final 87 */ 88 goog.labs.testing.EndsWithMatcher = function(value) { 89 /** 90 * @type {string} 91 * @private 92 */ 93 this.value_ = value; 94 }; 95 96 97 /** 98 * Determines if input string ends with the expected string. 99 * 100 * @override 101 */ 102 goog.labs.testing.EndsWithMatcher.prototype.matches = function(actualValue) { 103 goog.asserts.assertString(actualValue); 104 return goog.string.endsWith(actualValue, this.value_); 105 }; 106 107 108 /** 109 * @override 110 */ 111 goog.labs.testing.EndsWithMatcher.prototype.describe = 112 function(actualValue) { 113 return actualValue + ' does not end with ' + this.value_; 114 }; 115 116 117 118 /** 119 * The EqualToIgnoringWhitespace matcher. 120 * 121 * @param {string} value The expected string. 122 * 123 * @constructor 124 * @struct 125 * @implements {goog.labs.testing.Matcher} 126 * @final 127 */ 128 goog.labs.testing.EqualToIgnoringWhitespaceMatcher = function(value) { 129 /** 130 * @type {string} 131 * @private 132 */ 133 this.value_ = value; 134 }; 135 136 137 /** 138 * Determines if input string contains the expected string. 139 * 140 * @override 141 */ 142 goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.matches = 143 function(actualValue) { 144 goog.asserts.assertString(actualValue); 145 var string1 = goog.string.collapseWhitespace(actualValue); 146 147 return goog.string.caseInsensitiveCompare(this.value_, string1) === 0; 148 }; 149 150 151 /** 152 * @override 153 */ 154 goog.labs.testing.EqualToIgnoringWhitespaceMatcher.prototype.describe = 155 function(actualValue) { 156 return actualValue + ' is not equal(ignoring whitespace) to ' + this.value_; 157 }; 158 159 160 161 /** 162 * The Equals matcher. 163 * 164 * @param {string} value The expected string. 165 * 166 * @constructor 167 * @struct 168 * @implements {goog.labs.testing.Matcher} 169 * @final 170 */ 171 goog.labs.testing.EqualsMatcher = function(value) { 172 /** 173 * @type {string} 174 * @private 175 */ 176 this.value_ = value; 177 }; 178 179 180 /** 181 * Determines if input string is equal to the expected string. 182 * 183 * @override 184 */ 185 goog.labs.testing.EqualsMatcher.prototype.matches = function(actualValue) { 186 goog.asserts.assertString(actualValue); 187 return this.value_ === actualValue; 188 }; 189 190 191 /** 192 * @override 193 */ 194 goog.labs.testing.EqualsMatcher.prototype.describe = 195 function(actualValue) { 196 return actualValue + ' is not equal to ' + this.value_; 197 }; 198 199 200 201 /** 202 * The MatchesRegex matcher. 203 * 204 * @param {!RegExp} regex The expected regex. 205 * 206 * @constructor 207 * @struct 208 * @implements {goog.labs.testing.Matcher} 209 * @final 210 */ 211 goog.labs.testing.RegexMatcher = function(regex) { 212 /** 213 * @type {!RegExp} 214 * @private 215 */ 216 this.regex_ = regex; 217 }; 218 219 220 /** 221 * Determines if input string is equal to the expected string. 222 * 223 * @override 224 */ 225 goog.labs.testing.RegexMatcher.prototype.matches = function( 226 actualValue) { 227 goog.asserts.assertString(actualValue); 228 return this.regex_.test(actualValue); 229 }; 230 231 232 /** 233 * @override 234 */ 235 goog.labs.testing.RegexMatcher.prototype.describe = 236 function(actualValue) { 237 return actualValue + ' does not match ' + this.regex_; 238 }; 239 240 241 242 /** 243 * The StartsWith matcher. 244 * 245 * @param {string} value The expected string. 246 * 247 * @constructor 248 * @struct 249 * @implements {goog.labs.testing.Matcher} 250 * @final 251 */ 252 goog.labs.testing.StartsWithMatcher = function(value) { 253 /** 254 * @type {string} 255 * @private 256 */ 257 this.value_ = value; 258 }; 259 260 261 /** 262 * Determines if input string starts with the expected string. 263 * 264 * @override 265 */ 266 goog.labs.testing.StartsWithMatcher.prototype.matches = function(actualValue) { 267 goog.asserts.assertString(actualValue); 268 return goog.string.startsWith(actualValue, this.value_); 269 }; 270 271 272 /** 273 * @override 274 */ 275 goog.labs.testing.StartsWithMatcher.prototype.describe = 276 function(actualValue) { 277 return actualValue + ' does not start with ' + this.value_; 278 }; 279 280 281 282 /** 283 * The StringContainsInOrdermatcher. 284 * 285 * @param {Array.<string>} values The expected string values. 286 * 287 * @constructor 288 * @struct 289 * @implements {goog.labs.testing.Matcher} 290 * @final 291 */ 292 goog.labs.testing.StringContainsInOrderMatcher = function(values) { 293 /** 294 * @type {Array.<string>} 295 * @private 296 */ 297 this.values_ = values; 298 }; 299 300 301 /** 302 * Determines if input string contains, in order, the expected array of strings. 303 * 304 * @override 305 */ 306 goog.labs.testing.StringContainsInOrderMatcher.prototype.matches = 307 function(actualValue) { 308 goog.asserts.assertString(actualValue); 309 var currentIndex, previousIndex = 0; 310 for (var i = 0; i < this.values_.length; i++) { 311 currentIndex = goog.string.contains(actualValue, this.values_[i]); 312 if (currentIndex < 0 || currentIndex < previousIndex) { 313 return false; 314 } 315 previousIndex = currentIndex; 316 } 317 return true; 318 }; 319 320 321 /** 322 * @override 323 */ 324 goog.labs.testing.StringContainsInOrderMatcher.prototype.describe = 325 function(actualValue) { 326 return actualValue + ' does not contain the expected values in order.'; 327 }; 328 329 330 /** 331 * Matches a string containing the given string. 332 * 333 * @param {string} value The expected value. 334 * 335 * @return {!goog.labs.testing.ContainsStringMatcher} A 336 * ContainsStringMatcher. 337 */ 338 function containsString(value) { 339 return new goog.labs.testing.ContainsStringMatcher(value); 340 } 341 342 343 /** 344 * Matches a string that ends with the given string. 345 * 346 * @param {string} value The expected value. 347 * 348 * @return {!goog.labs.testing.EndsWithMatcher} A 349 * EndsWithMatcher. 350 */ 351 function endsWith(value) { 352 return new goog.labs.testing.EndsWithMatcher(value); 353 } 354 355 356 /** 357 * Matches a string that equals (ignoring whitespace) the given string. 358 * 359 * @param {string} value The expected value. 360 * 361 * @return {!goog.labs.testing.EqualToIgnoringWhitespaceMatcher} A 362 * EqualToIgnoringWhitespaceMatcher. 363 */ 364 function equalToIgnoringWhitespace(value) { 365 return new goog.labs.testing.EqualToIgnoringWhitespaceMatcher(value); 366 } 367 368 369 /** 370 * Matches a string that equals the given string. 371 * 372 * @param {string} value The expected value. 373 * 374 * @return {!goog.labs.testing.EqualsMatcher} A EqualsMatcher. 375 */ 376 function equals(value) { 377 return new goog.labs.testing.EqualsMatcher(value); 378 } 379 380 381 /** 382 * Matches a string against a regular expression. 383 * 384 * @param {!RegExp} regex The expected regex. 385 * 386 * @return {!goog.labs.testing.RegexMatcher} A RegexMatcher. 387 */ 388 function matchesRegex(regex) { 389 return new goog.labs.testing.RegexMatcher(regex); 390 } 391 392 393 /** 394 * Matches a string that starts with the given string. 395 * 396 * @param {string} value The expected value. 397 * 398 * @return {!goog.labs.testing.StartsWithMatcher} A 399 * StartsWithMatcher. 400 */ 401 function startsWith(value) { 402 return new goog.labs.testing.StartsWithMatcher(value); 403 } 404 405 406 /** 407 * Matches a string that contains the given strings in order. 408 * 409 * @param {Array.<string>} values The expected value. 410 * 411 * @return {!goog.labs.testing.StringContainsInOrderMatcher} A 412 * StringContainsInOrderMatcher. 413 */ 414 function stringContainsInOrder(values) { 415 return new goog.labs.testing.StringContainsInOrderMatcher(values); 416 } lib/goog/labs/useragent/browser.js
1 // Copyright 2013 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Closure user agent detection (Browser). 17 * @see <a href="http://www.useragentstring.com/">User agent strings</a> 18 * For more information on rendering engine, platform, or device see the other 19 * sub-namespaces in goog.labs.userAgent, goog.labs.userAgent.platform, 20 * goog.labs.userAgent.device respectively.) 21 * 22 */ 23 24 goog.provide('goog.labs.userAgent.browser'); 25 26 goog.require('goog.array'); 27 goog.require('goog.asserts'); 28 goog.require('goog.labs.userAgent.util'); 29 goog.require('goog.string'); 30 31 32 /** 33 * @return {boolean} Whether the user's browser is Opera. 34 * @private 35 */ 36 goog.labs.userAgent.browser.matchOpera_ = function() { 37 return goog.labs.userAgent.util.matchUserAgent('Opera') || 38 goog.labs.userAgent.util.matchUserAgent('OPR'); 39 }; 40 41 42 /** 43 * @return {boolean} Whether the user's browser is IE. 44 * @private 45 */ 46 goog.labs.userAgent.browser.matchIE_ = function() { 47 return goog.labs.userAgent.util.matchUserAgent('Trident') || 48 goog.labs.userAgent.util.matchUserAgent('MSIE'); 49 }; 50 51 52 /** 53 * @return {boolean} Whether the user's browser is Firefox. 54 * @private 55 */ 56 goog.labs.userAgent.browser.matchFirefox_ = function() { 57 return goog.labs.userAgent.util.matchUserAgent('Firefox'); 58 }; 59 60 61 /** 62 * @return {boolean} Whether the user's browser is Safari. 63 * @private 64 */ 65 goog.labs.userAgent.browser.matchSafari_ = function() { 66 return goog.labs.userAgent.util.matchUserAgent('Safari') && 67 !goog.labs.userAgent.util.matchUserAgent('Chrome') && 68 !goog.labs.userAgent.util.matchUserAgent('CriOS') && 69 !goog.labs.userAgent.util.matchUserAgent('Android'); 70 }; 71 72 73 /** 74 * @return {boolean} Whether the user's browser is Chrome. 75 * @private 76 */ 77 goog.labs.userAgent.browser.matchChrome_ = function() { 78 return goog.labs.userAgent.util.matchUserAgent('Chrome') || 79 goog.labs.userAgent.util.matchUserAgent('CriOS'); 80 }; 81 82 83 /** 84 * @return {boolean} Whether the user's browser is the Android browser. 85 * @private 86 */ 87 goog.labs.userAgent.browser.matchAndroidBrowser_ = function() { 88 return goog.labs.userAgent.util.matchUserAgent('Android') && 89 !goog.labs.userAgent.util.matchUserAgent('Chrome') && 90 !goog.labs.userAgent.util.matchUserAgent('CriOS'); 91 }; 92 93 94 /** 95 * @return {boolean} Whether the user's browser is Opera. 96 */ 97 goog.labs.userAgent.browser.isOpera = goog.labs.userAgent.browser.matchOpera_; 98 99 100 /** 101 * @return {boolean} Whether the user's browser is IE. 102 */ 103 goog.labs.userAgent.browser.isIE = goog.labs.userAgent.browser.matchIE_; 104 105 106 /** 107 * @return {boolean} Whether the user's browser is Firefox. 108 */ 109 goog.labs.userAgent.browser.isFirefox = 110 goog.labs.userAgent.browser.matchFirefox_; 111 112 113 /** 114 * @return {boolean} Whether the user's browser is Safari. 115 */ 116 goog.labs.userAgent.browser.isSafari = 117 goog.labs.userAgent.browser.matchSafari_; 118 119 120 /** 121 * @return {boolean} Whether the user's browser is Chrome. 122 */ 123 goog.labs.userAgent.browser.isChrome = 124 goog.labs.userAgent.browser.matchChrome_; 125 126 127 /** 128 * @return {boolean} Whether the user's browser is the Android browser. 129 */ 130 goog.labs.userAgent.browser.isAndroidBrowser = 131 goog.labs.userAgent.browser.matchAndroidBrowser_; 132 133 134 /** 135 * For more information, see: 136 * http://docs.aws.amazon.com/silk/latest/developerguide/user-agent.html 137 * @return {boolean} Whether the user's browser is Silk. 138 */ 139 goog.labs.userAgent.browser.isSilk = function() { 140 return goog.labs.userAgent.util.matchUserAgent('Silk'); 141 }; 142 143 144 /** 145 * @return {string} The browser version or empty string if version cannot be 146 * determined. Note that for Internet Explorer, this returns the version of 147 * the browser, not the version of the rendering engine. (IE 8 in 148 * compatibility mode will return 8.0 rather than 7.0. To determine the 149 * rendering engine version, look at document.documentMode instead. See 150 * http://msdn.microsoft.com/en-us/library/cc196988(v=vs.85).aspx for more 151 * details.) 152 */ 153 goog.labs.userAgent.browser.getVersion = function() { 154 var userAgentString = goog.labs.userAgent.util.getUserAgent(); 155 // Special case IE since IE's version is inside the parenthesis and 156 // without the '/'. 157 if (goog.labs.userAgent.browser.isIE()) { 158 return goog.labs.userAgent.browser.getIEVersion_(userAgentString); 159 } 160 161 if (goog.labs.userAgent.browser.isOpera()) { 162 return goog.labs.userAgent.browser.getOperaVersion_(userAgentString); 163 } 164 165 var versionTuples = 166 goog.labs.userAgent.util.extractVersionTuples(userAgentString); 167 return goog.labs.userAgent.browser.getVersionFromTuples_(versionTuples); 168 }; 169 170 171 /** 172 * @param {string|number} version The version to check. 173 * @return {boolean} Whether the browser version is higher or the same as the 174 * given version. 175 */ 176 goog.labs.userAgent.browser.isVersionOrHigher = function(version) { 177 return goog.string.compareVersions(goog.labs.userAgent.browser.getVersion(), 178 version) >= 0; 179 }; 180 181 182 /** 183 * Determines IE version. More information: 184 * http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#uaString 185 * http://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx 186 * http://blogs.msdn.com/b/ie/archive/2010/03/23/introducing-ie9-s-user-agent-string.aspx 187 * http://blogs.msdn.com/b/ie/archive/2009/01/09/the-internet-explorer-8-user-agent-string-updated-edition.aspx 188 * 189 * @param {string} userAgent the User-Agent. 190 * @return {string} 191 * @private 192 */ 193 goog.labs.userAgent.browser.getIEVersion_ = function(userAgent) { 194 // IE11 may identify itself as MSIE 9.0 or MSIE 10.0 due to an IE 11 upgrade 195 // bug. Example UA: 196 // Mozilla/5.0 (MSIE 9.0; Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) 197 // like Gecko. 198 // See http://www.whatismybrowser.com/developers/unknown-user-agent-fragments. 199 var rv = /rv: *([\d\.]*)/.exec(userAgent); 200 if (rv && rv[1]) { 201 return rv[1]; 202 } 203 204 var version = ''; 205 var msie = /MSIE +([\d\.]+)/.exec(userAgent); 206 if (msie && msie[1]) { 207 // IE in compatibility mode usually identifies itself as MSIE 7.0; in this 208 // case, use the Trident version to determine the version of IE. For more 209 // details, see the links above. 210 var tridentVersion = /Trident\/(\d.\d)/.exec(userAgent); 211 if (msie[1] == '7.0') { 212 if (tridentVersion && tridentVersion[1]) { 213 switch (tridentVersion[1]) { 214 case '4.0': 215 version = '8.0'; 216 break; 217 case '5.0': 218 version = '9.0'; 219 break; 220 case '6.0': 221 version = '10.0'; 222 break; 223 case '7.0': 224 version = '11.0'; 225 break; 226 } 227 } else { 228 version = '7.0'; 229 } 230 } else { 231 version = msie[1]; 232 } 233 } 234 return version; 235 }; 236 237 238 /** 239 * Determines Opera version. More information: 240 * http://my.opera.com/ODIN/blog/2013/07/15/opera-user-agent-strings-opera-15-and-beyond 241 * 242 * @param {string} userAgent The User-Agent. 243 * @return {string} 244 * @private 245 */ 246 goog.labs.userAgent.browser.getOperaVersion_ = function(userAgent) { 247 var versionTuples = 248 goog.labs.userAgent.util.extractVersionTuples(userAgent); 249 var lastTuple = goog.array.peek(versionTuples); 250 if (lastTuple[0] == 'OPR' && lastTuple[1]) { 251 return lastTuple[1]; 252 } 253 254 return goog.labs.userAgent.browser.getVersionFromTuples_(versionTuples); 255 }; 256 257 258 /** 259 * Nearly all User-Agents start with Mozilla/N.0. This looks at the second tuple 260 * for the actual browser version number. 261 * @param {!Array.<!Array.<string>>} versionTuples 262 * @return {string} The version or empty string if it cannot be determined. 263 * @private 264 */ 265 goog.labs.userAgent.browser.getVersionFromTuples_ = function(versionTuples) { 266 // versionTuples[2] (The first X/Y tuple after the parenthesis) contains the 267 // browser version number. 268 goog.asserts.assert(versionTuples.length > 2, 269 'Couldn\'t extract version tuple from user agent string'); 270 return versionTuples[2] && versionTuples[2][1] ? versionTuples[2][1] : ''; 271 }; lib/goog/labs/useragent/engine.js
1 // Copyright 2013 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Closure user agent detection. 17 * @see http://en.wikipedia.org/wiki/User_agent 18 * For more information on browser brand, platform, or device see the other 19 * sub-namespaces in goog.labs.userAgent (browser, platform, and device). 20 * 21 */ 22 23 goog.provide('goog.labs.userAgent.engine'); 24 25 goog.require('goog.array'); 26 goog.require('goog.labs.userAgent.util'); 27 goog.require('goog.string'); 28 29 30 /** 31 * @return {boolean} Whether the rendering engine is Presto. 32 */ 33 goog.labs.userAgent.engine.isPresto = function() { 34 return goog.labs.userAgent.util.matchUserAgent('Presto'); 35 }; 36 37 38 /** 39 * @return {boolean} Whether the rendering engine is Trident. 40 */ 41 goog.labs.userAgent.engine.isTrident = function() { 42 // IE only started including the Trident token in IE8. 43 return goog.labs.userAgent.util.matchUserAgent('Trident') || 44 goog.labs.userAgent.util.matchUserAgent('MSIE'); 45 }; 46 47 48 /** 49 * @return {boolean} Whether the rendering engine is WebKit. 50 */ 51 goog.labs.userAgent.engine.isWebKit = function() { 52 return goog.labs.userAgent.util.matchUserAgentIgnoreCase('WebKit'); 53 }; 54 55 56 /** 57 * @return {boolean} Whether the rendering engine is Gecko. 58 */ 59 goog.labs.userAgent.engine.isGecko = function() { 60 return goog.labs.userAgent.util.matchUserAgent('Gecko') && 61 !goog.labs.userAgent.engine.isWebKit() && 62 !goog.labs.userAgent.engine.isTrident(); 63 }; 64 65 66 /** 67 * @return {string} The rendering engine's version or empty string if version 68 * can't be determined. 69 */ 70 goog.labs.userAgent.engine.getVersion = function() { 71 var userAgentString = goog.labs.userAgent.util.getUserAgent(); 72 if (userAgentString) { 73 var tuples = goog.labs.userAgent.util.extractVersionTuples( 74 userAgentString); 75 76 var engineTuple = tuples[1]; 77 if (engineTuple) { 78 // In Gecko, the version string is either in the browser info or the 79 // Firefox version. See Gecko user agent string reference: 80 // http://goo.gl/mULqa 81 if (engineTuple[0] == 'Gecko') { 82 return goog.labs.userAgent.engine.getVersionForKey_( 83 tuples, 'Firefox'); 84 } 85 86 return engineTuple[1]; 87 } 88 89 // IE has only one version identifier, and the Trident version is 90 // specified in the parenthetical. 91 var browserTuple = tuples[0]; 92 var info; 93 if (browserTuple && (info = browserTuple[2])) { 94 var match = /Trident\/([^\s;]+)/.exec(info); 95 if (match) { 96 return match[1]; 97 } 98 } 99 } 100 return ''; 101 }; 102 103 104 /** 105 * @param {string|number} version The version to check. 106 * @return {boolean} Whether the rendering engine version is higher or the same 107 * as the given version. 108 */ 109 goog.labs.userAgent.engine.isVersionOrHigher = function(version) { 110 return goog.string.compareVersions(goog.labs.userAgent.engine.getVersion(), 111 version) >= 0; 112 }; 113 114 115 /** 116 * @param {!Array.<!Array.<string>>} tuples Version tuples. 117 * @param {string} key The key to look for. 118 * @return {string} The version string of the given key, if present. 119 * Otherwise, the empty string. 120 * @private 121 */ 122 goog.labs.userAgent.engine.getVersionForKey_ = function(tuples, key) { 123 // TODO(nnaze): Move to util if useful elsewhere. 124 125 var pair = goog.array.find(tuples, function(pair) { 126 return key == pair[0]; 127 }); 128 129 return pair && pair[1] || ''; 130 }; lib/goog/labs/useragent/util.js
1 // Copyright 2013 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities used by goog.labs.userAgent tools. These functions 17 * should not be used outside of goog.labs.userAgent.*. 18 * 19 * @visibility {//closure/goog/bin/sizetests:__pkg__} 20 * @visibility {//closure/goog/dom:__subpackages__} 21 * @visibility {//closure/goog/style:__pkg__} 22 * @visibility {//closure/goog/testing:__pkg__} 23 * @visibility {//closure/goog/useragent:__subpackages__} 24 * @visibility {//testing/puppet/modules:__pkg__} * 25 * 26 * @author nnaze@google.com (Nathan Naze) 27 */ 28 29 goog.provide('goog.labs.userAgent.util'); 30 31 goog.require('goog.string'); 32 33 34 /** 35 * Gets the native userAgent string from navigator if it exists. 36 * If navigator or navigator.userAgent string is missing, returns an empty 37 * string. 38 * @return {string} 39 * @private 40 */ 41 goog.labs.userAgent.util.getNativeUserAgentString_ = function() { 42 var navigator = goog.labs.userAgent.util.getNavigator_(); 43 if (navigator) { 44 var userAgent = navigator.userAgent; 45 if (userAgent) { 46 return userAgent; 47 } 48 } 49 return ''; 50 }; 51 52 53 /** 54 * Getter for the native navigator. 55 * This is a separate function so it can be stubbed out in testing. 56 * @return {Navigator} 57 * @private 58 */ 59 goog.labs.userAgent.util.getNavigator_ = function() { 60 return goog.global.navigator; 61 }; 62 63 64 /** 65 * A possible override for applications which wish to not check 66 * navigator.userAgent but use a specified value for detection instead. 67 * @private {string} 68 */ 69 goog.labs.userAgent.util.userAgent_ = 70 goog.labs.userAgent.util.getNativeUserAgentString_(); 71 72 73 /** 74 * Applications may override browser detection on the built in 75 * navigator.userAgent object by setting this string. Set to null to use the 76 * browser object instead. 77 * @param {?string=} opt_userAgent The User-Agent override. 78 */ 79 goog.labs.userAgent.util.setUserAgent = function(opt_userAgent) { 80 goog.labs.userAgent.util.userAgent_ = opt_userAgent || 81 goog.labs.userAgent.util.getNativeUserAgentString_(); 82 }; 83 84 85 /** 86 * @return {string} The user agent string. 87 */ 88 goog.labs.userAgent.util.getUserAgent = function() { 89 return goog.labs.userAgent.util.userAgent_; 90 }; 91 92 93 /** 94 * @param {string} str 95 * @return {boolean} Whether the user agent contains the given string, ignoring 96 * case. 97 */ 98 goog.labs.userAgent.util.matchUserAgent = function(str) { 99 var userAgent = goog.labs.userAgent.util.getUserAgent(); 100 return goog.string.contains(userAgent, str); 101 }; 102 103 104 /** 105 * @param {string} str 106 * @return {boolean} Whether the user agent contains the given string. 107 */ 108 goog.labs.userAgent.util.matchUserAgentIgnoreCase = function(str) { 109 var userAgent = goog.labs.userAgent.util.getUserAgent(); 110 return goog.string.caseInsensitiveContains(userAgent, str); 111 }; 112 113 114 /** 115 * Parses the user agent into tuples for each section. 116 * @param {string} userAgent 117 * @return {!Array.<!Array.<string>>} Tuples of key, version, and the contents 118 * of the parenthetical. 119 */ 120 goog.labs.userAgent.util.extractVersionTuples = function(userAgent) { 121 // Matches each section of a user agent string. 122 // Example UA: 123 // Mozilla/5.0 (iPad; U; CPU OS 3_2_1 like Mac OS X; en-us) 124 // AppleWebKit/531.21.10 (KHTML, like Gecko) Mobile/7B405 125 // This has three version tuples: Mozilla, AppleWebKit, and Mobile. 126 127 var versionRegExp = new RegExp( 128 // Key. Note that a key may have a space. 129 // (i.e. 'Mobile Safari' in 'Mobile Safari/5.0') 130 '(\\w[\\w ]+)' + 131 132 '/' + // slash 133 '([^\\s]+)' + // version (i.e. '5.0b') 134 '\\s*' + // whitespace 135 '(?:\\((.*?)\\))?', // parenthetical info. parentheses not matched. 136 'g'); 137 138 var data = []; 139 var match; 140 141 // Iterate and collect the version tuples. Each iteration will be the 142 // next regex match. 143 while (match = versionRegExp.exec(userAgent)) { 144 data.push([ 145 match[1], // key 146 match[2], // value 147 // || undefined as this is not undefined in IE7 and IE8 148 match[3] || undefined // info 149 ]); 150 } 151 152 return data; 153 }; 154 lib/goog/math/math.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Additional mathematical functions. 17 */ 18 19 goog.provide('goog.math'); 20 21 goog.require('goog.array'); 22 goog.require('goog.asserts'); 23 24 25 /** 26 * Returns a random integer greater than or equal to 0 and less than {@code a}. 27 * @param {number} a The upper bound for the random integer (exclusive). 28 * @return {number} A random integer N such that 0 <= N < a. 29 */ 30 goog.math.randomInt = function(a) { 31 return Math.floor(Math.random() * a); 32 }; 33 34 35 /** 36 * Returns a random number greater than or equal to {@code a} and less than 37 * {@code b}. 38 * @param {number} a The lower bound for the random number (inclusive). 39 * @param {number} b The upper bound for the random number (exclusive). 40 * @return {number} A random number N such that a <= N < b. 41 */ 42 goog.math.uniformRandom = function(a, b) { 43 return a + Math.random() * (b - a); 44 }; 45 46 47 /** 48 * Takes a number and clamps it to within the provided bounds. 49 * @param {number} value The input number. 50 * @param {number} min The minimum value to return. 51 * @param {number} max The maximum value to return. 52 * @return {number} The input number if it is within bounds, or the nearest 53 * number within the bounds. 54 */ 55 goog.math.clamp = function(value, min, max) { 56 return Math.min(Math.max(value, min), max); 57 }; 58 59 60 /** 61 * The % operator in JavaScript returns the remainder of a / b, but differs from 62 * some other languages in that the result will have the same sign as the 63 * dividend. For example, -1 % 8 == -1, whereas in some other languages 64 * (such as Python) the result would be 7. This function emulates the more 65 * correct modulo behavior, which is useful for certain applications such as 66 * calculating an offset index in a circular list. 67 * 68 * @param {number} a The dividend. 69 * @param {number} b The divisor. 70 * @return {number} a % b where the result is between 0 and b (either 0 <= x < b 71 * or b < x <= 0, depending on the sign of b). 72 */ 73 goog.math.modulo = function(a, b) { 74 var r = a % b; 75 // If r and b differ in sign, add b to wrap the result to the correct sign. 76 return (r * b < 0) ? r + b : r; 77 }; 78 79 80 /** 81 * Performs linear interpolation between values a and b. Returns the value 82 * between a and b proportional to x (when x is between 0 and 1. When x is 83 * outside this range, the return value is a linear extrapolation). 84 * @param {number} a A number. 85 * @param {number} b A number. 86 * @param {number} x The proportion between a and b. 87 * @return {number} The interpolated value between a and b. 88 */ 89 goog.math.lerp = function(a, b, x) { 90 return a + x * (b - a); 91 }; 92 93 94 /** 95 * Tests whether the two values are equal to each other, within a certain 96 * tolerance to adjust for floating point errors. 97 * @param {number} a A number. 98 * @param {number} b A number. 99 * @param {number=} opt_tolerance Optional tolerance range. Defaults 100 * to 0.000001. If specified, should be greater than 0. 101 * @return {boolean} Whether {@code a} and {@code b} are nearly equal. 102 */ 103 goog.math.nearlyEquals = function(a, b, opt_tolerance) { 104 return Math.abs(a - b) <= (opt_tolerance || 0.000001); 105 }; 106 107 108 // TODO(user): Rename to normalizeAngle, retaining old name as deprecated 109 // alias. 110 /** 111 * Normalizes an angle to be in range [0-360). Angles outside this range will 112 * be normalized to be the equivalent angle with that range. 113 * @param {number} angle Angle in degrees. 114 * @return {number} Standardized angle. 115 */ 116 goog.math.standardAngle = function(angle) { 117 return goog.math.modulo(angle, 360); 118 }; 119 120 121 /** 122 * Normalizes an angle to be in range [0-2*PI). Angles outside this range will 123 * be normalized to be the equivalent angle with that range. 124 * @param {number} angle Angle in radians. 125 * @return {number} Standardized angle. 126 */ 127 goog.math.standardAngleInRadians = function(angle) { 128 return goog.math.modulo(angle, 2 * Math.PI); 129 }; 130 131 132 /** 133 * Converts degrees to radians. 134 * @param {number} angleDegrees Angle in degrees. 135 * @return {number} Angle in radians. 136 */ 137 goog.math.toRadians = function(angleDegrees) { 138 return angleDegrees * Math.PI / 180; 139 }; 140 141 142 /** 143 * Converts radians to degrees. 144 * @param {number} angleRadians Angle in radians. 145 * @return {number} Angle in degrees. 146 */ 147 goog.math.toDegrees = function(angleRadians) { 148 return angleRadians * 180 / Math.PI; 149 }; 150 151 152 /** 153 * For a given angle and radius, finds the X portion of the offset. 154 * @param {number} degrees Angle in degrees (zero points in +X direction). 155 * @param {number} radius Radius. 156 * @return {number} The x-distance for the angle and radius. 157 */ 158 goog.math.angleDx = function(degrees, radius) { 159 return radius * Math.cos(goog.math.toRadians(degrees)); 160 }; 161 162 163 /** 164 * For a given angle and radius, finds the Y portion of the offset. 165 * @param {number} degrees Angle in degrees (zero points in +X direction). 166 * @param {number} radius Radius. 167 * @return {number} The y-distance for the angle and radius. 168 */ 169 goog.math.angleDy = function(degrees, radius) { 170 return radius * Math.sin(goog.math.toRadians(degrees)); 171 }; 172 173 174 /** 175 * Computes the angle between two points (x1,y1) and (x2,y2). 176 * Angle zero points in the +X direction, 90 degrees points in the +Y 177 * direction (down) and from there we grow clockwise towards 360 degrees. 178 * @param {number} x1 x of first point. 179 * @param {number} y1 y of first point. 180 * @param {number} x2 x of second point. 181 * @param {number} y2 y of second point. 182 * @return {number} Standardized angle in degrees of the vector from 183 * x1,y1 to x2,y2. 184 */ 185 goog.math.angle = function(x1, y1, x2, y2) { 186 return goog.math.standardAngle(goog.math.toDegrees(Math.atan2(y2 - y1, 187 x2 - x1))); 188 }; 189 190 191 /** 192 * Computes the difference between startAngle and endAngle (angles in degrees). 193 * @param {number} startAngle Start angle in degrees. 194 * @param {number} endAngle End angle in degrees. 195 * @return {number} The number of degrees that when added to 196 * startAngle will result in endAngle. Positive numbers mean that the 197 * direction is clockwise. Negative numbers indicate a counter-clockwise 198 * direction. 199 * The shortest route (clockwise vs counter-clockwise) between the angles 200 * is used. 201 * When the difference is 180 degrees, the function returns 180 (not -180) 202 * angleDifference(30, 40) is 10, and angleDifference(40, 30) is -10. 203 * angleDifference(350, 10) is 20, and angleDifference(10, 350) is -20. 204 */ 205 goog.math.angleDifference = function(startAngle, endAngle) { 206 var d = goog.math.standardAngle(endAngle) - 207 goog.math.standardAngle(startAngle); 208 if (d > 180) { 209 d = d - 360; 210 } else if (d <= -180) { 211 d = 360 + d; 212 } 213 return d; 214 }; 215 216 217 /** 218 * Returns the sign of a number as per the "sign" or "signum" function. 219 * @param {number} x The number to take the sign of. 220 * @return {number} -1 when negative, 1 when positive, 0 when 0. 221 */ 222 goog.math.sign = function(x) { 223 return x == 0 ? 0 : (x < 0 ? -1 : 1); 224 }; 225 226 227 /** 228 * JavaScript implementation of Longest Common Subsequence problem. 229 * http://en.wikipedia.org/wiki/Longest_common_subsequence 230 * 231 * Returns the longest possible array that is subarray of both of given arrays. 232 * 233 * @param {Array.<Object>} array1 First array of objects. 234 * @param {Array.<Object>} array2 Second array of objects. 235 * @param {Function=} opt_compareFn Function that acts as a custom comparator 236 * for the array ojects. Function should return true if objects are equal, 237 * otherwise false. 238 * @param {Function=} opt_collectorFn Function used to decide what to return 239 * as a result subsequence. It accepts 2 arguments: index of common element 240 * in the first array and index in the second. The default function returns 241 * element from the first array. 242 * @return {!Array.<Object>} A list of objects that are common to both arrays 243 * such that there is no common subsequence with size greater than the 244 * length of the list. 245 */ 246 goog.math.longestCommonSubsequence = function( 247 array1, array2, opt_compareFn, opt_collectorFn) { 248 249 var compare = opt_compareFn || function(a, b) { 250 return a == b; 251 }; 252 253 var collect = opt_collectorFn || function(i1, i2) { 254 return array1[i1]; 255 }; 256 257 var length1 = array1.length; 258 var length2 = array2.length; 259 260 var arr = []; 261 for (var i = 0; i < length1 + 1; i++) { 262 arr[i] = []; 263 arr[i][0] = 0; 264 } 265 266 for (var j = 0; j < length2 + 1; j++) { 267 arr[0][j] = 0; 268 } 269 270 for (i = 1; i <= length1; i++) { 271 for (j = 1; j <= length2; j++) { 272 if (compare(array1[i - 1], array2[j - 1])) { 273 arr[i][j] = arr[i - 1][j - 1] + 1; 274 } else { 275 arr[i][j] = Math.max(arr[i - 1][j], arr[i][j - 1]); 276 } 277 } 278 } 279 280 // Backtracking 281 var result = []; 282 var i = length1, j = length2; 283 while (i > 0 && j > 0) { 284 if (compare(array1[i - 1], array2[j - 1])) { 285 result.unshift(collect(i - 1, j - 1)); 286 i--; 287 j--; 288 } else { 289 if (arr[i - 1][j] > arr[i][j - 1]) { 290 i--; 291 } else { 292 j--; 293 } 294 } 295 } 296 297 return result; 298 }; 299 300 301 /** 302 * Returns the sum of the arguments. 303 * @param {...number} var_args Numbers to add. 304 * @return {number} The sum of the arguments (0 if no arguments were provided, 305 * {@code NaN} if any of the arguments is not a valid number). 306 */ 307 goog.math.sum = function(var_args) { 308 return /** @type {number} */ (goog.array.reduce(arguments, 309 function(sum, value) { 310 return sum + value; 311 }, 0)); 312 }; 313 314 315 /** 316 * Returns the arithmetic mean of the arguments. 317 * @param {...number} var_args Numbers to average. 318 * @return {number} The average of the arguments ({@code NaN} if no arguments 319 * were provided or any of the arguments is not a valid number). 320 */ 321 goog.math.average = function(var_args) { 322 return goog.math.sum.apply(null, arguments) / arguments.length; 323 }; 324 325 326 /** 327 * Returns the unbiased sample variance of the arguments. For a definition, 328 * see e.g. http://en.wikipedia.org/wiki/Variance 329 * @param {...number} var_args Number samples to analyze. 330 * @return {number} The unbiased sample variance of the arguments (0 if fewer 331 * than two samples were provided, or {@code NaN} if any of the samples is 332 * not a valid number). 333 */ 334 goog.math.sampleVariance = function(var_args) { 335 var sampleSize = arguments.length; 336 if (sampleSize < 2) { 337 return 0; 338 } 339 340 var mean = goog.math.average.apply(null, arguments); 341 var variance = goog.math.sum.apply(null, goog.array.map(arguments, 342 function(val) { 343 return Math.pow(val - mean, 2); 344 })) / (sampleSize - 1); 345 346 return variance; 347 }; 348 349 350 /** 351 * Returns the sample standard deviation of the arguments. For a definition of 352 * sample standard deviation, see e.g. 353 * http://en.wikipedia.org/wiki/Standard_deviation 354 * @param {...number} var_args Number samples to analyze. 355 * @return {number} The sample standard deviation of the arguments (0 if fewer 356 * than two samples were provided, or {@code NaN} if any of the samples is 357 * not a valid number). 358 */ 359 goog.math.standardDeviation = function(var_args) { 360 return Math.sqrt(goog.math.sampleVariance.apply(null, arguments)); 361 }; 362 363 364 /** 365 * Returns whether the supplied number represents an integer, i.e. that is has 366 * no fractional component. No range-checking is performed on the number. 367 * @param {number} num The number to test. 368 * @return {boolean} Whether {@code num} is an integer. 369 */ 370 goog.math.isInt = function(num) { 371 return isFinite(num) && num % 1 == 0; 372 }; 373 374 375 /** 376 * Returns whether the supplied number is finite and not NaN. 377 * @param {number} num The number to test. 378 * @return {boolean} Whether {@code num} is a finite number. 379 */ 380 goog.math.isFiniteNumber = function(num) { 381 return isFinite(num) && !isNaN(num); 382 }; 383 384 385 /** 386 * Returns the precise value of floor(log10(num)). 387 * Simpler implementations didn't work because of floating point rounding 388 * errors. For example 389 * <ul> 390 * <li>Math.floor(Math.log(num) / Math.LN10) is off by one for num == 1e+3. 391 * <li>Math.floor(Math.log(num) * Math.LOG10E) is off by one for num == 1e+15. 392 * <li>Math.floor(Math.log10(num)) is off by one for num == 1e+15 - 1. 393 * </ul> 394 * @param {number} num A floating point number. 395 * @return {number} Its logarithm to base 10 rounded down to the nearest 396 * integer if num > 0. -Infinity if num == 0. NaN if num < 0. 397 */ 398 goog.math.log10Floor = function(num) { 399 if (num > 0) { 400 var x = Math.round(Math.log(num) * Math.LOG10E); 401 return x - (parseFloat('1e' + x) > num); 402 } 403 return num == 0 ? -Infinity : NaN; 404 }; 405 406 407 /** 408 * A tweaked variant of {@code Math.floor} which tolerates if the passed number 409 * is infinitesimally smaller than the closest integer. It often happens with 410 * the results of floating point calculations because of the finite precision 411 * of the intermediate results. For example {@code Math.floor(Math.log(1000) / 412 * Math.LN10) == 2}, not 3 as one would expect. 413 * @param {number} num A number. 414 * @param {number=} opt_epsilon An infinitesimally small positive number, the 415 * rounding error to tolerate. 416 * @return {number} The largest integer less than or equal to {@code num}. 417 */ 418 goog.math.safeFloor = function(num, opt_epsilon) { 419 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0); 420 return Math.floor(num + (opt_epsilon || 2e-15)); 421 }; 422 423 424 /** 425 * A tweaked variant of {@code Math.ceil}. See {@code goog.math.safeFloor} for 426 * details. 427 * @param {number} num A number. 428 * @param {number=} opt_epsilon An infinitesimally small positive number, the 429 * rounding error to tolerate. 430 * @return {number} The smallest integer greater than or equal to {@code num}. 431 */ 432 goog.math.safeCeil = function(num, opt_epsilon) { 433 goog.asserts.assert(!goog.isDef(opt_epsilon) || opt_epsilon > 0); 434 return Math.ceil(num - (opt_epsilon || 2e-15)); 435 }; lib/goog/net/wrapperxmlhttpfactory.js
1 // Copyright 2010 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Implementation of XmlHttpFactory which allows construction from 17 * simple factory methods. 18 * @author dbk@google.com (David Barrett-Kahn) 19 */ 20 21 goog.provide('goog.net.WrapperXmlHttpFactory'); 22 23 /** @suppress {extraRequire} Typedef. */ 24 goog.require('goog.net.XhrLike'); 25 goog.require('goog.net.XmlHttpFactory'); 26 27 28 29 /** 30 * An xhr factory subclass which can be constructed using two factory methods. 31 * This exists partly to allow the preservation of goog.net.XmlHttp.setFactory() 32 * with an unchanged signature. 33 * @param {function():!goog.net.XhrLike.OrNative} xhrFactory 34 * A function which returns a new XHR object. 35 * @param {function():!Object} optionsFactory A function which returns the 36 * options associated with xhr objects from this factory. 37 * @extends {goog.net.XmlHttpFactory} 38 * @constructor 39 * @final 40 */ 41 goog.net.WrapperXmlHttpFactory = function(xhrFactory, optionsFactory) { 42 goog.net.XmlHttpFactory.call(this); 43 44 /** 45 * XHR factory method. 46 * @type {function() : !goog.net.XhrLike.OrNative} 47 * @private 48 */ 49 this.xhrFactory_ = xhrFactory; 50 51 /** 52 * Options factory method. 53 * @type {function() : !Object} 54 * @private 55 */ 56 this.optionsFactory_ = optionsFactory; 57 }; 58 goog.inherits(goog.net.WrapperXmlHttpFactory, goog.net.XmlHttpFactory); 59 60 61 /** @override */ 62 goog.net.WrapperXmlHttpFactory.prototype.createInstance = function() { 63 return this.xhrFactory_(); 64 }; 65 66 67 /** @override */ 68 goog.net.WrapperXmlHttpFactory.prototype.getOptions = function() { 69 return this.optionsFactory_(); 70 }; 71 lib/goog/net/xhrlike.js
1 // Copyright 2013 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('goog.net.XhrLike'); 16 17 18 19 /** 20 * Interface for the common parts of XMLHttpRequest. 21 * 22 * Mostly copied from externs/w3c_xml.js. 23 * 24 * @interface 25 * @see http://www.w3.org/TR/XMLHttpRequest/ 26 */ 27 goog.net.XhrLike = function() {}; 28 29 30 /** 31 * Typedef that refers to either native or custom-implemented XHR objects. 32 * @typedef {!goog.net.XhrLike|!XMLHttpRequest} 33 */ 34 goog.net.XhrLike.OrNative; 35 36 37 /** 38 * @type {function()|null|undefined} 39 * @see http://www.w3.org/TR/XMLHttpRequest/#handler-xhr-onreadystatechange 40 */ 41 goog.net.XhrLike.prototype.onreadystatechange; 42 43 44 /** 45 * @type {string} 46 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsetext-attribute 47 */ 48 goog.net.XhrLike.prototype.responseText; 49 50 51 /** 52 * @type {Document} 53 * @see http://www.w3.org/TR/XMLHttpRequest/#the-responsexml-attribute 54 */ 55 goog.net.XhrLike.prototype.responseXML; 56 57 58 /** 59 * @type {number} 60 * @see http://www.w3.org/TR/XMLHttpRequest/#readystate 61 */ 62 goog.net.XhrLike.prototype.readyState; 63 64 65 /** 66 * @type {number} 67 * @see http://www.w3.org/TR/XMLHttpRequest/#status 68 */ 69 goog.net.XhrLike.prototype.status; 70 71 72 /** 73 * @type {string} 74 * @see http://www.w3.org/TR/XMLHttpRequest/#statustext 75 */ 76 goog.net.XhrLike.prototype.statusText; 77 78 79 /** 80 * @param {string} method 81 * @param {string} url 82 * @param {?boolean=} opt_async 83 * @param {?string=} opt_user 84 * @param {?string=} opt_password 85 * @see http://www.w3.org/TR/XMLHttpRequest/#the-open()-method 86 */ 87 goog.net.XhrLike.prototype.open = function(method, url, opt_async, opt_user, 88 opt_password) {}; 89 90 91 /** 92 * @param {ArrayBuffer|ArrayBufferView|Blob|Document|FormData|string=} opt_data 93 * @see http://www.w3.org/TR/XMLHttpRequest/#the-send()-method 94 */ 95 goog.net.XhrLike.prototype.send = function(opt_data) {}; 96 97 98 /** 99 * @see http://www.w3.org/TR/XMLHttpRequest/#the-abort()-method 100 */ 101 goog.net.XhrLike.prototype.abort = function() {}; 102 103 104 /** 105 * @param {string} header 106 * @param {string} value 107 * @see http://www.w3.org/TR/XMLHttpRequest/#the-setrequestheader()-method 108 */ 109 goog.net.XhrLike.prototype.setRequestHeader = function(header, value) {}; 110 111 112 /** 113 * @param {string} header 114 * @return {string} 115 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getresponseheader()-method 116 */ 117 goog.net.XhrLike.prototype.getResponseHeader = function(header) {}; 118 119 120 /** 121 * @return {string} 122 * @see http://www.w3.org/TR/XMLHttpRequest/#the-getallresponseheaders()-method 123 */ 124 goog.net.XhrLike.prototype.getAllResponseHeaders = function() {}; lib/goog/net/xmlhttp.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Low level handling of XMLHttpRequest. 17 * @author arv@google.com (Erik Arvidsson) 18 * @author dbk@google.com (David Barrett-Kahn) 19 */ 20 21 goog.provide('goog.net.DefaultXmlHttpFactory'); 22 goog.provide('goog.net.XmlHttp'); 23 goog.provide('goog.net.XmlHttp.OptionType'); 24 goog.provide('goog.net.XmlHttp.ReadyState'); 25 goog.provide('goog.net.XmlHttpDefines'); 26 27 goog.require('goog.asserts'); 28 goog.require('goog.net.WrapperXmlHttpFactory'); 29 goog.require('goog.net.XmlHttpFactory'); 30 31 32 /** 33 * Static class for creating XMLHttpRequest objects. 34 * @return {!goog.net.XhrLike.OrNative} A new XMLHttpRequest object. 35 */ 36 goog.net.XmlHttp = function() { 37 return goog.net.XmlHttp.factory_.createInstance(); 38 }; 39 40 41 /** 42 * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to 43 * true bypasses the ActiveX probing code. 44 * NOTE(user): Due to the way JSCompiler works, this define *will not* strip 45 * out the ActiveX probing code from binaries. To achieve this, use 46 * {@code goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR} instead. 47 * TODO(user): Collapse both defines. 48 */ 49 goog.define('goog.net.XmlHttp.ASSUME_NATIVE_XHR', false); 50 51 52 /** @const */ 53 goog.net.XmlHttpDefines = {}; 54 55 56 /** 57 * @define {boolean} Whether to assume XMLHttpRequest exists. Setting this to 58 * true eliminates the ActiveX probing code. 59 */ 60 goog.define('goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR', false); 61 62 63 /** 64 * Gets the options to use with the XMLHttpRequest objects obtained using 65 * the static methods. 66 * @return {Object} The options. 67 */ 68 goog.net.XmlHttp.getOptions = function() { 69 return goog.net.XmlHttp.factory_.getOptions(); 70 }; 71 72 73 /** 74 * Type of options that an XmlHttp object can have. 75 * @enum {number} 76 */ 77 goog.net.XmlHttp.OptionType = { 78 /** 79 * Whether a goog.nullFunction should be used to clear the onreadystatechange 80 * handler instead of null. 81 */ 82 USE_NULL_FUNCTION: 0, 83 84 /** 85 * NOTE(user): In IE if send() errors on a *local* request the readystate 86 * is still changed to COMPLETE. We need to ignore it and allow the 87 * try/catch around send() to pick up the error. 88 */ 89 LOCAL_REQUEST_ERROR: 1 90 }; 91 92 93 /** 94 * Status constants for XMLHTTP, matches: 95 * http://msdn.microsoft.com/library/default.asp?url=/library/ 96 * en-us/xmlsdk/html/0e6a34e4-f90c-489d-acff-cb44242fafc6.asp 97 * @enum {number} 98 */ 99 goog.net.XmlHttp.ReadyState = { 100 /** 101 * Constant for when xmlhttprequest.readyState is uninitialized 102 */ 103 UNINITIALIZED: 0, 104 105 /** 106 * Constant for when xmlhttprequest.readyState is loading. 107 */ 108 LOADING: 1, 109 110 /** 111 * Constant for when xmlhttprequest.readyState is loaded. 112 */ 113 LOADED: 2, 114 115 /** 116 * Constant for when xmlhttprequest.readyState is in an interactive state. 117 */ 118 INTERACTIVE: 3, 119 120 /** 121 * Constant for when xmlhttprequest.readyState is completed 122 */ 123 COMPLETE: 4 124 }; 125 126 127 /** 128 * The global factory instance for creating XMLHttpRequest objects. 129 * @type {goog.net.XmlHttpFactory} 130 * @private 131 */ 132 goog.net.XmlHttp.factory_; 133 134 135 /** 136 * Sets the factories for creating XMLHttpRequest objects and their options. 137 * @param {Function} factory The factory for XMLHttpRequest objects. 138 * @param {Function} optionsFactory The factory for options. 139 * @deprecated Use setGlobalFactory instead. 140 */ 141 goog.net.XmlHttp.setFactory = function(factory, optionsFactory) { 142 goog.net.XmlHttp.setGlobalFactory(new goog.net.WrapperXmlHttpFactory( 143 goog.asserts.assert(factory), 144 goog.asserts.assert(optionsFactory))); 145 }; 146 147 148 /** 149 * Sets the global factory object. 150 * @param {!goog.net.XmlHttpFactory} factory New global factory object. 151 */ 152 goog.net.XmlHttp.setGlobalFactory = function(factory) { 153 goog.net.XmlHttp.factory_ = factory; 154 }; 155 156 157 158 /** 159 * Default factory to use when creating xhr objects. You probably shouldn't be 160 * instantiating this directly, but rather using it via goog.net.XmlHttp. 161 * @extends {goog.net.XmlHttpFactory} 162 * @constructor 163 */ 164 goog.net.DefaultXmlHttpFactory = function() { 165 goog.net.XmlHttpFactory.call(this); 166 }; 167 goog.inherits(goog.net.DefaultXmlHttpFactory, goog.net.XmlHttpFactory); 168 169 170 /** @override */ 171 goog.net.DefaultXmlHttpFactory.prototype.createInstance = function() { 172 var progId = this.getProgId_(); 173 if (progId) { 174 return new ActiveXObject(progId); 175 } else { 176 return new XMLHttpRequest(); 177 } 178 }; 179 180 181 /** @override */ 182 goog.net.DefaultXmlHttpFactory.prototype.internalGetOptions = function() { 183 var progId = this.getProgId_(); 184 var options = {}; 185 if (progId) { 186 options[goog.net.XmlHttp.OptionType.USE_NULL_FUNCTION] = true; 187 options[goog.net.XmlHttp.OptionType.LOCAL_REQUEST_ERROR] = true; 188 } 189 return options; 190 }; 191 192 193 /** 194 * The ActiveX PROG ID string to use to create xhr's in IE. Lazily initialized. 195 * @type {string|undefined} 196 * @private 197 */ 198 goog.net.DefaultXmlHttpFactory.prototype.ieProgId_; 199 200 201 /** 202 * Initialize the private state used by other functions. 203 * @return {string} The ActiveX PROG ID string to use to create xhr's in IE. 204 * @private 205 */ 206 goog.net.DefaultXmlHttpFactory.prototype.getProgId_ = function() { 207 if (goog.net.XmlHttp.ASSUME_NATIVE_XHR || 208 goog.net.XmlHttpDefines.ASSUME_NATIVE_XHR) { 209 return ''; 210 } 211 212 // The following blog post describes what PROG IDs to use to create the 213 // XMLHTTP object in Internet Explorer: 214 // http://blogs.msdn.com/xmlteam/archive/2006/10/23/using-the-right-version-of-msxml-in-internet-explorer.aspx 215 // However we do not (yet) fully trust that this will be OK for old versions 216 // of IE on Win9x so we therefore keep the last 2. 217 if (!this.ieProgId_ && typeof XMLHttpRequest == 'undefined' && 218 typeof ActiveXObject != 'undefined') { 219 // Candidate Active X types. 220 var ACTIVE_X_IDENTS = ['MSXML2.XMLHTTP.6.0', 'MSXML2.XMLHTTP.3.0', 221 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP']; 222 for (var i = 0; i < ACTIVE_X_IDENTS.length; i++) { 223 var candidate = ACTIVE_X_IDENTS[i]; 224 /** @preserveTry */ 225 try { 226 new ActiveXObject(candidate); 227 // NOTE(user): cannot assign progid and return candidate in one line 228 // because JSCompiler complaings: BUG 658126 229 this.ieProgId_ = candidate; 230 return candidate; 231 } catch (e) { 232 // do nothing; try next choice 233 } 234 } 235 236 // couldn't find any matches 237 throw Error('Could not create ActiveXObject. ActiveX might be disabled,' + 238 ' or MSXML might not be installed'); 239 } 240 241 return /** @type {string} */ (this.ieProgId_); 242 }; 243 244 245 //Set the global factory to an instance of the default factory. 246 goog.net.XmlHttp.setGlobalFactory(new goog.net.DefaultXmlHttpFactory()); lib/goog/net/xmlhttpfactory.js
1 // Copyright 2010 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Interface for a factory for creating XMLHttpRequest objects 17 * and metadata about them. 18 * @author dbk@google.com (David Barrett-Kahn) 19 */ 20 21 goog.provide('goog.net.XmlHttpFactory'); 22 23 /** @suppress {extraRequire} Typedef. */ 24 goog.require('goog.net.XhrLike'); 25 26 27 28 /** 29 * Abstract base class for an XmlHttpRequest factory. 30 * @constructor 31 */ 32 goog.net.XmlHttpFactory = function() { 33 }; 34 35 36 /** 37 * Cache of options - we only actually call internalGetOptions once. 38 * @type {Object} 39 * @private 40 */ 41 goog.net.XmlHttpFactory.prototype.cachedOptions_ = null; 42 43 44 /** 45 * @return {!goog.net.XhrLike.OrNative} A new XhrLike instance. 46 */ 47 goog.net.XmlHttpFactory.prototype.createInstance = goog.abstractMethod; 48 49 50 /** 51 * @return {Object} Options describing how xhr objects obtained from this 52 * factory should be used. 53 */ 54 goog.net.XmlHttpFactory.prototype.getOptions = function() { 55 return this.cachedOptions_ || 56 (this.cachedOptions_ = this.internalGetOptions()); 57 }; 58 59 60 /** 61 * Override this method in subclasses to preserve the caching offered by 62 * getOptions(). 63 * @return {Object} Options describing how xhr objects obtained from this 64 * factory should be used. 65 * @protected 66 */ 67 goog.net.XmlHttpFactory.prototype.internalGetOptions = goog.abstractMethod; lib/goog/object/object.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities for manipulating objects/maps/hashes. 17 */ 18 19 goog.provide('goog.object'); 20 21 22 /** 23 * Calls a function for each element in an object/map/hash. 24 * 25 * @param {Object.<K,V>} obj The object over which to iterate. 26 * @param {function(this:T,V,?,Object.<K,V>):?} f The function to call 27 * for every element. This function takes 3 arguments (the element, the 28 * index and the object) and the return value is ignored. 29 * @param {T=} opt_obj This is used as the 'this' object within f. 30 * @template T,K,V 31 */ 32 goog.object.forEach = function(obj, f, opt_obj) { 33 for (var key in obj) { 34 f.call(opt_obj, obj[key], key, obj); 35 } 36 }; 37 38 39 /** 40 * Calls a function for each element in an object/map/hash. If that call returns 41 * true, adds the element to a new object. 42 * 43 * @param {Object.<K,V>} obj The object over which to iterate. 44 * @param {function(this:T,V,?,Object.<K,V>):boolean} f The function to call 45 * for every element. This 46 * function takes 3 arguments (the element, the index and the object) 47 * and should return a boolean. If the return value is true the 48 * element is added to the result object. If it is false the 49 * element is not included. 50 * @param {T=} opt_obj This is used as the 'this' object within f. 51 * @return {!Object.<K,V>} a new object in which only elements that passed the 52 * test are present. 53 * @template T,K,V 54 */ 55 goog.object.filter = function(obj, f, opt_obj) { 56 var res = {}; 57 for (var key in obj) { 58 if (f.call(opt_obj, obj[key], key, obj)) { 59 res[key] = obj[key]; 60 } 61 } 62 return res; 63 }; 64 65 66 /** 67 * For every element in an object/map/hash calls a function and inserts the 68 * result into a new object. 69 * 70 * @param {Object.<K,V>} obj The object over which to iterate. 71 * @param {function(this:T,V,?,Object.<K,V>):R} f The function to call 72 * for every element. This function 73 * takes 3 arguments (the element, the index and the object) 74 * and should return something. The result will be inserted 75 * into a new object. 76 * @param {T=} opt_obj This is used as the 'this' object within f. 77 * @return {!Object.<K,R>} a new object with the results from f. 78 * @template T,K,V,R 79 */ 80 goog.object.map = function(obj, f, opt_obj) { 81 var res = {}; 82 for (var key in obj) { 83 res[key] = f.call(opt_obj, obj[key], key, obj); 84 } 85 return res; 86 }; 87 88 89 /** 90 * Calls a function for each element in an object/map/hash. If any 91 * call returns true, returns true (without checking the rest). If 92 * all calls return false, returns false. 93 * 94 * @param {Object.<K,V>} obj The object to check. 95 * @param {function(this:T,V,?,Object.<K,V>):boolean} f The function to 96 * call for every element. This function 97 * takes 3 arguments (the element, the index and the object) and should 98 * return a boolean. 99 * @param {T=} opt_obj This is used as the 'this' object within f. 100 * @return {boolean} true if any element passes the test. 101 * @template T,K,V 102 */ 103 goog.object.some = function(obj, f, opt_obj) { 104 for (var key in obj) { 105 if (f.call(opt_obj, obj[key], key, obj)) { 106 return true; 107 } 108 } 109 return false; 110 }; 111 112 113 /** 114 * Calls a function for each element in an object/map/hash. If 115 * all calls return true, returns true. If any call returns false, returns 116 * false at this point and does not continue to check the remaining elements. 117 * 118 * @param {Object.<K,V>} obj The object to check. 119 * @param {?function(this:T,V,?,Object.<K,V>):boolean} f The function to 120 * call for every element. This function 121 * takes 3 arguments (the element, the index and the object) and should 122 * return a boolean. 123 * @param {T=} opt_obj This is used as the 'this' object within f. 124 * @return {boolean} false if any element fails the test. 125 * @template T,K,V 126 */ 127 goog.object.every = function(obj, f, opt_obj) { 128 for (var key in obj) { 129 if (!f.call(opt_obj, obj[key], key, obj)) { 130 return false; 131 } 132 } 133 return true; 134 }; 135 136 137 /** 138 * Returns the number of key-value pairs in the object map. 139 * 140 * @param {Object} obj The object for which to get the number of key-value 141 * pairs. 142 * @return {number} The number of key-value pairs in the object map. 143 */ 144 goog.object.getCount = function(obj) { 145 // JS1.5 has __count__ but it has been deprecated so it raises a warning... 146 // in other words do not use. Also __count__ only includes the fields on the 147 // actual object and not in the prototype chain. 148 var rv = 0; 149 for (var key in obj) { 150 rv++; 151 } 152 return rv; 153 }; 154 155 156 /** 157 * Returns one key from the object map, if any exists. 158 * For map literals the returned key will be the first one in most of the 159 * browsers (a know exception is Konqueror). 160 * 161 * @param {Object} obj The object to pick a key from. 162 * @return {string|undefined} The key or undefined if the object is empty. 163 */ 164 goog.object.getAnyKey = function(obj) { 165 for (var key in obj) { 166 return key; 167 } 168 }; 169 170 171 /** 172 * Returns one value from the object map, if any exists. 173 * For map literals the returned value will be the first one in most of the 174 * browsers (a know exception is Konqueror). 175 * 176 * @param {Object.<K,V>} obj The object to pick a value from. 177 * @return {V|undefined} The value or undefined if the object is empty. 178 * @template K,V 179 */ 180 goog.object.getAnyValue = function(obj) { 181 for (var key in obj) { 182 return obj[key]; 183 } 184 }; 185 186 187 /** 188 * Whether the object/hash/map contains the given object as a value. 189 * An alias for goog.object.containsValue(obj, val). 190 * 191 * @param {Object.<K,V>} obj The object in which to look for val. 192 * @param {V} val The object for which to check. 193 * @return {boolean} true if val is present. 194 * @template K,V 195 */ 196 goog.object.contains = function(obj, val) { 197 return goog.object.containsValue(obj, val); 198 }; 199 200 201 /** 202 * Returns the values of the object/map/hash. 203 * 204 * @param {Object.<K,V>} obj The object from which to get the values. 205 * @return {!Array.<V>} The values in the object/map/hash. 206 * @template K,V 207 */ 208 goog.object.getValues = function(obj) { 209 var res = []; 210 var i = 0; 211 for (var key in obj) { 212 res[i++] = obj[key]; 213 } 214 return res; 215 }; 216 217 218 /** 219 * Returns the keys of the object/map/hash. 220 * 221 * @param {Object} obj The object from which to get the keys. 222 * @return {!Array.<string>} Array of property keys. 223 */ 224 goog.object.getKeys = function(obj) { 225 var res = []; 226 var i = 0; 227 for (var key in obj) { 228 res[i++] = key; 229 } 230 return res; 231 }; 232 233 234 /** 235 * Get a value from an object multiple levels deep. This is useful for 236 * pulling values from deeply nested objects, such as JSON responses. 237 * Example usage: getValueByKeys(jsonObj, 'foo', 'entries', 3) 238 * 239 * @param {!Object} obj An object to get the value from. Can be array-like. 240 * @param {...(string|number|!Array.<number|string>)} var_args A number of keys 241 * (as strings, or numbers, for array-like objects). Can also be 242 * specified as a single array of keys. 243 * @return {*} The resulting value. If, at any point, the value for a key 244 * is undefined, returns undefined. 245 */ 246 goog.object.getValueByKeys = function(obj, var_args) { 247 var isArrayLike = goog.isArrayLike(var_args); 248 var keys = isArrayLike ? var_args : arguments; 249 250 // Start with the 2nd parameter for the variable parameters syntax. 251 for (var i = isArrayLike ? 0 : 1; i < keys.length; i++) { 252 obj = obj[keys[i]]; 253 if (!goog.isDef(obj)) { 254 break; 255 } 256 } 257 258 return obj; 259 }; 260 261 262 /** 263 * Whether the object/map/hash contains the given key. 264 * 265 * @param {Object} obj The object in which to look for key. 266 * @param {*} key The key for which to check. 267 * @return {boolean} true If the map contains the key. 268 */ 269 goog.object.containsKey = function(obj, key) { 270 return key in obj; 271 }; 272 273 274 /** 275 * Whether the object/map/hash contains the given value. This is O(n). 276 * 277 * @param {Object.<K,V>} obj The object in which to look for val. 278 * @param {V} val The value for which to check. 279 * @return {boolean} true If the map contains the value. 280 * @template K,V 281 */ 282 goog.object.containsValue = function(obj, val) { 283 for (var key in obj) { 284 if (obj[key] == val) { 285 return true; 286 } 287 } 288 return false; 289 }; 290 291 292 /** 293 * Searches an object for an element that satisfies the given condition and 294 * returns its key. 295 * @param {Object.<K,V>} obj The object to search in. 296 * @param {function(this:T,V,string,Object.<K,V>):boolean} f The 297 * function to call for every element. Takes 3 arguments (the value, 298 * the key and the object) and should return a boolean. 299 * @param {T=} opt_this An optional "this" context for the function. 300 * @return {string|undefined} The key of an element for which the function 301 * returns true or undefined if no such element is found. 302 * @template T,K,V 303 */ 304 goog.object.findKey = function(obj, f, opt_this) { 305 for (var key in obj) { 306 if (f.call(opt_this, obj[key], key, obj)) { 307 return key; 308 } 309 } 310 return undefined; 311 }; 312 313 314 /** 315 * Searches an object for an element that satisfies the given condition and 316 * returns its value. 317 * @param {Object.<K,V>} obj The object to search in. 318 * @param {function(this:T,V,string,Object.<K,V>):boolean} f The function 319 * to call for every element. Takes 3 arguments (the value, the key 320 * and the object) and should return a boolean. 321 * @param {T=} opt_this An optional "this" context for the function. 322 * @return {V} The value of an element for which the function returns true or 323 * undefined if no such element is found. 324 * @template T,K,V 325 */ 326 goog.object.findValue = function(obj, f, opt_this) { 327 var key = goog.object.findKey(obj, f, opt_this); 328 return key && obj[key]; 329 }; 330 331 332 /** 333 * Whether the object/map/hash is empty. 334 * 335 * @param {Object} obj The object to test. 336 * @return {boolean} true if obj is empty. 337 */ 338 goog.object.isEmpty = function(obj) { 339 for (var key in obj) { 340 return false; 341 } 342 return true; 343 }; 344 345 346 /** 347 * Removes all key value pairs from the object/map/hash. 348 * 349 * @param {Object} obj The object to clear. 350 */ 351 goog.object.clear = function(obj) { 352 for (var i in obj) { 353 delete obj[i]; 354 } 355 }; 356 357 358 /** 359 * Removes a key-value pair based on the key. 360 * 361 * @param {Object} obj The object from which to remove the key. 362 * @param {*} key The key to remove. 363 * @return {boolean} Whether an element was removed. 364 */ 365 goog.object.remove = function(obj, key) { 366 var rv; 367 if ((rv = key in obj)) { 368 delete obj[key]; 369 } 370 return rv; 371 }; 372 373 374 /** 375 * Adds a key-value pair to the object. Throws an exception if the key is 376 * already in use. Use set if you want to change an existing pair. 377 * 378 * @param {Object.<K,V>} obj The object to which to add the key-value pair. 379 * @param {string} key The key to add. 380 * @param {V} val The value to add. 381 * @template K,V 382 */ 383 goog.object.add = function(obj, key, val) { 384 if (key in obj) { 385 throw Error('The object already contains the key "' + key + '"'); 386 } 387 goog.object.set(obj, key, val); 388 }; 389 390 391 /** 392 * Returns the value for the given key. 393 * 394 * @param {Object.<K,V>} obj The object from which to get the value. 395 * @param {string} key The key for which to get the value. 396 * @param {R=} opt_val The value to return if no item is found for the given 397 * key (default is undefined). 398 * @return {V|R|undefined} The value for the given key. 399 * @template K,V,R 400 */ 401 goog.object.get = function(obj, key, opt_val) { 402 if (key in obj) { 403 return obj[key]; 404 } 405 return opt_val; 406 }; 407 408 409 /** 410 * Adds a key-value pair to the object/map/hash. 411 * 412 * @param {Object.<K,V>} obj The object to which to add the key-value pair. 413 * @param {string} key The key to add. 414 * @param {V} value The value to add. 415 * @template K,V 416 */ 417 goog.object.set = function(obj, key, value) { 418 obj[key] = value; 419 }; 420 421 422 /** 423 * Adds a key-value pair to the object/map/hash if it doesn't exist yet. 424 * 425 * @param {Object.<K,V>} obj The object to which to add the key-value pair. 426 * @param {string} key The key to add. 427 * @param {V} value The value to add if the key wasn't present. 428 * @return {V} The value of the entry at the end of the function. 429 * @template K,V 430 */ 431 goog.object.setIfUndefined = function(obj, key, value) { 432 return key in obj ? obj[key] : (obj[key] = value); 433 }; 434 435 436 /** 437 * Does a flat clone of the object. 438 * 439 * @param {Object.<K,V>} obj Object to clone. 440 * @return {!Object.<K,V>} Clone of the input object. 441 * @template K,V 442 */ 443 goog.object.clone = function(obj) { 444 // We cannot use the prototype trick because a lot of methods depend on where 445 // the actual key is set. 446 447 var res = {}; 448 for (var key in obj) { 449 res[key] = obj[key]; 450 } 451 return res; 452 // We could also use goog.mixin but I wanted this to be independent from that. 453 }; 454 455 456 /** 457 * Clones a value. The input may be an Object, Array, or basic type. Objects and 458 * arrays will be cloned recursively. 459 * 460 * WARNINGS: 461 * <code>goog.object.unsafeClone</code> does not detect reference loops. Objects 462 * that refer to themselves will cause infinite recursion. 463 * 464 * <code>goog.object.unsafeClone</code> is unaware of unique identifiers, and 465 * copies UIDs created by <code>getUid</code> into cloned results. 466 * 467 * @param {*} obj The value to clone. 468 * @return {*} A clone of the input value. 469 */ 470 goog.object.unsafeClone = function(obj) { 471 var type = goog.typeOf(obj); 472 if (type == 'object' || type == 'array') { 473 if (obj.clone) { 474 return obj.clone(); 475 } 476 var clone = type == 'array' ? [] : {}; 477 for (var key in obj) { 478 clone[key] = goog.object.unsafeClone(obj[key]); 479 } 480 return clone; 481 } 482 483 return obj; 484 }; 485 486 487 /** 488 * Returns a new object in which all the keys and values are interchanged 489 * (keys become values and values become keys). If multiple keys map to the 490 * same value, the chosen transposed value is implementation-dependent. 491 * 492 * @param {Object} obj The object to transpose. 493 * @return {!Object} The transposed object. 494 */ 495 goog.object.transpose = function(obj) { 496 var transposed = {}; 497 for (var key in obj) { 498 transposed[obj[key]] = key; 499 } 500 return transposed; 501 }; 502 503 504 /** 505 * The names of the fields that are defined on Object.prototype. 506 * @type {Array.<string>} 507 * @private 508 */ 509 goog.object.PROTOTYPE_FIELDS_ = [ 510 'constructor', 511 'hasOwnProperty', 512 'isPrototypeOf', 513 'propertyIsEnumerable', 514 'toLocaleString', 515 'toString', 516 'valueOf' 517 ]; 518 519 520 /** 521 * Extends an object with another object. 522 * This operates 'in-place'; it does not create a new Object. 523 * 524 * Example: 525 * var o = {}; 526 * goog.object.extend(o, {a: 0, b: 1}); 527 * o; // {a: 0, b: 1} 528 * goog.object.extend(o, {b: 2, c: 3}); 529 * o; // {a: 0, b: 2, c: 3} 530 * 531 * @param {Object} target The object to modify. Existing properties will be 532 * overwritten if they are also present in one of the objects in 533 * {@code var_args}. 534 * @param {...Object} var_args The objects from which values will be copied. 535 */ 536 goog.object.extend = function(target, var_args) { 537 var key, source; 538 for (var i = 1; i < arguments.length; i++) { 539 source = arguments[i]; 540 for (key in source) { 541 target[key] = source[key]; 542 } 543 544 // For IE the for-in-loop does not contain any properties that are not 545 // enumerable on the prototype object (for example isPrototypeOf from 546 // Object.prototype) and it will also not include 'replace' on objects that 547 // extend String and change 'replace' (not that it is common for anyone to 548 // extend anything except Object). 549 550 for (var j = 0; j < goog.object.PROTOTYPE_FIELDS_.length; j++) { 551 key = goog.object.PROTOTYPE_FIELDS_[j]; 552 if (Object.prototype.hasOwnProperty.call(source, key)) { 553 target[key] = source[key]; 554 } 555 } 556 } 557 }; 558 559 560 /** 561 * Creates a new object built from the key-value pairs provided as arguments. 562 * @param {...*} var_args If only one argument is provided and it is an array 563 * then this is used as the arguments, otherwise even arguments are used as 564 * the property names and odd arguments are used as the property values. 565 * @return {!Object} The new object. 566 * @throws {Error} If there are uneven number of arguments or there is only one 567 * non array argument. 568 */ 569 goog.object.create = function(var_args) { 570 var argLength = arguments.length; 571 if (argLength == 1 && goog.isArray(arguments[0])) { 572 return goog.object.create.apply(null, arguments[0]); 573 } 574 575 if (argLength % 2) { 576 throw Error('Uneven number of arguments'); 577 } 578 579 var rv = {}; 580 for (var i = 0; i < argLength; i += 2) { 581 rv[arguments[i]] = arguments[i + 1]; 582 } 583 return rv; 584 }; 585 586 587 /** 588 * Creates a new object where the property names come from the arguments but 589 * the value is always set to true 590 * @param {...*} var_args If only one argument is provided and it is an array 591 * then this is used as the arguments, otherwise the arguments are used 592 * as the property names. 593 * @return {!Object} The new object. 594 */ 595 goog.object.createSet = function(var_args) { 596 var argLength = arguments.length; 597 if (argLength == 1 && goog.isArray(arguments[0])) { 598 return goog.object.createSet.apply(null, arguments[0]); 599 } 600 601 var rv = {}; 602 for (var i = 0; i < argLength; i++) { 603 rv[arguments[i]] = true; 604 } 605 return rv; 606 }; 607 608 609 /** 610 * Creates an immutable view of the underlying object, if the browser 611 * supports immutable objects. 612 * 613 * In default mode, writes to this view will fail silently. In strict mode, 614 * they will throw an error. 615 * 616 * @param {!Object.<K,V>} obj An object. 617 * @return {!Object.<K,V>} An immutable view of that object, or the 618 * original object if this browser does not support immutables. 619 * @template K,V 620 */ 621 goog.object.createImmutableView = function(obj) { 622 var result = obj; 623 if (Object.isFrozen && !Object.isFrozen(obj)) { 624 result = Object.create(obj); 625 Object.freeze(result); 626 } 627 return result; 628 }; 629 630 631 /** 632 * @param {!Object} obj An object. 633 * @return {boolean} Whether this is an immutable view of the object. 634 */ 635 goog.object.isImmutableView = function(obj) { 636 return !!Object.isFrozen && Object.isFrozen(obj); 637 }; lib/goog/string/string.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Utilities for string manipulation. 17 */ 18 19 20 /** 21 * Namespace for string utilities 22 */ 23 goog.provide('goog.string'); 24 goog.provide('goog.string.Unicode'); 25 26 27 /** 28 * @define {boolean} Enables HTML escaping of lowercase letter "e" which helps 29 * with detection of double-escaping as this letter is frequently used. 30 */ 31 goog.define('goog.string.DETECT_DOUBLE_ESCAPING', false); 32 33 34 /** 35 * Common Unicode string characters. 36 * @enum {string} 37 */ 38 goog.string.Unicode = { 39 NBSP: '\xa0' 40 }; 41 42 43 /** 44 * Fast prefix-checker. 45 * @param {string} str The string to check. 46 * @param {string} prefix A string to look for at the start of {@code str}. 47 * @return {boolean} True if {@code str} begins with {@code prefix}. 48 */ 49 goog.string.startsWith = function(str, prefix) { 50 return str.lastIndexOf(prefix, 0) == 0; 51 }; 52 53 54 /** 55 * Fast suffix-checker. 56 * @param {string} str The string to check. 57 * @param {string} suffix A string to look for at the end of {@code str}. 58 * @return {boolean} True if {@code str} ends with {@code suffix}. 59 */ 60 goog.string.endsWith = function(str, suffix) { 61 var l = str.length - suffix.length; 62 return l >= 0 && str.indexOf(suffix, l) == l; 63 }; 64 65 66 /** 67 * Case-insensitive prefix-checker. 68 * @param {string} str The string to check. 69 * @param {string} prefix A string to look for at the end of {@code str}. 70 * @return {boolean} True if {@code str} begins with {@code prefix} (ignoring 71 * case). 72 */ 73 goog.string.caseInsensitiveStartsWith = function(str, prefix) { 74 return goog.string.caseInsensitiveCompare( 75 prefix, str.substr(0, prefix.length)) == 0; 76 }; 77 78 79 /** 80 * Case-insensitive suffix-checker. 81 * @param {string} str The string to check. 82 * @param {string} suffix A string to look for at the end of {@code str}. 83 * @return {boolean} True if {@code str} ends with {@code suffix} (ignoring 84 * case). 85 */ 86 goog.string.caseInsensitiveEndsWith = function(str, suffix) { 87 return goog.string.caseInsensitiveCompare( 88 suffix, str.substr(str.length - suffix.length, suffix.length)) == 0; 89 }; 90 91 92 /** 93 * Case-insensitive equality checker. 94 * @param {string} str1 First string to check. 95 * @param {string} str2 Second string to check. 96 * @return {boolean} True if {@code str1} and {@code str2} are the same string, 97 * ignoring case. 98 */ 99 goog.string.caseInsensitiveEquals = function(str1, str2) { 100 return str1.toLowerCase() == str2.toLowerCase(); 101 }; 102 103 104 /** 105 * Does simple python-style string substitution. 106 * subs("foo%s hot%s", "bar", "dog") becomes "foobar hotdog". 107 * @param {string} str The string containing the pattern. 108 * @param {...*} var_args The items to substitute into the pattern. 109 * @return {string} A copy of {@code str} in which each occurrence of 110 * {@code %s} has been replaced an argument from {@code var_args}. 111 */ 112 goog.string.subs = function(str, var_args) { 113 var splitParts = str.split('%s'); 114 var returnString = ''; 115 116 var subsArguments = Array.prototype.slice.call(arguments, 1); 117 while (subsArguments.length && 118 // Replace up to the last split part. We are inserting in the 119 // positions between split parts. 120 splitParts.length > 1) { 121 returnString += splitParts.shift() + subsArguments.shift(); 122 } 123 124 return returnString + splitParts.join('%s'); // Join unused '%s' 125 }; 126 127 128 /** 129 * Converts multiple whitespace chars (spaces, non-breaking-spaces, new lines 130 * and tabs) to a single space, and strips leading and trailing whitespace. 131 * @param {string} str Input string. 132 * @return {string} A copy of {@code str} with collapsed whitespace. 133 */ 134 goog.string.collapseWhitespace = function(str) { 135 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 136 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 137 // include it in the regexp to enforce consistent cross-browser behavior. 138 return str.replace(/[\s\xa0]+/g, ' ').replace(/^\s+|\s+$/g, ''); 139 }; 140 141 142 /** 143 * Checks if a string is empty or contains only whitespaces. 144 * @param {string} str The string to check. 145 * @return {boolean} True if {@code str} is empty or whitespace only. 146 */ 147 goog.string.isEmpty = function(str) { 148 // testing length == 0 first is actually slower in all browsers (about the 149 // same in Opera). 150 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 151 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 152 // include it in the regexp to enforce consistent cross-browser behavior. 153 return /^[\s\xa0]*$/.test(str); 154 }; 155 156 157 /** 158 * Checks if a string is null, undefined, empty or contains only whitespaces. 159 * @param {*} str The string to check. 160 * @return {boolean} True if{@code str} is null, undefined, empty, or 161 * whitespace only. 162 */ 163 goog.string.isEmptySafe = function(str) { 164 return goog.string.isEmpty(goog.string.makeSafe(str)); 165 }; 166 167 168 /** 169 * Checks if a string is all breaking whitespace. 170 * @param {string} str The string to check. 171 * @return {boolean} Whether the string is all breaking whitespace. 172 */ 173 goog.string.isBreakingWhitespace = function(str) { 174 return !/[^\t\n\r ]/.test(str); 175 }; 176 177 178 /** 179 * Checks if a string contains all letters. 180 * @param {string} str string to check. 181 * @return {boolean} True if {@code str} consists entirely of letters. 182 */ 183 goog.string.isAlpha = function(str) { 184 return !/[^a-zA-Z]/.test(str); 185 }; 186 187 188 /** 189 * Checks if a string contains only numbers. 190 * @param {*} str string to check. If not a string, it will be 191 * casted to one. 192 * @return {boolean} True if {@code str} is numeric. 193 */ 194 goog.string.isNumeric = function(str) { 195 return !/[^0-9]/.test(str); 196 }; 197 198 199 /** 200 * Checks if a string contains only numbers or letters. 201 * @param {string} str string to check. 202 * @return {boolean} True if {@code str} is alphanumeric. 203 */ 204 goog.string.isAlphaNumeric = function(str) { 205 return !/[^a-zA-Z0-9]/.test(str); 206 }; 207 208 209 /** 210 * Checks if a character is a space character. 211 * @param {string} ch Character to check. 212 * @return {boolean} True if {code ch} is a space. 213 */ 214 goog.string.isSpace = function(ch) { 215 return ch == ' '; 216 }; 217 218 219 /** 220 * Checks if a character is a valid unicode character. 221 * @param {string} ch Character to check. 222 * @return {boolean} True if {code ch} is a valid unicode character. 223 */ 224 goog.string.isUnicodeChar = function(ch) { 225 return ch.length == 1 && ch >= ' ' && ch <= '~' || 226 ch >= '\u0080' && ch <= '\uFFFD'; 227 }; 228 229 230 /** 231 * Takes a string and replaces newlines with a space. Multiple lines are 232 * replaced with a single space. 233 * @param {string} str The string from which to strip newlines. 234 * @return {string} A copy of {@code str} stripped of newlines. 235 */ 236 goog.string.stripNewlines = function(str) { 237 return str.replace(/(\r\n|\r|\n)+/g, ' '); 238 }; 239 240 241 /** 242 * Replaces Windows and Mac new lines with unix style: \r or \r\n with \n. 243 * @param {string} str The string to in which to canonicalize newlines. 244 * @return {string} {@code str} A copy of {@code} with canonicalized newlines. 245 */ 246 goog.string.canonicalizeNewlines = function(str) { 247 return str.replace(/(\r\n|\r|\n)/g, '\n'); 248 }; 249 250 251 /** 252 * Normalizes whitespace in a string, replacing all whitespace chars with 253 * a space. 254 * @param {string} str The string in which to normalize whitespace. 255 * @return {string} A copy of {@code str} with all whitespace normalized. 256 */ 257 goog.string.normalizeWhitespace = function(str) { 258 return str.replace(/\xa0|\s/g, ' '); 259 }; 260 261 262 /** 263 * Normalizes spaces in a string, replacing all consecutive spaces and tabs 264 * with a single space. Replaces non-breaking space with a space. 265 * @param {string} str The string in which to normalize spaces. 266 * @return {string} A copy of {@code str} with all consecutive spaces and tabs 267 * replaced with a single space. 268 */ 269 goog.string.normalizeSpaces = function(str) { 270 return str.replace(/\xa0|[ \t]+/g, ' '); 271 }; 272 273 274 /** 275 * Removes the breaking spaces from the left and right of the string and 276 * collapses the sequences of breaking spaces in the middle into single spaces. 277 * The original and the result strings render the same way in HTML. 278 * @param {string} str A string in which to collapse spaces. 279 * @return {string} Copy of the string with normalized breaking spaces. 280 */ 281 goog.string.collapseBreakingSpaces = function(str) { 282 return str.replace(/[\t\r\n ]+/g, ' ').replace( 283 /^[\t\r\n ]+|[\t\r\n ]+$/g, ''); 284 }; 285 286 287 /** 288 * Trims white spaces to the left and right of a string. 289 * @param {string} str The string to trim. 290 * @return {string} A trimmed copy of {@code str}. 291 */ 292 goog.string.trim = function(str) { 293 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 294 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 295 // include it in the regexp to enforce consistent cross-browser behavior. 296 return str.replace(/^[\s\xa0]+|[\s\xa0]+$/g, ''); 297 }; 298 299 300 /** 301 * Trims whitespaces at the left end of a string. 302 * @param {string} str The string to left trim. 303 * @return {string} A trimmed copy of {@code str}. 304 */ 305 goog.string.trimLeft = function(str) { 306 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 307 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 308 // include it in the regexp to enforce consistent cross-browser behavior. 309 return str.replace(/^[\s\xa0]+/, ''); 310 }; 311 312 313 /** 314 * Trims whitespaces at the right end of a string. 315 * @param {string} str The string to right trim. 316 * @return {string} A trimmed copy of {@code str}. 317 */ 318 goog.string.trimRight = function(str) { 319 // Since IE doesn't include non-breaking-space (0xa0) in their \s character 320 // class (as required by section 7.2 of the ECMAScript spec), we explicitly 321 // include it in the regexp to enforce consistent cross-browser behavior. 322 return str.replace(/[\s\xa0]+$/, ''); 323 }; 324 325 326 /** 327 * A string comparator that ignores case. 328 * -1 = str1 less than str2 329 * 0 = str1 equals str2 330 * 1 = str1 greater than str2 331 * 332 * @param {string} str1 The string to compare. 333 * @param {string} str2 The string to compare {@code str1} to. 334 * @return {number} The comparator result, as described above. 335 */ 336 goog.string.caseInsensitiveCompare = function(str1, str2) { 337 var test1 = String(str1).toLowerCase(); 338 var test2 = String(str2).toLowerCase(); 339 340 if (test1 < test2) { 341 return -1; 342 } else if (test1 == test2) { 343 return 0; 344 } else { 345 return 1; 346 } 347 }; 348 349 350 /** 351 * Regular expression used for splitting a string into substrings of fractional 352 * numbers, integers, and non-numeric characters. 353 * @type {RegExp} 354 * @private 355 */ 356 goog.string.numerateCompareRegExp_ = /(\.\d+)|(\d+)|(\D+)/g; 357 358 359 /** 360 * String comparison function that handles numbers in a way humans might expect. 361 * Using this function, the string "File 2.jpg" sorts before "File 10.jpg". The 362 * comparison is mostly case-insensitive, though strings that are identical 363 * except for case are sorted with the upper-case strings before lower-case. 364 * 365 * This comparison function is significantly slower (about 500x) than either 366 * the default or the case-insensitive compare. It should not be used in 367 * time-critical code, but should be fast enough to sort several hundred short 368 * strings (like filenames) with a reasonable delay. 369 * 370 * @param {string} str1 The string to compare in a numerically sensitive way. 371 * @param {string} str2 The string to compare {@code str1} to. 372 * @return {number} less than 0 if str1 < str2, 0 if str1 == str2, greater than 373 * 0 if str1 > str2. 374 */ 375 goog.string.numerateCompare = function(str1, str2) { 376 if (str1 == str2) { 377 return 0; 378 } 379 if (!str1) { 380 return -1; 381 } 382 if (!str2) { 383 return 1; 384 } 385 386 // Using match to split the entire string ahead of time turns out to be faster 387 // for most inputs than using RegExp.exec or iterating over each character. 388 var tokens1 = str1.toLowerCase().match(goog.string.numerateCompareRegExp_); 389 var tokens2 = str2.toLowerCase().match(goog.string.numerateCompareRegExp_); 390 391 var count = Math.min(tokens1.length, tokens2.length); 392 393 for (var i = 0; i < count; i++) { 394 var a = tokens1[i]; 395 var b = tokens2[i]; 396 397 // Compare pairs of tokens, returning if one token sorts before the other. 398 if (a != b) { 399 400 // Only if both tokens are integers is a special comparison required. 401 // Decimal numbers are sorted as strings (e.g., '.09' < '.1'). 402 var num1 = parseInt(a, 10); 403 if (!isNaN(num1)) { 404 var num2 = parseInt(b, 10); 405 if (!isNaN(num2) && num1 - num2) { 406 return num1 - num2; 407 } 408 } 409 return a < b ? -1 : 1; 410 } 411 } 412 413 // If one string is a substring of the other, the shorter string sorts first. 414 if (tokens1.length != tokens2.length) { 415 return tokens1.length - tokens2.length; 416 } 417 418 // The two strings must be equivalent except for case (perfect equality is 419 // tested at the head of the function.) Revert to default ASCII-betical string 420 // comparison to stablize the sort. 421 return str1 < str2 ? -1 : 1; 422 }; 423 424 425 /** 426 * URL-encodes a string 427 * @param {*} str The string to url-encode. 428 * @return {string} An encoded copy of {@code str} that is safe for urls. 429 * Note that '#', ':', and other characters used to delimit portions 430 * of URLs *will* be encoded. 431 */ 432 goog.string.urlEncode = function(str) { 433 return encodeURIComponent(String(str)); 434 }; 435 436 437 /** 438 * URL-decodes the string. We need to specially handle '+'s because 439 * the javascript library doesn't convert them to spaces. 440 * @param {string} str The string to url decode. 441 * @return {string} The decoded {@code str}. 442 */ 443 goog.string.urlDecode = function(str) { 444 return decodeURIComponent(str.replace(/\+/g, ' ')); 445 }; 446 447 448 /** 449 * Converts \n to <br>s or <br />s. 450 * @param {string} str The string in which to convert newlines. 451 * @param {boolean=} opt_xml Whether to use XML compatible tags. 452 * @return {string} A copy of {@code str} with converted newlines. 453 */ 454 goog.string.newLineToBr = function(str, opt_xml) { 455 return str.replace(/(\r\n|\r|\n)/g, opt_xml ? '<br />' : '<br>'); 456 }; 457 458 459 /** 460 * Escapes double quote '"' and single quote '\'' characters in addition to 461 * '&', '<', and '>' so that a string can be included in an HTML tag attribute 462 * value within double or single quotes. 463 * 464 * It should be noted that > doesn't need to be escaped for the HTML or XML to 465 * be valid, but it has been decided to escape it for consistency with other 466 * implementations. 467 * 468 * With goog.string.DETECT_DOUBLE_ESCAPING, this function escapes also the 469 * lowercase letter "e". 470 * 471 * NOTE(user): 472 * HtmlEscape is often called during the generation of large blocks of HTML. 473 * Using statics for the regular expressions and strings is an optimization 474 * that can more than half the amount of time IE spends in this function for 475 * large apps, since strings and regexes both contribute to GC allocations. 476 * 477 * Testing for the presence of a character before escaping increases the number 478 * of function calls, but actually provides a speed increase for the average 479 * case -- since the average case often doesn't require the escaping of all 4 480 * characters and indexOf() is much cheaper than replace(). 481 * The worst case does suffer slightly from the additional calls, therefore the 482 * opt_isLikelyToContainHtmlChars option has been included for situations 483 * where all 4 HTML entities are very likely to be present and need escaping. 484 * 485 * Some benchmarks (times tended to fluctuate +-0.05ms): 486 * FireFox IE6 487 * (no chars / average (mix of cases) / all 4 chars) 488 * no checks 0.13 / 0.22 / 0.22 0.23 / 0.53 / 0.80 489 * indexOf 0.08 / 0.17 / 0.26 0.22 / 0.54 / 0.84 490 * indexOf + re test 0.07 / 0.17 / 0.28 0.19 / 0.50 / 0.85 491 * 492 * An additional advantage of checking if replace actually needs to be called 493 * is a reduction in the number of object allocations, so as the size of the 494 * application grows the difference between the various methods would increase. 495 * 496 * @param {string} str string to be escaped. 497 * @param {boolean=} opt_isLikelyToContainHtmlChars Don't perform a check to see 498 * if the character needs replacing - use this option if you expect each of 499 * the characters to appear often. Leave false if you expect few html 500 * characters to occur in your strings, such as if you are escaping HTML. 501 * @return {string} An escaped copy of {@code str}. 502 */ 503 goog.string.htmlEscape = function(str, opt_isLikelyToContainHtmlChars) { 504 505 if (opt_isLikelyToContainHtmlChars) { 506 str = str.replace(goog.string.AMP_RE_, '&') 507 .replace(goog.string.LT_RE_, '<') 508 .replace(goog.string.GT_RE_, '>') 509 .replace(goog.string.QUOT_RE_, '"') 510 .replace(goog.string.SINGLE_QUOTE_RE_, ''') 511 .replace(goog.string.NULL_RE_, '�'); 512 if (goog.string.DETECT_DOUBLE_ESCAPING) { 513 str = str.replace(goog.string.E_RE_, 'e'); 514 } 515 return str; 516 517 } else { 518 // quick test helps in the case when there are no chars to replace, in 519 // worst case this makes barely a difference to the time taken 520 if (!goog.string.ALL_RE_.test(str)) return str; 521 522 // str.indexOf is faster than regex.test in this case 523 if (str.indexOf('&') != -1) { 524 str = str.replace(goog.string.AMP_RE_, '&'); 525 } 526 if (str.indexOf('<') != -1) { 527 str = str.replace(goog.string.LT_RE_, '<'); 528 } 529 if (str.indexOf('>') != -1) { 530 str = str.replace(goog.string.GT_RE_, '>'); 531 } 532 if (str.indexOf('"') != -1) { 533 str = str.replace(goog.string.QUOT_RE_, '"'); 534 } 535 if (str.indexOf('\'') != -1) { 536 str = str.replace(goog.string.SINGLE_QUOTE_RE_, '''); 537 } 538 if (str.indexOf('\x00') != -1) { 539 str = str.replace(goog.string.NULL_RE_, '�'); 540 } 541 if (goog.string.DETECT_DOUBLE_ESCAPING && str.indexOf('e') != -1) { 542 str = str.replace(goog.string.E_RE_, 'e'); 543 } 544 return str; 545 } 546 }; 547 548 549 /** 550 * Regular expression that matches an ampersand, for use in escaping. 551 * @const {!RegExp} 552 * @private 553 */ 554 goog.string.AMP_RE_ = /&/g; 555 556 557 /** 558 * Regular expression that matches a less than sign, for use in escaping. 559 * @const {!RegExp} 560 * @private 561 */ 562 goog.string.LT_RE_ = /</g; 563 564 565 /** 566 * Regular expression that matches a greater than sign, for use in escaping. 567 * @const {!RegExp} 568 * @private 569 */ 570 goog.string.GT_RE_ = />/g; 571 572 573 /** 574 * Regular expression that matches a double quote, for use in escaping. 575 * @const {!RegExp} 576 * @private 577 */ 578 goog.string.QUOT_RE_ = /"/g; 579 580 581 /** 582 * Regular expression that matches a single quote, for use in escaping. 583 * @const {!RegExp} 584 * @private 585 */ 586 goog.string.SINGLE_QUOTE_RE_ = /'/g; 587 588 589 /** 590 * Regular expression that matches null character, for use in escaping. 591 * @const {!RegExp} 592 * @private 593 */ 594 goog.string.NULL_RE_ = /\x00/g; 595 596 597 /** 598 * Regular expression that matches a lowercase letter "e", for use in escaping. 599 * @const {!RegExp} 600 * @private 601 */ 602 goog.string.E_RE_ = /e/g; 603 604 605 /** 606 * Regular expression that matches any character that needs to be escaped. 607 * @const {!RegExp} 608 * @private 609 */ 610 goog.string.ALL_RE_ = (goog.string.DETECT_DOUBLE_ESCAPING ? 611 /[\x00&<>"'e]/ : 612 /[\x00&<>"']/); 613 614 615 /** 616 * Unescapes an HTML string. 617 * 618 * @param {string} str The string to unescape. 619 * @return {string} An unescaped copy of {@code str}. 620 */ 621 goog.string.unescapeEntities = function(str) { 622 if (goog.string.contains(str, '&')) { 623 // We are careful not to use a DOM if we do not have one. We use the [] 624 // notation so that the JSCompiler will not complain about these objects and 625 // fields in the case where we have no DOM. 626 if ('document' in goog.global) { 627 return goog.string.unescapeEntitiesUsingDom_(str); 628 } else { 629 // Fall back on pure XML entities 630 return goog.string.unescapePureXmlEntities_(str); 631 } 632 } 633 return str; 634 }; 635 636 637 /** 638 * Unescapes a HTML string using the provided document. 639 * 640 * @param {string} str The string to unescape. 641 * @param {!Document} document A document to use in escaping the string. 642 * @return {string} An unescaped copy of {@code str}. 643 */ 644 goog.string.unescapeEntitiesWithDocument = function(str, document) { 645 if (goog.string.contains(str, '&')) { 646 return goog.string.unescapeEntitiesUsingDom_(str, document); 647 } 648 return str; 649 }; 650 651 652 /** 653 * Unescapes an HTML string using a DOM to resolve non-XML, non-numeric 654 * entities. This function is XSS-safe and whitespace-preserving. 655 * @private 656 * @param {string} str The string to unescape. 657 * @param {Document=} opt_document An optional document to use for creating 658 * elements. If this is not specified then the default window.document 659 * will be used. 660 * @return {string} The unescaped {@code str} string. 661 */ 662 goog.string.unescapeEntitiesUsingDom_ = function(str, opt_document) { 663 var seen = {'&': '&', '<': '<', '>': '>', '"': '"'}; 664 var div; 665 if (opt_document) { 666 div = opt_document.createElement('div'); 667 } else { 668 div = goog.global.document.createElement('div'); 669 } 670 // Match as many valid entity characters as possible. If the actual entity 671 // happens to be shorter, it will still work as innerHTML will return the 672 // trailing characters unchanged. Since the entity characters do not include 673 // open angle bracket, there is no chance of XSS from the innerHTML use. 674 // Since no whitespace is passed to innerHTML, whitespace is preserved. 675 return str.replace(goog.string.HTML_ENTITY_PATTERN_, function(s, entity) { 676 // Check for cached entity. 677 var value = seen[s]; 678 if (value) { 679 return value; 680 } 681 // Check for numeric entity. 682 if (entity.charAt(0) == '#') { 683 // Prefix with 0 so that hex entities (e.g. ) parse as hex numbers. 684 var n = Number('0' + entity.substr(1)); 685 if (!isNaN(n)) { 686 value = String.fromCharCode(n); 687 } 688 } 689 // Fall back to innerHTML otherwise. 690 if (!value) { 691 // Append a non-entity character to avoid a bug in Webkit that parses 692 // an invalid entity at the end of innerHTML text as the empty string. 693 div.innerHTML = s + ' '; 694 // Then remove the trailing character from the result. 695 value = div.firstChild.nodeValue.slice(0, -1); 696 } 697 // Cache and return. 698 return seen[s] = value; 699 }); 700 }; 701 702 703 /** 704 * Unescapes XML entities. 705 * @private 706 * @param {string} str The string to unescape. 707 * @return {string} An unescaped copy of {@code str}. 708 */ 709 goog.string.unescapePureXmlEntities_ = function(str) { 710 return str.replace(/&([^;]+);/g, function(s, entity) { 711 switch (entity) { 712 case 'amp': 713 return '&'; 714 case 'lt': 715 return '<'; 716 case 'gt': 717 return '>'; 718 case 'quot': 719 return '"'; 720 default: 721 if (entity.charAt(0) == '#') { 722 // Prefix with 0 so that hex entities (e.g. ) parse as hex. 723 var n = Number('0' + entity.substr(1)); 724 if (!isNaN(n)) { 725 return String.fromCharCode(n); 726 } 727 } 728 // For invalid entities we just return the entity 729 return s; 730 } 731 }); 732 }; 733 734 735 /** 736 * Regular expression that matches an HTML entity. 737 * See also HTML5: Tokenization / Tokenizing character references. 738 * @private 739 * @type {!RegExp} 740 */ 741 goog.string.HTML_ENTITY_PATTERN_ = /&([^;\s<&]+);?/g; 742 743 744 /** 745 * Do escaping of whitespace to preserve spatial formatting. We use character 746 * entity #160 to make it safer for xml. 747 * @param {string} str The string in which to escape whitespace. 748 * @param {boolean=} opt_xml Whether to use XML compatible tags. 749 * @return {string} An escaped copy of {@code str}. 750 */ 751 goog.string.whitespaceEscape = function(str, opt_xml) { 752 // This doesn't use goog.string.preserveSpaces for backwards compatibility. 753 return goog.string.newLineToBr(str.replace(/ /g, '  '), opt_xml); 754 }; 755 756 757 /** 758 * Preserve spaces that would be otherwise collapsed in HTML by replacing them 759 * with non-breaking space Unicode characters. 760 * @param {string} str The string in which to preserve whitespace. 761 * @return {string} A copy of {@code str} with preserved whitespace. 762 */ 763 goog.string.preserveSpaces = function(str) { 764 return str.replace(/(^|[\n ]) /g, '$1' + goog.string.Unicode.NBSP); 765 }; 766 767 768 /** 769 * Strip quote characters around a string. The second argument is a string of 770 * characters to treat as quotes. This can be a single character or a string of 771 * multiple character and in that case each of those are treated as possible 772 * quote characters. For example: 773 * 774 * <pre> 775 * goog.string.stripQuotes('"abc"', '"`') --> 'abc' 776 * goog.string.stripQuotes('`abc`', '"`') --> 'abc' 777 * </pre> 778 * 779 * @param {string} str The string to strip. 780 * @param {string} quoteChars The quote characters to strip. 781 * @return {string} A copy of {@code str} without the quotes. 782 */ 783 goog.string.stripQuotes = function(str, quoteChars) { 784 var length = quoteChars.length; 785 for (var i = 0; i < length; i++) { 786 var quoteChar = length == 1 ? quoteChars : quoteChars.charAt(i); 787 if (str.charAt(0) == quoteChar && str.charAt(str.length - 1) == quoteChar) { 788 return str.substring(1, str.length - 1); 789 } 790 } 791 return str; 792 }; 793 794 795 /** 796 * Truncates a string to a certain length and adds '...' if necessary. The 797 * length also accounts for the ellipsis, so a maximum length of 10 and a string 798 * 'Hello World!' produces 'Hello W...'. 799 * @param {string} str The string to truncate. 800 * @param {number} chars Max number of characters. 801 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped 802 * characters from being cut off in the middle. 803 * @return {string} The truncated {@code str} string. 804 */ 805 goog.string.truncate = function(str, chars, opt_protectEscapedCharacters) { 806 if (opt_protectEscapedCharacters) { 807 str = goog.string.unescapeEntities(str); 808 } 809 810 if (str.length > chars) { 811 str = str.substring(0, chars - 3) + '...'; 812 } 813 814 if (opt_protectEscapedCharacters) { 815 str = goog.string.htmlEscape(str); 816 } 817 818 return str; 819 }; 820 821 822 /** 823 * Truncate a string in the middle, adding "..." if necessary, 824 * and favoring the beginning of the string. 825 * @param {string} str The string to truncate the middle of. 826 * @param {number} chars Max number of characters. 827 * @param {boolean=} opt_protectEscapedCharacters Whether to protect escaped 828 * characters from being cutoff in the middle. 829 * @param {number=} opt_trailingChars Optional number of trailing characters to 830 * leave at the end of the string, instead of truncating as close to the 831 * middle as possible. 832 * @return {string} A truncated copy of {@code str}. 833 */ 834 goog.string.truncateMiddle = function(str, chars, 835 opt_protectEscapedCharacters, opt_trailingChars) { 836 if (opt_protectEscapedCharacters) { 837 str = goog.string.unescapeEntities(str); 838 } 839 840 if (opt_trailingChars && str.length > chars) { 841 if (opt_trailingChars > chars) { 842 opt_trailingChars = chars; 843 } 844 var endPoint = str.length - opt_trailingChars; 845 var startPoint = chars - opt_trailingChars; 846 str = str.substring(0, startPoint) + '...' + str.substring(endPoint); 847 } else if (str.length > chars) { 848 // Favor the beginning of the string: 849 var half = Math.floor(chars / 2); 850 var endPos = str.length - half; 851 half += chars % 2; 852 str = str.substring(0, half) + '...' + str.substring(endPos); 853 } 854 855 if (opt_protectEscapedCharacters) { 856 str = goog.string.htmlEscape(str); 857 } 858 859 return str; 860 }; 861 862 863 /** 864 * Special chars that need to be escaped for goog.string.quote. 865 * @private 866 * @type {Object} 867 */ 868 goog.string.specialEscapeChars_ = { 869 '\0': '\\0', 870 '\b': '\\b', 871 '\f': '\\f', 872 '\n': '\\n', 873 '\r': '\\r', 874 '\t': '\\t', 875 '\x0B': '\\x0B', // '\v' is not supported in JScript 876 '"': '\\"', 877 '\\': '\\\\' 878 }; 879 880 881 /** 882 * Character mappings used internally for goog.string.escapeChar. 883 * @private 884 * @type {Object} 885 */ 886 goog.string.jsEscapeCache_ = { 887 '\'': '\\\'' 888 }; 889 890 891 /** 892 * Encloses a string in double quotes and escapes characters so that the 893 * string is a valid JS string. 894 * @param {string} s The string to quote. 895 * @return {string} A copy of {@code s} surrounded by double quotes. 896 */ 897 goog.string.quote = function(s) { 898 s = String(s); 899 if (s.quote) { 900 return s.quote(); 901 } else { 902 var sb = ['"']; 903 for (var i = 0; i < s.length; i++) { 904 var ch = s.charAt(i); 905 var cc = ch.charCodeAt(0); 906 sb[i + 1] = goog.string.specialEscapeChars_[ch] || 907 ((cc > 31 && cc < 127) ? ch : goog.string.escapeChar(ch)); 908 } 909 sb.push('"'); 910 return sb.join(''); 911 } 912 }; 913 914 915 /** 916 * Takes a string and returns the escaped string for that character. 917 * @param {string} str The string to escape. 918 * @return {string} An escaped string representing {@code str}. 919 */ 920 goog.string.escapeString = function(str) { 921 var sb = []; 922 for (var i = 0; i < str.length; i++) { 923 sb[i] = goog.string.escapeChar(str.charAt(i)); 924 } 925 return sb.join(''); 926 }; 927 928 929 /** 930 * Takes a character and returns the escaped string for that character. For 931 * example escapeChar(String.fromCharCode(15)) -> "\\x0E". 932 * @param {string} c The character to escape. 933 * @return {string} An escaped string representing {@code c}. 934 */ 935 goog.string.escapeChar = function(c) { 936 if (c in goog.string.jsEscapeCache_) { 937 return goog.string.jsEscapeCache_[c]; 938 } 939 940 if (c in goog.string.specialEscapeChars_) { 941 return goog.string.jsEscapeCache_[c] = goog.string.specialEscapeChars_[c]; 942 } 943 944 var rv = c; 945 var cc = c.charCodeAt(0); 946 if (cc > 31 && cc < 127) { 947 rv = c; 948 } else { 949 // tab is 9 but handled above 950 if (cc < 256) { 951 rv = '\\x'; 952 if (cc < 16 || cc > 256) { 953 rv += '0'; 954 } 955 } else { 956 rv = '\\u'; 957 if (cc < 4096) { // \u1000 958 rv += '0'; 959 } 960 } 961 rv += cc.toString(16).toUpperCase(); 962 } 963 964 return goog.string.jsEscapeCache_[c] = rv; 965 }; 966 967 968 /** 969 * Takes a string and creates a map (Object) in which the keys are the 970 * characters in the string. The value for the key is set to true. You can 971 * then use goog.object.map or goog.array.map to change the values. 972 * @param {string} s The string to build the map from. 973 * @return {!Object} The map of characters used. 974 */ 975 // TODO(arv): It seems like we should have a generic goog.array.toMap. But do 976 // we want a dependency on goog.array in goog.string? 977 goog.string.toMap = function(s) { 978 var rv = {}; 979 for (var i = 0; i < s.length; i++) { 980 rv[s.charAt(i)] = true; 981 } 982 return rv; 983 }; 984 985 986 /** 987 * Determines whether a string contains a substring. 988 * @param {string} str The string to search. 989 * @param {string} subString The substring to search for. 990 * @return {boolean} Whether {@code str} contains {@code subString}. 991 */ 992 goog.string.contains = function(str, subString) { 993 return str.indexOf(subString) != -1; 994 }; 995 996 997 /** 998 * Determines whether a string contains a substring, ignoring case. 999 * @param {string} str The string to search. 1000 * @param {string} subString The substring to search for. 1001 * @return {boolean} Whether {@code str} contains {@code subString}. 1002 */ 1003 goog.string.caseInsensitiveContains = function(str, subString) { 1004 return goog.string.contains(str.toLowerCase(), subString.toLowerCase()); 1005 }; 1006 1007 1008 /** 1009 * Returns the non-overlapping occurrences of ss in s. 1010 * If either s or ss evalutes to false, then returns zero. 1011 * @param {string} s The string to look in. 1012 * @param {string} ss The string to look for. 1013 * @return {number} Number of occurrences of ss in s. 1014 */ 1015 goog.string.countOf = function(s, ss) { 1016 return s && ss ? s.split(ss).length - 1 : 0; 1017 }; 1018 1019 1020 /** 1021 * Removes a substring of a specified length at a specific 1022 * index in a string. 1023 * @param {string} s The base string from which to remove. 1024 * @param {number} index The index at which to remove the substring. 1025 * @param {number} stringLength The length of the substring to remove. 1026 * @return {string} A copy of {@code s} with the substring removed or the full 1027 * string if nothing is removed or the input is invalid. 1028 */ 1029 goog.string.removeAt = function(s, index, stringLength) { 1030 var resultStr = s; 1031 // If the index is greater or equal to 0 then remove substring 1032 if (index >= 0 && index < s.length && stringLength > 0) { 1033 resultStr = s.substr(0, index) + 1034 s.substr(index + stringLength, s.length - index - stringLength); 1035 } 1036 return resultStr; 1037 }; 1038 1039 1040 /** 1041 * Removes the first occurrence of a substring from a string. 1042 * @param {string} s The base string from which to remove. 1043 * @param {string} ss The string to remove. 1044 * @return {string} A copy of {@code s} with {@code ss} removed or the full 1045 * string if nothing is removed. 1046 */ 1047 goog.string.remove = function(s, ss) { 1048 var re = new RegExp(goog.string.regExpEscape(ss), ''); 1049 return s.replace(re, ''); 1050 }; 1051 1052 1053 /** 1054 * Removes all occurrences of a substring from a string. 1055 * @param {string} s The base string from which to remove. 1056 * @param {string} ss The string to remove. 1057 * @return {string} A copy of {@code s} with {@code ss} removed or the full 1058 * string if nothing is removed. 1059 */ 1060 goog.string.removeAll = function(s, ss) { 1061 var re = new RegExp(goog.string.regExpEscape(ss), 'g'); 1062 return s.replace(re, ''); 1063 }; 1064 1065 1066 /** 1067 * Escapes characters in the string that are not safe to use in a RegExp. 1068 * @param {*} s The string to escape. If not a string, it will be casted 1069 * to one. 1070 * @return {string} A RegExp safe, escaped copy of {@code s}. 1071 */ 1072 goog.string.regExpEscape = function(s) { 1073 return String(s).replace(/([-()\[\]{}+?*.$\^|,:#<!\\])/g, '\\$1'). 1074 replace(/\x08/g, '\\x08'); 1075 }; 1076 1077 1078 /** 1079 * Repeats a string n times. 1080 * @param {string} string The string to repeat. 1081 * @param {number} length The number of times to repeat. 1082 * @return {string} A string containing {@code length} repetitions of 1083 * {@code string}. 1084 */ 1085 goog.string.repeat = function(string, length) { 1086 return new Array(length + 1).join(string); 1087 }; 1088 1089 1090 /** 1091 * Pads number to given length and optionally rounds it to a given precision. 1092 * For example: 1093 * <pre>padNumber(1.25, 2, 3) -> '01.250' 1094 * padNumber(1.25, 2) -> '01.25' 1095 * padNumber(1.25, 2, 1) -> '01.3' 1096 * padNumber(1.25, 0) -> '1.25'</pre> 1097 * 1098 * @param {number} num The number to pad. 1099 * @param {number} length The desired length. 1100 * @param {number=} opt_precision The desired precision. 1101 * @return {string} {@code num} as a string with the given options. 1102 */ 1103 goog.string.padNumber = function(num, length, opt_precision) { 1104 var s = goog.isDef(opt_precision) ? num.toFixed(opt_precision) : String(num); 1105 var index = s.indexOf('.'); 1106 if (index == -1) { 1107 index = s.length; 1108 } 1109 return goog.string.repeat('0', Math.max(0, length - index)) + s; 1110 }; 1111 1112 1113 /** 1114 * Returns a string representation of the given object, with 1115 * null and undefined being returned as the empty string. 1116 * 1117 * @param {*} obj The object to convert. 1118 * @return {string} A string representation of the {@code obj}. 1119 */ 1120 goog.string.makeSafe = function(obj) { 1121 return obj == null ? '' : String(obj); 1122 }; 1123 1124 1125 /** 1126 * Concatenates string expressions. This is useful 1127 * since some browsers are very inefficient when it comes to using plus to 1128 * concat strings. Be careful when using null and undefined here since 1129 * these will not be included in the result. If you need to represent these 1130 * be sure to cast the argument to a String first. 1131 * For example: 1132 * <pre>buildString('a', 'b', 'c', 'd') -> 'abcd' 1133 * buildString(null, undefined) -> '' 1134 * </pre> 1135 * @param {...*} var_args A list of strings to concatenate. If not a string, 1136 * it will be casted to one. 1137 * @return {string} The concatenation of {@code var_args}. 1138 */ 1139 goog.string.buildString = function(var_args) { 1140 return Array.prototype.join.call(arguments, ''); 1141 }; 1142 1143 1144 /** 1145 * Returns a string with at least 64-bits of randomness. 1146 * 1147 * Doesn't trust Javascript's random function entirely. Uses a combination of 1148 * random and current timestamp, and then encodes the string in base-36 to 1149 * make it shorter. 1150 * 1151 * @return {string} A random string, e.g. sn1s7vb4gcic. 1152 */ 1153 goog.string.getRandomString = function() { 1154 var x = 2147483648; 1155 return Math.floor(Math.random() * x).toString(36) + 1156 Math.abs(Math.floor(Math.random() * x) ^ goog.now()).toString(36); 1157 }; 1158 1159 1160 /** 1161 * Compares two version numbers. 1162 * 1163 * @param {string|number} version1 Version of first item. 1164 * @param {string|number} version2 Version of second item. 1165 * 1166 * @return {number} 1 if {@code version1} is higher. 1167 * 0 if arguments are equal. 1168 * -1 if {@code version2} is higher. 1169 */ 1170 goog.string.compareVersions = function(version1, version2) { 1171 var order = 0; 1172 // Trim leading and trailing whitespace and split the versions into 1173 // subversions. 1174 var v1Subs = goog.string.trim(String(version1)).split('.'); 1175 var v2Subs = goog.string.trim(String(version2)).split('.'); 1176 var subCount = Math.max(v1Subs.length, v2Subs.length); 1177 1178 // Iterate over the subversions, as long as they appear to be equivalent. 1179 for (var subIdx = 0; order == 0 && subIdx < subCount; subIdx++) { 1180 var v1Sub = v1Subs[subIdx] || ''; 1181 var v2Sub = v2Subs[subIdx] || ''; 1182 1183 // Split the subversions into pairs of numbers and qualifiers (like 'b'). 1184 // Two different RegExp objects are needed because they are both using 1185 // the 'g' flag. 1186 var v1CompParser = new RegExp('(\\d*)(\\D*)', 'g'); 1187 var v2CompParser = new RegExp('(\\d*)(\\D*)', 'g'); 1188 do { 1189 var v1Comp = v1CompParser.exec(v1Sub) || ['', '', '']; 1190 var v2Comp = v2CompParser.exec(v2Sub) || ['', '', '']; 1191 // Break if there are no more matches. 1192 if (v1Comp[0].length == 0 && v2Comp[0].length == 0) { 1193 break; 1194 } 1195 1196 // Parse the numeric part of the subversion. A missing number is 1197 // equivalent to 0. 1198 var v1CompNum = v1Comp[1].length == 0 ? 0 : parseInt(v1Comp[1], 10); 1199 var v2CompNum = v2Comp[1].length == 0 ? 0 : parseInt(v2Comp[1], 10); 1200 1201 // Compare the subversion components. The number has the highest 1202 // precedence. Next, if the numbers are equal, a subversion without any 1203 // qualifier is always higher than a subversion with any qualifier. Next, 1204 // the qualifiers are compared as strings. 1205 order = goog.string.compareElements_(v1CompNum, v2CompNum) || 1206 goog.string.compareElements_(v1Comp[2].length == 0, 1207 v2Comp[2].length == 0) || 1208 goog.string.compareElements_(v1Comp[2], v2Comp[2]); 1209 // Stop as soon as an inequality is discovered. 1210 } while (order == 0); 1211 } 1212 1213 return order; 1214 }; 1215 1216 1217 /** 1218 * Compares elements of a version number. 1219 * 1220 * @param {string|number|boolean} left An element from a version number. 1221 * @param {string|number|boolean} right An element from a version number. 1222 * 1223 * @return {number} 1 if {@code left} is higher. 1224 * 0 if arguments are equal. 1225 * -1 if {@code right} is higher. 1226 * @private 1227 */ 1228 goog.string.compareElements_ = function(left, right) { 1229 if (left < right) { 1230 return -1; 1231 } else if (left > right) { 1232 return 1; 1233 } 1234 return 0; 1235 }; 1236 1237 1238 /** 1239 * Maximum value of #goog.string.hashCode, exclusive. 2^32. 1240 * @type {number} 1241 * @private 1242 */ 1243 goog.string.HASHCODE_MAX_ = 0x100000000; 1244 1245 1246 /** 1247 * String hash function similar to java.lang.String.hashCode(). 1248 * The hash code for a string is computed as 1249 * s[0] * 31 ^ (n - 1) + s[1] * 31 ^ (n - 2) + ... + s[n - 1], 1250 * where s[i] is the ith character of the string and n is the length of 1251 * the string. We mod the result to make it between 0 (inclusive) and 2^32 1252 * (exclusive). 1253 * @param {string} str A string. 1254 * @return {number} Hash value for {@code str}, between 0 (inclusive) and 2^32 1255 * (exclusive). The empty string returns 0. 1256 */ 1257 goog.string.hashCode = function(str) { 1258 var result = 0; 1259 for (var i = 0; i < str.length; ++i) { 1260 result = 31 * result + str.charCodeAt(i); 1261 // Normalize to 4 byte range, 0 ... 2^32. 1262 result %= goog.string.HASHCODE_MAX_; 1263 } 1264 return result; 1265 }; 1266 1267 1268 /** 1269 * The most recent unique ID. |0 is equivalent to Math.floor in this case. 1270 * @type {number} 1271 * @private 1272 */ 1273 goog.string.uniqueStringCounter_ = Math.random() * 0x80000000 | 0; 1274 1275 1276 /** 1277 * Generates and returns a string which is unique in the current document. 1278 * This is useful, for example, to create unique IDs for DOM elements. 1279 * @return {string} A unique id. 1280 */ 1281 goog.string.createUniqueString = function() { 1282 return 'goog_' + goog.string.uniqueStringCounter_++; 1283 }; 1284 1285 1286 /** 1287 * Converts the supplied string to a number, which may be Infinity or NaN. 1288 * This function strips whitespace: (toNumber(' 123') === 123) 1289 * This function accepts scientific notation: (toNumber('1e1') === 10) 1290 * 1291 * This is better than Javascript's built-in conversions because, sadly: 1292 * (Number(' ') === 0) and (parseFloat('123a') === 123) 1293 * 1294 * @param {string} str The string to convert. 1295 * @return {number} The number the supplied string represents, or NaN. 1296 */ 1297 goog.string.toNumber = function(str) { 1298 var num = Number(str); 1299 if (num == 0 && goog.string.isEmpty(str)) { 1300 return NaN; 1301 } 1302 return num; 1303 }; 1304 1305 1306 /** 1307 * Returns whether the given string is lower camel case (e.g. "isFooBar"). 1308 * 1309 * Note that this assumes the string is entirely letters. 1310 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms 1311 * 1312 * @param {string} str String to test. 1313 * @return {boolean} Whether the string is lower camel case. 1314 */ 1315 goog.string.isLowerCamelCase = function(str) { 1316 return /^[a-z]+([A-Z][a-z]*)*$/.test(str); 1317 }; 1318 1319 1320 /** 1321 * Returns whether the given string is upper camel case (e.g. "FooBarBaz"). 1322 * 1323 * Note that this assumes the string is entirely letters. 1324 * @see http://en.wikipedia.org/wiki/CamelCase#Variations_and_synonyms 1325 * 1326 * @param {string} str String to test. 1327 * @return {boolean} Whether the string is upper camel case. 1328 */ 1329 goog.string.isUpperCamelCase = function(str) { 1330 return /^([A-Z][a-z]*)+$/.test(str); 1331 }; 1332 1333 1334 /** 1335 * Converts a string from selector-case to camelCase (e.g. from 1336 * "multi-part-string" to "multiPartString"), useful for converting 1337 * CSS selectors and HTML dataset keys to their equivalent JS properties. 1338 * @param {string} str The string in selector-case form. 1339 * @return {string} The string in camelCase form. 1340 */ 1341 goog.string.toCamelCase = function(str) { 1342 return String(str).replace(/\-([a-z])/g, function(all, match) { 1343 return match.toUpperCase(); 1344 }); 1345 }; 1346 1347 1348 /** 1349 * Converts a string from camelCase to selector-case (e.g. from 1350 * "multiPartString" to "multi-part-string"), useful for converting JS 1351 * style and dataset properties to equivalent CSS selectors and HTML keys. 1352 * @param {string} str The string in camelCase form. 1353 * @return {string} The string in selector-case form. 1354 */ 1355 goog.string.toSelectorCase = function(str) { 1356 return String(str).replace(/([A-Z])/g, '-$1').toLowerCase(); 1357 }; 1358 1359 1360 /** 1361 * Converts a string into TitleCase. First character of the string is always 1362 * capitalized in addition to the first letter of every subsequent word. 1363 * Words are delimited by one or more whitespaces by default. Custom delimiters 1364 * can optionally be specified to replace the default, which doesn't preserve 1365 * whitespace delimiters and instead must be explicitly included if needed. 1366 * 1367 * Default delimiter => " ": 1368 * goog.string.toTitleCase('oneTwoThree') => 'OneTwoThree' 1369 * goog.string.toTitleCase('one two three') => 'One Two Three' 1370 * goog.string.toTitleCase(' one two ') => ' One Two ' 1371 * goog.string.toTitleCase('one_two_three') => 'One_two_three' 1372 * goog.string.toTitleCase('one-two-three') => 'One-two-three' 1373 * 1374 * Custom delimiter => "_-.": 1375 * goog.string.toTitleCase('oneTwoThree', '_-.') => 'OneTwoThree' 1376 * goog.string.toTitleCase('one two three', '_-.') => 'One two three' 1377 * goog.string.toTitleCase(' one two ', '_-.') => ' one two ' 1378 * goog.string.toTitleCase('one_two_three', '_-.') => 'One_Two_Three' 1379 * goog.string.toTitleCase('one-two-three', '_-.') => 'One-Two-Three' 1380 * goog.string.toTitleCase('one...two...three', '_-.') => 'One...Two...Three' 1381 * goog.string.toTitleCase('one. two. three', '_-.') => 'One. two. three' 1382 * goog.string.toTitleCase('one-two.three', '_-.') => 'One-Two.Three' 1383 * 1384 * @param {string} str String value in camelCase form. 1385 * @param {string=} opt_delimiters Custom delimiter character set used to 1386 * distinguish words in the string value. Each character represents a 1387 * single delimiter. When provided, default whitespace delimiter is 1388 * overridden and must be explicitly included if needed. 1389 * @return {string} String value in TitleCase form. 1390 */ 1391 goog.string.toTitleCase = function(str, opt_delimiters) { 1392 var delimiters = goog.isString(opt_delimiters) ? 1393 goog.string.regExpEscape(opt_delimiters) : '\\s'; 1394 1395 // For IE8, we need to prevent using an empty character set. Otherwise, 1396 // incorrect matching will occur. 1397 delimiters = delimiters ? '|[' + delimiters + ']+' : ''; 1398 1399 var regexp = new RegExp('(^' + delimiters + ')([a-z])', 'g'); 1400 return str.replace(regexp, function(all, p1, p2) { 1401 return p1 + p2.toUpperCase(); 1402 }); 1403 }; 1404 1405 1406 /** 1407 * Parse a string in decimal or hexidecimal ('0xFFFF') form. 1408 * 1409 * To parse a particular radix, please use parseInt(string, radix) directly. See 1410 * https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt 1411 * 1412 * This is a wrapper for the built-in parseInt function that will only parse 1413 * numbers as base 10 or base 16. Some JS implementations assume strings 1414 * starting with "0" are intended to be octal. ES3 allowed but discouraged 1415 * this behavior. ES5 forbids it. This function emulates the ES5 behavior. 1416 * 1417 * For more information, see Mozilla JS Reference: http://goo.gl/8RiFj 1418 * 1419 * @param {string|number|null|undefined} value The value to be parsed. 1420 * @return {number} The number, parsed. If the string failed to parse, this 1421 * will be NaN. 1422 */ 1423 goog.string.parseInt = function(value) { 1424 // Force finite numbers to strings. 1425 if (isFinite(value)) { 1426 value = String(value); 1427 } 1428 1429 if (goog.isString(value)) { 1430 // If the string starts with '0x' or '-0x', parse as hex. 1431 return /^\s*-?0x/i.test(value) ? 1432 parseInt(value, 16) : parseInt(value, 10); 1433 } 1434 1435 return NaN; 1436 }; 1437 1438 1439 /** 1440 * Splits a string on a separator a limited number of times. 1441 * 1442 * This implementation is more similar to Python or Java, where the limit 1443 * parameter specifies the maximum number of splits rather than truncating 1444 * the number of results. 1445 * 1446 * See http://docs.python.org/2/library/stdtypes.html#str.split 1447 * See JavaDoc: http://goo.gl/F2AsY 1448 * See Mozilla reference: http://goo.gl/dZdZs 1449 * 1450 * @param {string} str String to split. 1451 * @param {string} separator The separator. 1452 * @param {number} limit The limit to the number of splits. The resulting array 1453 * will have a maximum length of limit+1. Negative numbers are the same 1454 * as zero. 1455 * @return {!Array.<string>} The string, split. 1456 */ 1457 1458 goog.string.splitLimit = function(str, separator, limit) { 1459 var parts = str.split(separator); 1460 var returnVal = []; 1461 1462 // Only continue doing this while we haven't hit the limit and we have 1463 // parts left. 1464 while (limit > 0 && parts.length) { 1465 returnVal.push(parts.shift()); 1466 limit--; 1467 } 1468 1469 // If there are remaining parts, append them to the end. 1470 if (parts.length) { 1471 returnVal.push(parts.join(separator)); 1472 } 1473 1474 return returnVal; 1475 }; 1476 lib/goog/structs/map.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Datastructure: Hash Map. 17 * 18 * @author arv@google.com (Erik Arvidsson) 19 * @author jonp@google.com (Jon Perlow) Optimized for IE6 20 * 21 * This file contains an implementation of a Map structure. It implements a lot 22 * of the methods used in goog.structs so those functions work on hashes. This 23 * is best suited for complex key types. For simple keys such as numbers and 24 * strings, and where special names like __proto__ are not a concern, consider 25 * using the lighter-weight utilities in goog.object. 26 */ 27 28 29 goog.provide('goog.structs.Map'); 30 31 goog.require('goog.iter.Iterator'); 32 goog.require('goog.iter.StopIteration'); 33 goog.require('goog.object'); 34 35 36 37 /** 38 * Class for Hash Map datastructure. 39 * @param {*=} opt_map Map or Object to initialize the map with. 40 * @param {...*} var_args If 2 or more arguments are present then they 41 * will be used as key-value pairs. 42 * @constructor 43 * @template K, V 44 */ 45 goog.structs.Map = function(opt_map, var_args) { 46 47 /** 48 * Underlying JS object used to implement the map. 49 * @private {!Object} 50 */ 51 this.map_ = {}; 52 53 /** 54 * An array of keys. This is necessary for two reasons: 55 * 1. Iterating the keys using for (var key in this.map_) allocates an 56 * object for every key in IE which is really bad for IE6 GC perf. 57 * 2. Without a side data structure, we would need to escape all the keys 58 * as that would be the only way we could tell during iteration if the 59 * key was an internal key or a property of the object. 60 * 61 * This array can contain deleted keys so it's necessary to check the map 62 * as well to see if the key is still in the map (this doesn't require a 63 * memory allocation in IE). 64 * @private {!Array.<string>} 65 */ 66 this.keys_ = []; 67 68 /** 69 * The number of key value pairs in the map. 70 * @private {number} 71 */ 72 this.count_ = 0; 73 74 /** 75 * Version used to detect changes while iterating. 76 * @private {number} 77 */ 78 this.version_ = 0; 79 80 var argLength = arguments.length; 81 82 if (argLength > 1) { 83 if (argLength % 2) { 84 throw Error('Uneven number of arguments'); 85 } 86 for (var i = 0; i < argLength; i += 2) { 87 this.set(arguments[i], arguments[i + 1]); 88 } 89 } else if (opt_map) { 90 this.addAll(/** @type {Object} */ (opt_map)); 91 } 92 }; 93 94 95 /** 96 * @return {number} The number of key-value pairs in the map. 97 */ 98 goog.structs.Map.prototype.getCount = function() { 99 return this.count_; 100 }; 101 102 103 /** 104 * Returns the values of the map. 105 * @return {!Array.<V>} The values in the map. 106 */ 107 goog.structs.Map.prototype.getValues = function() { 108 this.cleanupKeysArray_(); 109 110 var rv = []; 111 for (var i = 0; i < this.keys_.length; i++) { 112 var key = this.keys_[i]; 113 rv.push(this.map_[key]); 114 } 115 return rv; 116 }; 117 118 119 /** 120 * Returns the keys of the map. 121 * @return {!Array.<string>} Array of string values. 122 */ 123 goog.structs.Map.prototype.getKeys = function() { 124 this.cleanupKeysArray_(); 125 return /** @type {!Array.<string>} */ (this.keys_.concat()); 126 }; 127 128 129 /** 130 * Whether the map contains the given key. 131 * @param {*} key The key to check for. 132 * @return {boolean} Whether the map contains the key. 133 */ 134 goog.structs.Map.prototype.containsKey = function(key) { 135 return goog.structs.Map.hasKey_(this.map_, key); 136 }; 137 138 139 /** 140 * Whether the map contains the given value. This is O(n). 141 * @param {V} val The value to check for. 142 * @return {boolean} Whether the map contains the value. 143 */ 144 goog.structs.Map.prototype.containsValue = function(val) { 145 for (var i = 0; i < this.keys_.length; i++) { 146 var key = this.keys_[i]; 147 if (goog.structs.Map.hasKey_(this.map_, key) && this.map_[key] == val) { 148 return true; 149 } 150 } 151 return false; 152 }; 153 154 155 /** 156 * Whether this map is equal to the argument map. 157 * @param {goog.structs.Map} otherMap The map against which to test equality. 158 * @param {function(V, V): boolean=} opt_equalityFn Optional equality function 159 * to test equality of values. If not specified, this will test whether 160 * the values contained in each map are identical objects. 161 * @return {boolean} Whether the maps are equal. 162 */ 163 goog.structs.Map.prototype.equals = function(otherMap, opt_equalityFn) { 164 if (this === otherMap) { 165 return true; 166 } 167 168 if (this.count_ != otherMap.getCount()) { 169 return false; 170 } 171 172 var equalityFn = opt_equalityFn || goog.structs.Map.defaultEquals; 173 174 this.cleanupKeysArray_(); 175 for (var key, i = 0; key = this.keys_[i]; i++) { 176 if (!equalityFn(this.get(key), otherMap.get(key))) { 177 return false; 178 } 179 } 180 181 return true; 182 }; 183 184 185 /** 186 * Default equality test for values. 187 * @param {*} a The first value. 188 * @param {*} b The second value. 189 * @return {boolean} Whether a and b reference the same object. 190 */ 191 goog.structs.Map.defaultEquals = function(a, b) { 192 return a === b; 193 }; 194 195 196 /** 197 * @return {boolean} Whether the map is empty. 198 */ 199 goog.structs.Map.prototype.isEmpty = function() { 200 return this.count_ == 0; 201 }; 202 203 204 /** 205 * Removes all key-value pairs from the map. 206 */ 207 goog.structs.Map.prototype.clear = function() { 208 this.map_ = {}; 209 this.keys_.length = 0; 210 this.count_ = 0; 211 this.version_ = 0; 212 }; 213 214 215 /** 216 * Removes a key-value pair based on the key. This is O(logN) amortized due to 217 * updating the keys array whenever the count becomes half the size of the keys 218 * in the keys array. 219 * @param {*} key The key to remove. 220 * @return {boolean} Whether object was removed. 221 */ 222 goog.structs.Map.prototype.remove = function(key) { 223 if (goog.structs.Map.hasKey_(this.map_, key)) { 224 delete this.map_[key]; 225 this.count_--; 226 this.version_++; 227 228 // clean up the keys array if the threshhold is hit 229 if (this.keys_.length > 2 * this.count_) { 230 this.cleanupKeysArray_(); 231 } 232 233 return true; 234 } 235 return false; 236 }; 237 238 239 /** 240 * Cleans up the temp keys array by removing entries that are no longer in the 241 * map. 242 * @private 243 */ 244 goog.structs.Map.prototype.cleanupKeysArray_ = function() { 245 if (this.count_ != this.keys_.length) { 246 // First remove keys that are no longer in the map. 247 var srcIndex = 0; 248 var destIndex = 0; 249 while (srcIndex < this.keys_.length) { 250 var key = this.keys_[srcIndex]; 251 if (goog.structs.Map.hasKey_(this.map_, key)) { 252 this.keys_[destIndex++] = key; 253 } 254 srcIndex++; 255 } 256 this.keys_.length = destIndex; 257 } 258 259 if (this.count_ != this.keys_.length) { 260 // If the count still isn't correct, that means we have duplicates. This can 261 // happen when the same key is added and removed multiple times. Now we have 262 // to allocate one extra Object to remove the duplicates. This could have 263 // been done in the first pass, but in the common case, we can avoid 264 // allocating an extra object by only doing this when necessary. 265 var seen = {}; 266 var srcIndex = 0; 267 var destIndex = 0; 268 while (srcIndex < this.keys_.length) { 269 var key = this.keys_[srcIndex]; 270 if (!(goog.structs.Map.hasKey_(seen, key))) { 271 this.keys_[destIndex++] = key; 272 seen[key] = 1; 273 } 274 srcIndex++; 275 } 276 this.keys_.length = destIndex; 277 } 278 }; 279 280 281 /** 282 * Returns the value for the given key. If the key is not found and the default 283 * value is not given this will return {@code undefined}. 284 * @param {*} key The key to get the value for. 285 * @param {DEFAULT=} opt_val The value to return if no item is found for the 286 * given key, defaults to undefined. 287 * @return {V|DEFAULT} The value for the given key. 288 * @template DEFAULT 289 */ 290 goog.structs.Map.prototype.get = function(key, opt_val) { 291 if (goog.structs.Map.hasKey_(this.map_, key)) { 292 return this.map_[key]; 293 } 294 return opt_val; 295 }; 296 297 298 /** 299 * Adds a key-value pair to the map. 300 * @param {*} key The key. 301 * @param {V} value The value to add. 302 * @return {*} Some subclasses return a value. 303 */ 304 goog.structs.Map.prototype.set = function(key, value) { 305 if (!(goog.structs.Map.hasKey_(this.map_, key))) { 306 this.count_++; 307 this.keys_.push(key); 308 // Only change the version if we add a new key. 309 this.version_++; 310 } 311 this.map_[key] = value; 312 }; 313 314 315 /** 316 * Adds multiple key-value pairs from another goog.structs.Map or Object. 317 * @param {Object} map Object containing the data to add. 318 */ 319 goog.structs.Map.prototype.addAll = function(map) { 320 var keys, values; 321 if (map instanceof goog.structs.Map) { 322 keys = map.getKeys(); 323 values = map.getValues(); 324 } else { 325 keys = goog.object.getKeys(map); 326 values = goog.object.getValues(map); 327 } 328 // we could use goog.array.forEach here but I don't want to introduce that 329 // dependency just for this. 330 for (var i = 0; i < keys.length; i++) { 331 this.set(keys[i], values[i]); 332 } 333 }; 334 335 336 /** 337 * Calls the given function on each entry in the map. 338 * @param {function(this:T, V, K, goog.structs.Map.<K,V>)} f 339 * @param {T=} opt_obj The value of "this" inside f. 340 * @template T 341 */ 342 goog.structs.Map.prototype.forEach = function(f, opt_obj) { 343 var keys = this.getKeys(); 344 for (var i = 0; i < keys.length; i++) { 345 var key = keys[i]; 346 var value = this.get(key); 347 f.call(opt_obj, value, key, this); 348 } 349 }; 350 351 352 /** 353 * Clones a map and returns a new map. 354 * @return {!goog.structs.Map} A new map with the same key-value pairs. 355 */ 356 goog.structs.Map.prototype.clone = function() { 357 return new goog.structs.Map(this); 358 }; 359 360 361 /** 362 * Returns a new map in which all the keys and values are interchanged 363 * (keys become values and values become keys). If multiple keys map to the 364 * same value, the chosen transposed value is implementation-dependent. 365 * 366 * It acts very similarly to {goog.object.transpose(Object)}. 367 * 368 * @return {!goog.structs.Map} The transposed map. 369 */ 370 goog.structs.Map.prototype.transpose = function() { 371 var transposed = new goog.structs.Map(); 372 for (var i = 0; i < this.keys_.length; i++) { 373 var key = this.keys_[i]; 374 var value = this.map_[key]; 375 transposed.set(value, key); 376 } 377 378 return transposed; 379 }; 380 381 382 /** 383 * @return {!Object} Object representation of the map. 384 */ 385 goog.structs.Map.prototype.toObject = function() { 386 this.cleanupKeysArray_(); 387 var obj = {}; 388 for (var i = 0; i < this.keys_.length; i++) { 389 var key = this.keys_[i]; 390 obj[key] = this.map_[key]; 391 } 392 return obj; 393 }; 394 395 396 /** 397 * Returns an iterator that iterates over the keys in the map. Removal of keys 398 * while iterating might have undesired side effects. 399 * @return {!goog.iter.Iterator} An iterator over the keys in the map. 400 */ 401 goog.structs.Map.prototype.getKeyIterator = function() { 402 return this.__iterator__(true); 403 }; 404 405 406 /** 407 * Returns an iterator that iterates over the values in the map. Removal of 408 * keys while iterating might have undesired side effects. 409 * @return {!goog.iter.Iterator} An iterator over the values in the map. 410 */ 411 goog.structs.Map.prototype.getValueIterator = function() { 412 return this.__iterator__(false); 413 }; 414 415 416 /** 417 * Returns an iterator that iterates over the values or the keys in the map. 418 * This throws an exception if the map was mutated since the iterator was 419 * created. 420 * @param {boolean=} opt_keys True to iterate over the keys. False to iterate 421 * over the values. The default value is false. 422 * @return {!goog.iter.Iterator} An iterator over the values or keys in the map. 423 */ 424 goog.structs.Map.prototype.__iterator__ = function(opt_keys) { 425 // Clean up keys to minimize the risk of iterating over dead keys. 426 this.cleanupKeysArray_(); 427 428 var i = 0; 429 var keys = this.keys_; 430 var map = this.map_; 431 var version = this.version_; 432 var selfObj = this; 433 434 var newIter = new goog.iter.Iterator; 435 newIter.next = function() { 436 while (true) { 437 if (version != selfObj.version_) { 438 throw Error('The map has changed since the iterator was created'); 439 } 440 if (i >= keys.length) { 441 throw goog.iter.StopIteration; 442 } 443 var key = keys[i++]; 444 return opt_keys ? key : map[key]; 445 } 446 }; 447 return newIter; 448 }; 449 450 451 /** 452 * Safe way to test for hasOwnProperty. It even allows testing for 453 * 'hasOwnProperty'. 454 * @param {Object} obj The object to test for presence of the given key. 455 * @param {*} key The key to check for. 456 * @return {boolean} Whether the object has the key. 457 * @private 458 */ 459 goog.structs.Map.hasKey_ = function(obj, key) { 460 return Object.prototype.hasOwnProperty.call(obj, key); 461 }; lib/goog/structs/structs.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Generics method for collection-like classes and objects. 17 * 18 * @author arv@google.com (Erik Arvidsson) 19 * 20 * This file contains functions to work with collections. It supports using 21 * Map, Set, Array and Object and other classes that implement collection-like 22 * methods. 23 */ 24 25 26 goog.provide('goog.structs'); 27 28 goog.require('goog.array'); 29 goog.require('goog.object'); 30 31 32 // We treat an object as a dictionary if it has getKeys or it is an object that 33 // isn't arrayLike. 34 35 36 /** 37 * Returns the number of values in the collection-like object. 38 * @param {Object} col The collection-like object. 39 * @return {number} The number of values in the collection-like object. 40 */ 41 goog.structs.getCount = function(col) { 42 if (typeof col.getCount == 'function') { 43 return col.getCount(); 44 } 45 if (goog.isArrayLike(col) || goog.isString(col)) { 46 return col.length; 47 } 48 return goog.object.getCount(col); 49 }; 50 51 52 /** 53 * Returns the values of the collection-like object. 54 * @param {Object} col The collection-like object. 55 * @return {!Array} The values in the collection-like object. 56 */ 57 goog.structs.getValues = function(col) { 58 if (typeof col.getValues == 'function') { 59 return col.getValues(); 60 } 61 if (goog.isString(col)) { 62 return col.split(''); 63 } 64 if (goog.isArrayLike(col)) { 65 var rv = []; 66 var l = col.length; 67 for (var i = 0; i < l; i++) { 68 rv.push(col[i]); 69 } 70 return rv; 71 } 72 return goog.object.getValues(col); 73 }; 74 75 76 /** 77 * Returns the keys of the collection. Some collections have no notion of 78 * keys/indexes and this function will return undefined in those cases. 79 * @param {Object} col The collection-like object. 80 * @return {!Array|undefined} The keys in the collection. 81 */ 82 goog.structs.getKeys = function(col) { 83 if (typeof col.getKeys == 'function') { 84 return col.getKeys(); 85 } 86 // if we have getValues but no getKeys we know this is a key-less collection 87 if (typeof col.getValues == 'function') { 88 return undefined; 89 } 90 if (goog.isArrayLike(col) || goog.isString(col)) { 91 var rv = []; 92 var l = col.length; 93 for (var i = 0; i < l; i++) { 94 rv.push(i); 95 } 96 return rv; 97 } 98 99 return goog.object.getKeys(col); 100 }; 101 102 103 /** 104 * Whether the collection contains the given value. This is O(n) and uses 105 * equals (==) to test the existence. 106 * @param {Object} col The collection-like object. 107 * @param {*} val The value to check for. 108 * @return {boolean} True if the map contains the value. 109 */ 110 goog.structs.contains = function(col, val) { 111 if (typeof col.contains == 'function') { 112 return col.contains(val); 113 } 114 if (typeof col.containsValue == 'function') { 115 return col.containsValue(val); 116 } 117 if (goog.isArrayLike(col) || goog.isString(col)) { 118 return goog.array.contains(/** @type {Array} */ (col), val); 119 } 120 return goog.object.containsValue(col, val); 121 }; 122 123 124 /** 125 * Whether the collection is empty. 126 * @param {Object} col The collection-like object. 127 * @return {boolean} True if empty. 128 */ 129 goog.structs.isEmpty = function(col) { 130 if (typeof col.isEmpty == 'function') { 131 return col.isEmpty(); 132 } 133 134 // We do not use goog.string.isEmpty because here we treat the string as 135 // collection and as such even whitespace matters 136 137 if (goog.isArrayLike(col) || goog.isString(col)) { 138 return goog.array.isEmpty(/** @type {Array} */ (col)); 139 } 140 return goog.object.isEmpty(col); 141 }; 142 143 144 /** 145 * Removes all the elements from the collection. 146 * @param {Object} col The collection-like object. 147 */ 148 goog.structs.clear = function(col) { 149 // NOTE(arv): This should not contain strings because strings are immutable 150 if (typeof col.clear == 'function') { 151 col.clear(); 152 } else if (goog.isArrayLike(col)) { 153 goog.array.clear(/** @type {goog.array.ArrayLike} */ (col)); 154 } else { 155 goog.object.clear(col); 156 } 157 }; 158 159 160 /** 161 * Calls a function for each value in a collection. The function takes 162 * three arguments; the value, the key and the collection. 163 * 164 * NOTE: This will be deprecated soon! Please use a more specific method if 165 * possible, e.g. goog.array.forEach, goog.object.forEach, etc. 166 * 167 * @param {S} col The collection-like object. 168 * @param {function(this:T,?,?,S):?} f The function to call for every value. 169 * This function takes 170 * 3 arguments (the value, the key or undefined if the collection has no 171 * notion of keys, and the collection) and the return value is irrelevant. 172 * @param {T=} opt_obj The object to be used as the value of 'this' 173 * within {@code f}. 174 * @template T,S 175 */ 176 goog.structs.forEach = function(col, f, opt_obj) { 177 if (typeof col.forEach == 'function') { 178 col.forEach(f, opt_obj); 179 } else if (goog.isArrayLike(col) || goog.isString(col)) { 180 goog.array.forEach(/** @type {Array} */ (col), f, opt_obj); 181 } else { 182 var keys = goog.structs.getKeys(col); 183 var values = goog.structs.getValues(col); 184 var l = values.length; 185 for (var i = 0; i < l; i++) { 186 f.call(opt_obj, values[i], keys && keys[i], col); 187 } 188 } 189 }; 190 191 192 /** 193 * Calls a function for every value in the collection. When a call returns true, 194 * adds the value to a new collection (Array is returned by default). 195 * 196 * @param {S} col The collection-like object. 197 * @param {function(this:T,?,?,S):boolean} f The function to call for every 198 * value. This function takes 199 * 3 arguments (the value, the key or undefined if the collection has no 200 * notion of keys, and the collection) and should return a Boolean. If the 201 * return value is true the value is added to the result collection. If it 202 * is false the value is not included. 203 * @param {T=} opt_obj The object to be used as the value of 'this' 204 * within {@code f}. 205 * @return {!Object|!Array} A new collection where the passed values are 206 * present. If col is a key-less collection an array is returned. If col 207 * has keys and values a plain old JS object is returned. 208 * @template T,S 209 */ 210 goog.structs.filter = function(col, f, opt_obj) { 211 if (typeof col.filter == 'function') { 212 return col.filter(f, opt_obj); 213 } 214 if (goog.isArrayLike(col) || goog.isString(col)) { 215 return goog.array.filter(/** @type {!Array} */ (col), f, opt_obj); 216 } 217 218 var rv; 219 var keys = goog.structs.getKeys(col); 220 var values = goog.structs.getValues(col); 221 var l = values.length; 222 if (keys) { 223 rv = {}; 224 for (var i = 0; i < l; i++) { 225 if (f.call(opt_obj, values[i], keys[i], col)) { 226 rv[keys[i]] = values[i]; 227 } 228 } 229 } else { 230 // We should not use goog.array.filter here since we want to make sure that 231 // the index is undefined as well as make sure that col is passed to the 232 // function. 233 rv = []; 234 for (var i = 0; i < l; i++) { 235 if (f.call(opt_obj, values[i], undefined, col)) { 236 rv.push(values[i]); 237 } 238 } 239 } 240 return rv; 241 }; 242 243 244 /** 245 * Calls a function for every value in the collection and adds the result into a 246 * new collection (defaults to creating a new Array). 247 * 248 * @param {S} col The collection-like object. 249 * @param {function(this:T,?,?,S):V} f The function to call for every value. 250 * This function takes 3 arguments (the value, the key or undefined if the 251 * collection has no notion of keys, and the collection) and should return 252 * something. The result will be used as the value in the new collection. 253 * @param {T=} opt_obj The object to be used as the value of 'this' 254 * within {@code f}. 255 * @return {!Object.<V>|!Array.<V>} A new collection with the new values. If 256 * col is a key-less collection an array is returned. If col has keys and 257 * values a plain old JS object is returned. 258 * @template T,S,V 259 */ 260 goog.structs.map = function(col, f, opt_obj) { 261 if (typeof col.map == 'function') { 262 return col.map(f, opt_obj); 263 } 264 if (goog.isArrayLike(col) || goog.isString(col)) { 265 return goog.array.map(/** @type {!Array} */ (col), f, opt_obj); 266 } 267 268 var rv; 269 var keys = goog.structs.getKeys(col); 270 var values = goog.structs.getValues(col); 271 var l = values.length; 272 if (keys) { 273 rv = {}; 274 for (var i = 0; i < l; i++) { 275 rv[keys[i]] = f.call(opt_obj, values[i], keys[i], col); 276 } 277 } else { 278 // We should not use goog.array.map here since we want to make sure that 279 // the index is undefined as well as make sure that col is passed to the 280 // function. 281 rv = []; 282 for (var i = 0; i < l; i++) { 283 rv[i] = f.call(opt_obj, values[i], undefined, col); 284 } 285 } 286 return rv; 287 }; 288 289 290 /** 291 * Calls f for each value in a collection. If any call returns true this returns 292 * true (without checking the rest). If all returns false this returns false. 293 * 294 * @param {S} col The collection-like object. 295 * @param {function(this:T,?,?,S):boolean} f The function to call for every 296 * value. This function takes 3 arguments (the value, the key or undefined 297 * if the collection has no notion of keys, and the collection) and should 298 * return a boolean. 299 * @param {T=} opt_obj The object to be used as the value of 'this' 300 * within {@code f}. 301 * @return {boolean} True if any value passes the test. 302 * @template T,S 303 */ 304 goog.structs.some = function(col, f, opt_obj) { 305 if (typeof col.some == 'function') { 306 return col.some(f, opt_obj); 307 } 308 if (goog.isArrayLike(col) || goog.isString(col)) { 309 return goog.array.some(/** @type {!Array} */ (col), f, opt_obj); 310 } 311 var keys = goog.structs.getKeys(col); 312 var values = goog.structs.getValues(col); 313 var l = values.length; 314 for (var i = 0; i < l; i++) { 315 if (f.call(opt_obj, values[i], keys && keys[i], col)) { 316 return true; 317 } 318 } 319 return false; 320 }; 321 322 323 /** 324 * Calls f for each value in a collection. If all calls return true this return 325 * true this returns true. If any returns false this returns false at this point 326 * and does not continue to check the remaining values. 327 * 328 * @param {S} col The collection-like object. 329 * @param {function(this:T,?,?,S):boolean} f The function to call for every 330 * value. This function takes 3 arguments (the value, the key or 331 * undefined if the collection has no notion of keys, and the collection) 332 * and should return a boolean. 333 * @param {T=} opt_obj The object to be used as the value of 'this' 334 * within {@code f}. 335 * @return {boolean} True if all key-value pairs pass the test. 336 * @template T,S 337 */ 338 goog.structs.every = function(col, f, opt_obj) { 339 if (typeof col.every == 'function') { 340 return col.every(f, opt_obj); 341 } 342 if (goog.isArrayLike(col) || goog.isString(col)) { 343 return goog.array.every(/** @type {!Array} */ (col), f, opt_obj); 344 } 345 var keys = goog.structs.getKeys(col); 346 var values = goog.structs.getValues(col); 347 var l = values.length; 348 for (var i = 0; i < l; i++) { 349 if (!f.call(opt_obj, values[i], keys && keys[i], col)) { 350 return false; 351 } 352 } 353 return true; 354 }; lib/goog/uri/uri.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Class for parsing and formatting URIs. 17 * 18 * Use goog.Uri(string) to parse a URI string. Use goog.Uri.create(...) to 19 * create a new instance of the goog.Uri object from Uri parts. 20 * 21 * e.g: <code>var myUri = new goog.Uri(window.location);</code> 22 * 23 * Implements RFC 3986 for parsing/formatting URIs. 24 * http://www.ietf.org/rfc/rfc3986.txt 25 * 26 * Some changes have been made to the interface (more like .NETs), though the 27 * internal representation is now of un-encoded parts, this will change the 28 * behavior slightly. 29 * 30 */ 31 32 goog.provide('goog.Uri'); 33 goog.provide('goog.Uri.QueryData'); 34 35 goog.require('goog.array'); 36 goog.require('goog.string'); 37 goog.require('goog.structs'); 38 goog.require('goog.structs.Map'); 39 goog.require('goog.uri.utils'); 40 goog.require('goog.uri.utils.ComponentIndex'); 41 goog.require('goog.uri.utils.StandardQueryParam'); 42 43 44 45 /** 46 * This class contains setters and getters for the parts of the URI. 47 * The <code>getXyz</code>/<code>setXyz</code> methods return the decoded part 48 * -- so<code>goog.Uri.parse('/foo%20bar').getPath()</code> will return the 49 * decoded path, <code>/foo bar</code>. 50 * 51 * The constructor accepts an optional unparsed, raw URI string. The parser 52 * is relaxed, so special characters that aren't escaped but don't cause 53 * ambiguities will not cause parse failures. 54 * 55 * All setters return <code>this</code> and so may be chained, a la 56 * <code>goog.Uri.parse('/foo').setFragment('part').toString()</code>. 57 * 58 * @param {*=} opt_uri Optional string URI to parse 59 * (use goog.Uri.create() to create a URI from parts), or if 60 * a goog.Uri is passed, a clone is created. 61 * @param {boolean=} opt_ignoreCase If true, #getParameterValue will ignore 62 * the case of the parameter name. 63 * 64 * @constructor 65 */ 66 goog.Uri = function(opt_uri, opt_ignoreCase) { 67 // Parse in the uri string 68 var m; 69 if (opt_uri instanceof goog.Uri) { 70 this.ignoreCase_ = goog.isDef(opt_ignoreCase) ? 71 opt_ignoreCase : opt_uri.getIgnoreCase(); 72 this.setScheme(opt_uri.getScheme()); 73 this.setUserInfo(opt_uri.getUserInfo()); 74 this.setDomain(opt_uri.getDomain()); 75 this.setPort(opt_uri.getPort()); 76 this.setPath(opt_uri.getPath()); 77 this.setQueryData(opt_uri.getQueryData().clone()); 78 this.setFragment(opt_uri.getFragment()); 79 } else if (opt_uri && (m = goog.uri.utils.split(String(opt_uri)))) { 80 this.ignoreCase_ = !!opt_ignoreCase; 81 82 // Set the parts -- decoding as we do so. 83 // COMPATABILITY NOTE - In IE, unmatched fields may be empty strings, 84 // whereas in other browsers they will be undefined. 85 this.setScheme(m[goog.uri.utils.ComponentIndex.SCHEME] || '', true); 86 this.setUserInfo(m[goog.uri.utils.ComponentIndex.USER_INFO] || '', true); 87 this.setDomain(m[goog.uri.utils.ComponentIndex.DOMAIN] || '', true); 88 this.setPort(m[goog.uri.utils.ComponentIndex.PORT]); 89 this.setPath(m[goog.uri.utils.ComponentIndex.PATH] || '', true); 90 this.setQueryData(m[goog.uri.utils.ComponentIndex.QUERY_DATA] || '', true); 91 this.setFragment(m[goog.uri.utils.ComponentIndex.FRAGMENT] || '', true); 92 93 } else { 94 this.ignoreCase_ = !!opt_ignoreCase; 95 this.queryData_ = new goog.Uri.QueryData(null, null, this.ignoreCase_); 96 } 97 }; 98 99 100 /** 101 * If true, we preserve the type of query parameters set programmatically. 102 * 103 * This means that if you set a parameter to a boolean, and then call 104 * getParameterValue, you will get a boolean back. 105 * 106 * If false, we will coerce parameters to strings, just as they would 107 * appear in real URIs. 108 * 109 * TODO(nicksantos): Remove this once people have time to fix all tests. 110 * 111 * @type {boolean} 112 */ 113 goog.Uri.preserveParameterTypesCompatibilityFlag = false; 114 115 116 /** 117 * Parameter name added to stop caching. 118 * @type {string} 119 */ 120 goog.Uri.RANDOM_PARAM = goog.uri.utils.StandardQueryParam.RANDOM; 121 122 123 /** 124 * Scheme such as "http". 125 * @type {string} 126 * @private 127 */ 128 goog.Uri.prototype.scheme_ = ''; 129 130 131 /** 132 * User credentials in the form "username:password". 133 * @type {string} 134 * @private 135 */ 136 goog.Uri.prototype.userInfo_ = ''; 137 138 139 /** 140 * Domain part, e.g. "www.google.com". 141 * @type {string} 142 * @private 143 */ 144 goog.Uri.prototype.domain_ = ''; 145 146 147 /** 148 * Port, e.g. 8080. 149 * @type {?number} 150 * @private 151 */ 152 goog.Uri.prototype.port_ = null; 153 154 155 /** 156 * Path, e.g. "/tests/img.png". 157 * @type {string} 158 * @private 159 */ 160 goog.Uri.prototype.path_ = ''; 161 162 163 /** 164 * Object representing query data. 165 * @type {!goog.Uri.QueryData} 166 * @private 167 */ 168 goog.Uri.prototype.queryData_; 169 170 171 /** 172 * The fragment without the #. 173 * @type {string} 174 * @private 175 */ 176 goog.Uri.prototype.fragment_ = ''; 177 178 179 /** 180 * Whether or not this Uri should be treated as Read Only. 181 * @type {boolean} 182 * @private 183 */ 184 goog.Uri.prototype.isReadOnly_ = false; 185 186 187 /** 188 * Whether or not to ignore case when comparing query params. 189 * @type {boolean} 190 * @private 191 */ 192 goog.Uri.prototype.ignoreCase_ = false; 193 194 195 /** 196 * @return {string} The string form of the url. 197 * @override 198 */ 199 goog.Uri.prototype.toString = function() { 200 var out = []; 201 202 var scheme = this.getScheme(); 203 if (scheme) { 204 out.push(goog.Uri.encodeSpecialChars_( 205 scheme, goog.Uri.reDisallowedInSchemeOrUserInfo_), ':'); 206 } 207 208 var domain = this.getDomain(); 209 if (domain) { 210 out.push('//'); 211 212 var userInfo = this.getUserInfo(); 213 if (userInfo) { 214 out.push(goog.Uri.encodeSpecialChars_( 215 userInfo, goog.Uri.reDisallowedInSchemeOrUserInfo_), '@'); 216 } 217 218 out.push(goog.string.urlEncode(domain)); 219 220 var port = this.getPort(); 221 if (port != null) { 222 out.push(':', String(port)); 223 } 224 } 225 226 var path = this.getPath(); 227 if (path) { 228 if (this.hasDomain() && path.charAt(0) != '/') { 229 out.push('/'); 230 } 231 out.push(goog.Uri.encodeSpecialChars_( 232 path, 233 path.charAt(0) == '/' ? 234 goog.Uri.reDisallowedInAbsolutePath_ : 235 goog.Uri.reDisallowedInRelativePath_)); 236 } 237 238 var query = this.getEncodedQuery(); 239 if (query) { 240 out.push('?', query); 241 } 242 243 var fragment = this.getFragment(); 244 if (fragment) { 245 out.push('#', goog.Uri.encodeSpecialChars_( 246 fragment, goog.Uri.reDisallowedInFragment_)); 247 } 248 return out.join(''); 249 }; 250 251 252 /** 253 * Resolves the given relative URI (a goog.Uri object), using the URI 254 * represented by this instance as the base URI. 255 * 256 * There are several kinds of relative URIs:<br> 257 * 1. foo - replaces the last part of the path, the whole query and fragment<br> 258 * 2. /foo - replaces the the path, the query and fragment<br> 259 * 3. //foo - replaces everything from the domain on. foo is a domain name<br> 260 * 4. ?foo - replace the query and fragment<br> 261 * 5. #foo - replace the fragment only 262 * 263 * Additionally, if relative URI has a non-empty path, all ".." and "." 264 * segments will be resolved, as described in RFC 3986. 265 * 266 * @param {goog.Uri} relativeUri The relative URI to resolve. 267 * @return {!goog.Uri} The resolved URI. 268 */ 269 goog.Uri.prototype.resolve = function(relativeUri) { 270 271 var absoluteUri = this.clone(); 272 273 // we satisfy these conditions by looking for the first part of relativeUri 274 // that is not blank and applying defaults to the rest 275 276 var overridden = relativeUri.hasScheme(); 277 278 if (overridden) { 279 absoluteUri.setScheme(relativeUri.getScheme()); 280 } else { 281 overridden = relativeUri.hasUserInfo(); 282 } 283 284 if (overridden) { 285 absoluteUri.setUserInfo(relativeUri.getUserInfo()); 286 } else { 287 overridden = relativeUri.hasDomain(); 288 } 289 290 if (overridden) { 291 absoluteUri.setDomain(relativeUri.getDomain()); 292 } else { 293 overridden = relativeUri.hasPort(); 294 } 295 296 var path = relativeUri.getPath(); 297 if (overridden) { 298 absoluteUri.setPort(relativeUri.getPort()); 299 } else { 300 overridden = relativeUri.hasPath(); 301 if (overridden) { 302 // resolve path properly 303 if (path.charAt(0) != '/') { 304 // path is relative 305 if (this.hasDomain() && !this.hasPath()) { 306 // RFC 3986, section 5.2.3, case 1 307 path = '/' + path; 308 } else { 309 // RFC 3986, section 5.2.3, case 2 310 var lastSlashIndex = absoluteUri.getPath().lastIndexOf('/'); 311 if (lastSlashIndex != -1) { 312 path = absoluteUri.getPath().substr(0, lastSlashIndex + 1) + path; 313 } 314 } 315 } 316 path = goog.Uri.removeDotSegments(path); 317 } 318 } 319 320 if (overridden) { 321 absoluteUri.setPath(path); 322 } else { 323 overridden = relativeUri.hasQuery(); 324 } 325 326 if (overridden) { 327 absoluteUri.setQueryData(relativeUri.getDecodedQuery()); 328 } else { 329 overridden = relativeUri.hasFragment(); 330 } 331 332 if (overridden) { 333 absoluteUri.setFragment(relativeUri.getFragment()); 334 } 335 336 return absoluteUri; 337 }; 338 339 340 /** 341 * Clones the URI instance. 342 * @return {!goog.Uri} New instance of the URI object. 343 */ 344 goog.Uri.prototype.clone = function() { 345 return new goog.Uri(this); 346 }; 347 348 349 /** 350 * @return {string} The encoded scheme/protocol for the URI. 351 */ 352 goog.Uri.prototype.getScheme = function() { 353 return this.scheme_; 354 }; 355 356 357 /** 358 * Sets the scheme/protocol. 359 * @param {string} newScheme New scheme value. 360 * @param {boolean=} opt_decode Optional param for whether to decode new value. 361 * @return {!goog.Uri} Reference to this URI object. 362 */ 363 goog.Uri.prototype.setScheme = function(newScheme, opt_decode) { 364 this.enforceReadOnly(); 365 this.scheme_ = opt_decode ? goog.Uri.decodeOrEmpty_(newScheme) : newScheme; 366 367 // remove an : at the end of the scheme so somebody can pass in 368 // window.location.protocol 369 if (this.scheme_) { 370 this.scheme_ = this.scheme_.replace(/:$/, ''); 371 } 372 return this; 373 }; 374 375 376 /** 377 * @return {boolean} Whether the scheme has been set. 378 */ 379 goog.Uri.prototype.hasScheme = function() { 380 return !!this.scheme_; 381 }; 382 383 384 /** 385 * @return {string} The decoded user info. 386 */ 387 goog.Uri.prototype.getUserInfo = function() { 388 return this.userInfo_; 389 }; 390 391 392 /** 393 * Sets the userInfo. 394 * @param {string} newUserInfo New userInfo value. 395 * @param {boolean=} opt_decode Optional param for whether to decode new value. 396 * @return {!goog.Uri} Reference to this URI object. 397 */ 398 goog.Uri.prototype.setUserInfo = function(newUserInfo, opt_decode) { 399 this.enforceReadOnly(); 400 this.userInfo_ = opt_decode ? goog.Uri.decodeOrEmpty_(newUserInfo) : 401 newUserInfo; 402 return this; 403 }; 404 405 406 /** 407 * @return {boolean} Whether the user info has been set. 408 */ 409 goog.Uri.prototype.hasUserInfo = function() { 410 return !!this.userInfo_; 411 }; 412 413 414 /** 415 * @return {string} The decoded domain. 416 */ 417 goog.Uri.prototype.getDomain = function() { 418 return this.domain_; 419 }; 420 421 422 /** 423 * Sets the domain. 424 * @param {string} newDomain New domain value. 425 * @param {boolean=} opt_decode Optional param for whether to decode new value. 426 * @return {!goog.Uri} Reference to this URI object. 427 */ 428 goog.Uri.prototype.setDomain = function(newDomain, opt_decode) { 429 this.enforceReadOnly(); 430 this.domain_ = opt_decode ? goog.Uri.decodeOrEmpty_(newDomain) : newDomain; 431 return this; 432 }; 433 434 435 /** 436 * @return {boolean} Whether the domain has been set. 437 */ 438 goog.Uri.prototype.hasDomain = function() { 439 return !!this.domain_; 440 }; 441 442 443 /** 444 * @return {?number} The port number. 445 */ 446 goog.Uri.prototype.getPort = function() { 447 return this.port_; 448 }; 449 450 451 /** 452 * Sets the port number. 453 * @param {*} newPort Port number. Will be explicitly casted to a number. 454 * @return {!goog.Uri} Reference to this URI object. 455 */ 456 goog.Uri.prototype.setPort = function(newPort) { 457 this.enforceReadOnly(); 458 459 if (newPort) { 460 newPort = Number(newPort); 461 if (isNaN(newPort) || newPort < 0) { 462 throw Error('Bad port number ' + newPort); 463 } 464 this.port_ = newPort; 465 } else { 466 this.port_ = null; 467 } 468 469 return this; 470 }; 471 472 473 /** 474 * @return {boolean} Whether the port has been set. 475 */ 476 goog.Uri.prototype.hasPort = function() { 477 return this.port_ != null; 478 }; 479 480 481 /** 482 * @return {string} The decoded path. 483 */ 484 goog.Uri.prototype.getPath = function() { 485 return this.path_; 486 }; 487 488 489 /** 490 * Sets the path. 491 * @param {string} newPath New path value. 492 * @param {boolean=} opt_decode Optional param for whether to decode new value. 493 * @return {!goog.Uri} Reference to this URI object. 494 */ 495 goog.Uri.prototype.setPath = function(newPath, opt_decode) { 496 this.enforceReadOnly(); 497 this.path_ = opt_decode ? goog.Uri.decodeOrEmpty_(newPath) : newPath; 498 return this; 499 }; 500 501 502 /** 503 * @return {boolean} Whether the path has been set. 504 */ 505 goog.Uri.prototype.hasPath = function() { 506 return !!this.path_; 507 }; 508 509 510 /** 511 * @return {boolean} Whether the query string has been set. 512 */ 513 goog.Uri.prototype.hasQuery = function() { 514 return this.queryData_.toString() !== ''; 515 }; 516 517 518 /** 519 * Sets the query data. 520 * @param {goog.Uri.QueryData|string|undefined} queryData QueryData object. 521 * @param {boolean=} opt_decode Optional param for whether to decode new value. 522 * Applies only if queryData is a string. 523 * @return {!goog.Uri} Reference to this URI object. 524 */ 525 goog.Uri.prototype.setQueryData = function(queryData, opt_decode) { 526 this.enforceReadOnly(); 527 528 if (queryData instanceof goog.Uri.QueryData) { 529 this.queryData_ = queryData; 530 this.queryData_.setIgnoreCase(this.ignoreCase_); 531 } else { 532 if (!opt_decode) { 533 // QueryData accepts encoded query string, so encode it if 534 // opt_decode flag is not true. 535 queryData = goog.Uri.encodeSpecialChars_(queryData, 536 goog.Uri.reDisallowedInQuery_); 537 } 538 this.queryData_ = new goog.Uri.QueryData(queryData, null, this.ignoreCase_); 539 } 540 541 return this; 542 }; 543 544 545 /** 546 * Sets the URI query. 547 * @param {string} newQuery New query value. 548 * @param {boolean=} opt_decode Optional param for whether to decode new value. 549 * @return {!goog.Uri} Reference to this URI object. 550 */ 551 goog.Uri.prototype.setQuery = function(newQuery, opt_decode) { 552 return this.setQueryData(newQuery, opt_decode); 553 }; 554 555 556 /** 557 * @return {string} The encoded URI query, not including the ?. 558 */ 559 goog.Uri.prototype.getEncodedQuery = function() { 560 return this.queryData_.toString(); 561 }; 562 563 564 /** 565 * @return {string} The decoded URI query, not including the ?. 566 */ 567 goog.Uri.prototype.getDecodedQuery = function() { 568 return this.queryData_.toDecodedString(); 569 }; 570 571 572 /** 573 * Returns the query data. 574 * @return {!goog.Uri.QueryData} QueryData object. 575 */ 576 goog.Uri.prototype.getQueryData = function() { 577 return this.queryData_; 578 }; 579 580 581 /** 582 * @return {string} The encoded URI query, not including the ?. 583 * 584 * Warning: This method, unlike other getter methods, returns encoded 585 * value, instead of decoded one. 586 */ 587 goog.Uri.prototype.getQuery = function() { 588 return this.getEncodedQuery(); 589 }; 590 591 592 /** 593 * Sets the value of the named query parameters, clearing previous values for 594 * that key. 595 * 596 * @param {string} key The parameter to set. 597 * @param {*} value The new value. 598 * @return {!goog.Uri} Reference to this URI object. 599 */ 600 goog.Uri.prototype.setParameterValue = function(key, value) { 601 this.enforceReadOnly(); 602 this.queryData_.set(key, value); 603 return this; 604 }; 605 606 607 /** 608 * Sets the values of the named query parameters, clearing previous values for 609 * that key. Not new values will currently be moved to the end of the query 610 * string. 611 * 612 * So, <code>goog.Uri.parse('foo?a=b&c=d&e=f').setParameterValues('c', ['new']) 613 * </code> yields <tt>foo?a=b&e=f&c=new</tt>.</p> 614 * 615 * @param {string} key The parameter to set. 616 * @param {*} values The new values. If values is a single 617 * string then it will be treated as the sole value. 618 * @return {!goog.Uri} Reference to this URI object. 619 */ 620 goog.Uri.prototype.setParameterValues = function(key, values) { 621 this.enforceReadOnly(); 622 623 if (!goog.isArray(values)) { 624 values = [String(values)]; 625 } 626 627 // TODO(nicksantos): This cast shouldn't be necessary. 628 this.queryData_.setValues(key, /** @type {Array} */ (values)); 629 630 return this; 631 }; 632 633 634 /** 635 * Returns the value<b>s</b> for a given cgi parameter as a list of decoded 636 * query parameter values. 637 * @param {string} name The parameter to get values for. 638 * @return {!Array} The values for a given cgi parameter as a list of 639 * decoded query parameter values. 640 */ 641 goog.Uri.prototype.getParameterValues = function(name) { 642 return this.queryData_.getValues(name); 643 }; 644 645 646 /** 647 * Returns the first value for a given cgi parameter or undefined if the given 648 * parameter name does not appear in the query string. 649 * @param {string} paramName Unescaped parameter name. 650 * @return {string|undefined} The first value for a given cgi parameter or 651 * undefined if the given parameter name does not appear in the query 652 * string. 653 */ 654 goog.Uri.prototype.getParameterValue = function(paramName) { 655 // NOTE(nicksantos): This type-cast is a lie when 656 // preserveParameterTypesCompatibilityFlag is set to true. 657 // But this should only be set to true in tests. 658 return /** @type {string|undefined} */ (this.queryData_.get(paramName)); 659 }; 660 661 662 /** 663 * @return {string} The URI fragment, not including the #. 664 */ 665 goog.Uri.prototype.getFragment = function() { 666 return this.fragment_; 667 }; 668 669 670 /** 671 * Sets the URI fragment. 672 * @param {string} newFragment New fragment value. 673 * @param {boolean=} opt_decode Optional param for whether to decode new value. 674 * @return {!goog.Uri} Reference to this URI object. 675 */ 676 goog.Uri.prototype.setFragment = function(newFragment, opt_decode) { 677 this.enforceReadOnly(); 678 this.fragment_ = opt_decode ? goog.Uri.decodeOrEmpty_(newFragment) : 679 newFragment; 680 return this; 681 }; 682 683 684 /** 685 * @return {boolean} Whether the URI has a fragment set. 686 */ 687 goog.Uri.prototype.hasFragment = function() { 688 return !!this.fragment_; 689 }; 690 691 692 /** 693 * Returns true if this has the same domain as that of uri2. 694 * @param {goog.Uri} uri2 The URI object to compare to. 695 * @return {boolean} true if same domain; false otherwise. 696 */ 697 goog.Uri.prototype.hasSameDomainAs = function(uri2) { 698 return ((!this.hasDomain() && !uri2.hasDomain()) || 699 this.getDomain() == uri2.getDomain()) && 700 ((!this.hasPort() && !uri2.hasPort()) || 701 this.getPort() == uri2.getPort()); 702 }; 703 704 705 /** 706 * Adds a random parameter to the Uri. 707 * @return {!goog.Uri} Reference to this Uri object. 708 */ 709 goog.Uri.prototype.makeUnique = function() { 710 this.enforceReadOnly(); 711 this.setParameterValue(goog.Uri.RANDOM_PARAM, goog.string.getRandomString()); 712 713 return this; 714 }; 715 716 717 /** 718 * Removes the named query parameter. 719 * 720 * @param {string} key The parameter to remove. 721 * @return {!goog.Uri} Reference to this URI object. 722 */ 723 goog.Uri.prototype.removeParameter = function(key) { 724 this.enforceReadOnly(); 725 this.queryData_.remove(key); 726 return this; 727 }; 728 729 730 /** 731 * Sets whether Uri is read only. If this goog.Uri is read-only, 732 * enforceReadOnly_ will be called at the start of any function that may modify 733 * this Uri. 734 * @param {boolean} isReadOnly whether this goog.Uri should be read only. 735 * @return {!goog.Uri} Reference to this Uri object. 736 */ 737 goog.Uri.prototype.setReadOnly = function(isReadOnly) { 738 this.isReadOnly_ = isReadOnly; 739 return this; 740 }; 741 742 743 /** 744 * @return {boolean} Whether the URI is read only. 745 */ 746 goog.Uri.prototype.isReadOnly = function() { 747 return this.isReadOnly_; 748 }; 749 750 751 /** 752 * Checks if this Uri has been marked as read only, and if so, throws an error. 753 * This should be called whenever any modifying function is called. 754 */ 755 goog.Uri.prototype.enforceReadOnly = function() { 756 if (this.isReadOnly_) { 757 throw Error('Tried to modify a read-only Uri'); 758 } 759 }; 760 761 762 /** 763 * Sets whether to ignore case. 764 * NOTE: If there are already key/value pairs in the QueryData, and 765 * ignoreCase_ is set to false, the keys will all be lower-cased. 766 * @param {boolean} ignoreCase whether this goog.Uri should ignore case. 767 * @return {!goog.Uri} Reference to this Uri object. 768 */ 769 goog.Uri.prototype.setIgnoreCase = function(ignoreCase) { 770 this.ignoreCase_ = ignoreCase; 771 if (this.queryData_) { 772 this.queryData_.setIgnoreCase(ignoreCase); 773 } 774 return this; 775 }; 776 777 778 /** 779 * @return {boolean} Whether to ignore case. 780 */ 781 goog.Uri.prototype.getIgnoreCase = function() { 782 return this.ignoreCase_; 783 }; 784 785 786 //============================================================================== 787 // Static members 788 //============================================================================== 789 790 791 /** 792 * Creates a uri from the string form. Basically an alias of new goog.Uri(). 793 * If a Uri object is passed to parse then it will return a clone of the object. 794 * 795 * @param {*} uri Raw URI string or instance of Uri 796 * object. 797 * @param {boolean=} opt_ignoreCase Whether to ignore the case of parameter 798 * names in #getParameterValue. 799 * @return {!goog.Uri} The new URI object. 800 */ 801 goog.Uri.parse = function(uri, opt_ignoreCase) { 802 return uri instanceof goog.Uri ? 803 uri.clone() : new goog.Uri(uri, opt_ignoreCase); 804 }; 805 806 807 /** 808 * Creates a new goog.Uri object from unencoded parts. 809 * 810 * @param {?string=} opt_scheme Scheme/protocol or full URI to parse. 811 * @param {?string=} opt_userInfo username:password. 812 * @param {?string=} opt_domain www.google.com. 813 * @param {?number=} opt_port 9830. 814 * @param {?string=} opt_path /some/path/to/a/file.html. 815 * @param {string|goog.Uri.QueryData=} opt_query a=1&b=2. 816 * @param {?string=} opt_fragment The fragment without the #. 817 * @param {boolean=} opt_ignoreCase Whether to ignore parameter name case in 818 * #getParameterValue. 819 * 820 * @return {!goog.Uri} The new URI object. 821 */ 822 goog.Uri.create = function(opt_scheme, opt_userInfo, opt_domain, opt_port, 823 opt_path, opt_query, opt_fragment, opt_ignoreCase) { 824 825 var uri = new goog.Uri(null, opt_ignoreCase); 826 827 // Only set the parts if they are defined and not empty strings. 828 opt_scheme && uri.setScheme(opt_scheme); 829 opt_userInfo && uri.setUserInfo(opt_userInfo); 830 opt_domain && uri.setDomain(opt_domain); 831 opt_port && uri.setPort(opt_port); 832 opt_path && uri.setPath(opt_path); 833 opt_query && uri.setQueryData(opt_query); 834 opt_fragment && uri.setFragment(opt_fragment); 835 836 return uri; 837 }; 838 839 840 /** 841 * Resolves a relative Uri against a base Uri, accepting both strings and 842 * Uri objects. 843 * 844 * @param {*} base Base Uri. 845 * @param {*} rel Relative Uri. 846 * @return {!goog.Uri} Resolved uri. 847 */ 848 goog.Uri.resolve = function(base, rel) { 849 if (!(base instanceof goog.Uri)) { 850 base = goog.Uri.parse(base); 851 } 852 853 if (!(rel instanceof goog.Uri)) { 854 rel = goog.Uri.parse(rel); 855 } 856 857 return base.resolve(rel); 858 }; 859 860 861 /** 862 * Removes dot segments in given path component, as described in 863 * RFC 3986, section 5.2.4. 864 * 865 * @param {string} path A non-empty path component. 866 * @return {string} Path component with removed dot segments. 867 */ 868 goog.Uri.removeDotSegments = function(path) { 869 if (path == '..' || path == '.') { 870 return ''; 871 872 } else if (!goog.string.contains(path, './') && 873 !goog.string.contains(path, '/.')) { 874 // This optimization detects uris which do not contain dot-segments, 875 // and as a consequence do not require any processing. 876 return path; 877 878 } else { 879 var leadingSlash = goog.string.startsWith(path, '/'); 880 var segments = path.split('/'); 881 var out = []; 882 883 for (var pos = 0; pos < segments.length; ) { 884 var segment = segments[pos++]; 885 886 if (segment == '.') { 887 if (leadingSlash && pos == segments.length) { 888 out.push(''); 889 } 890 } else if (segment == '..') { 891 if (out.length > 1 || out.length == 1 && out[0] != '') { 892 out.pop(); 893 } 894 if (leadingSlash && pos == segments.length) { 895 out.push(''); 896 } 897 } else { 898 out.push(segment); 899 leadingSlash = true; 900 } 901 } 902 903 return out.join('/'); 904 } 905 }; 906 907 908 /** 909 * Decodes a value or returns the empty string if it isn't defined or empty. 910 * @param {string|undefined} val Value to decode. 911 * @return {string} Decoded value. 912 * @private 913 */ 914 goog.Uri.decodeOrEmpty_ = function(val) { 915 // Don't use UrlDecode() here because val is not a query parameter. 916 return val ? decodeURIComponent(val) : ''; 917 }; 918 919 920 /** 921 * If unescapedPart is non null, then escapes any characters in it that aren't 922 * valid characters in a url and also escapes any special characters that 923 * appear in extra. 924 * 925 * @param {*} unescapedPart The string to encode. 926 * @param {RegExp} extra A character set of characters in [\01-\177]. 927 * @return {?string} null iff unescapedPart == null. 928 * @private 929 */ 930 goog.Uri.encodeSpecialChars_ = function(unescapedPart, extra) { 931 if (goog.isString(unescapedPart)) { 932 return encodeURI(unescapedPart).replace(extra, goog.Uri.encodeChar_); 933 } 934 return null; 935 }; 936 937 938 /** 939 * Converts a character in [\01-\177] to its unicode character equivalent. 940 * @param {string} ch One character string. 941 * @return {string} Encoded string. 942 * @private 943 */ 944 goog.Uri.encodeChar_ = function(ch) { 945 var n = ch.charCodeAt(0); 946 return '%' + ((n >> 4) & 0xf).toString(16) + (n & 0xf).toString(16); 947 }; 948 949 950 /** 951 * Regular expression for characters that are disallowed in the scheme or 952 * userInfo part of the URI. 953 * @type {RegExp} 954 * @private 955 */ 956 goog.Uri.reDisallowedInSchemeOrUserInfo_ = /[#\/\?@]/g; 957 958 959 /** 960 * Regular expression for characters that are disallowed in a relative path. 961 * @type {RegExp} 962 * @private 963 */ 964 goog.Uri.reDisallowedInRelativePath_ = /[\#\?:]/g; 965 966 967 /** 968 * Regular expression for characters that are disallowed in an absolute path. 969 * @type {RegExp} 970 * @private 971 */ 972 goog.Uri.reDisallowedInAbsolutePath_ = /[\#\?]/g; 973 974 975 /** 976 * Regular expression for characters that are disallowed in the query. 977 * @type {RegExp} 978 * @private 979 */ 980 goog.Uri.reDisallowedInQuery_ = /[\#\?@]/g; 981 982 983 /** 984 * Regular expression for characters that are disallowed in the fragment. 985 * @type {RegExp} 986 * @private 987 */ 988 goog.Uri.reDisallowedInFragment_ = /#/g; 989 990 991 /** 992 * Checks whether two URIs have the same domain. 993 * @param {string} uri1String First URI string. 994 * @param {string} uri2String Second URI string. 995 * @return {boolean} true if the two URIs have the same domain; false otherwise. 996 */ 997 goog.Uri.haveSameDomain = function(uri1String, uri2String) { 998 // Differs from goog.uri.utils.haveSameDomain, since this ignores scheme. 999 // TODO(gboyer): Have this just call goog.uri.util.haveSameDomain. 1000 var pieces1 = goog.uri.utils.split(uri1String); 1001 var pieces2 = goog.uri.utils.split(uri2String); 1002 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] == 1003 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] && 1004 pieces1[goog.uri.utils.ComponentIndex.PORT] == 1005 pieces2[goog.uri.utils.ComponentIndex.PORT]; 1006 }; 1007 1008 1009 1010 /** 1011 * Class used to represent URI query parameters. It is essentially a hash of 1012 * name-value pairs, though a name can be present more than once. 1013 * 1014 * Has the same interface as the collections in goog.structs. 1015 * 1016 * @param {?string=} opt_query Optional encoded query string to parse into 1017 * the object. 1018 * @param {goog.Uri=} opt_uri Optional uri object that should have its 1019 * cache invalidated when this object updates. Deprecated -- this 1020 * is no longer required. 1021 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter 1022 * name in #get. 1023 * @constructor 1024 * @final 1025 */ 1026 goog.Uri.QueryData = function(opt_query, opt_uri, opt_ignoreCase) { 1027 /** 1028 * Encoded query string, or null if it requires computing from the key map. 1029 * @type {?string} 1030 * @private 1031 */ 1032 this.encodedQuery_ = opt_query || null; 1033 1034 /** 1035 * If true, ignore the case of the parameter name in #get. 1036 * @type {boolean} 1037 * @private 1038 */ 1039 this.ignoreCase_ = !!opt_ignoreCase; 1040 }; 1041 1042 1043 /** 1044 * If the underlying key map is not yet initialized, it parses the 1045 * query string and fills the map with parsed data. 1046 * @private 1047 */ 1048 goog.Uri.QueryData.prototype.ensureKeyMapInitialized_ = function() { 1049 if (!this.keyMap_) { 1050 this.keyMap_ = new goog.structs.Map(); 1051 this.count_ = 0; 1052 1053 if (this.encodedQuery_) { 1054 var pairs = this.encodedQuery_.split('&'); 1055 for (var i = 0; i < pairs.length; i++) { 1056 var indexOfEquals = pairs[i].indexOf('='); 1057 var name = null; 1058 var value = null; 1059 if (indexOfEquals >= 0) { 1060 name = pairs[i].substring(0, indexOfEquals); 1061 value = pairs[i].substring(indexOfEquals + 1); 1062 } else { 1063 name = pairs[i]; 1064 } 1065 name = goog.string.urlDecode(name); 1066 name = this.getKeyName_(name); 1067 this.add(name, value ? goog.string.urlDecode(value) : ''); 1068 } 1069 } 1070 } 1071 }; 1072 1073 1074 /** 1075 * Creates a new query data instance from a map of names and values. 1076 * 1077 * @param {!goog.structs.Map|!Object} map Map of string parameter 1078 * names to parameter value. If parameter value is an array, it is 1079 * treated as if the key maps to each individual value in the 1080 * array. 1081 * @param {goog.Uri=} opt_uri URI object that should have its cache 1082 * invalidated when this object updates. 1083 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter 1084 * name in #get. 1085 * @return {!goog.Uri.QueryData} The populated query data instance. 1086 */ 1087 goog.Uri.QueryData.createFromMap = function(map, opt_uri, opt_ignoreCase) { 1088 var keys = goog.structs.getKeys(map); 1089 if (typeof keys == 'undefined') { 1090 throw Error('Keys are undefined'); 1091 } 1092 1093 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase); 1094 var values = goog.structs.getValues(map); 1095 for (var i = 0; i < keys.length; i++) { 1096 var key = keys[i]; 1097 var value = values[i]; 1098 if (!goog.isArray(value)) { 1099 queryData.add(key, value); 1100 } else { 1101 queryData.setValues(key, value); 1102 } 1103 } 1104 return queryData; 1105 }; 1106 1107 1108 /** 1109 * Creates a new query data instance from parallel arrays of parameter names 1110 * and values. Allows for duplicate parameter names. Throws an error if the 1111 * lengths of the arrays differ. 1112 * 1113 * @param {Array.<string>} keys Parameter names. 1114 * @param {Array} values Parameter values. 1115 * @param {goog.Uri=} opt_uri URI object that should have its cache 1116 * invalidated when this object updates. 1117 * @param {boolean=} opt_ignoreCase If true, ignore the case of the parameter 1118 * name in #get. 1119 * @return {!goog.Uri.QueryData} The populated query data instance. 1120 */ 1121 goog.Uri.QueryData.createFromKeysValues = function( 1122 keys, values, opt_uri, opt_ignoreCase) { 1123 if (keys.length != values.length) { 1124 throw Error('Mismatched lengths for keys/values'); 1125 } 1126 var queryData = new goog.Uri.QueryData(null, null, opt_ignoreCase); 1127 for (var i = 0; i < keys.length; i++) { 1128 queryData.add(keys[i], values[i]); 1129 } 1130 return queryData; 1131 }; 1132 1133 1134 /** 1135 * The map containing name/value or name/array-of-values pairs. 1136 * May be null if it requires parsing from the query string. 1137 * 1138 * We need to use a Map because we cannot guarantee that the key names will 1139 * not be problematic for IE. 1140 * 1141 * @type {goog.structs.Map.<string, Array>} 1142 * @private 1143 */ 1144 goog.Uri.QueryData.prototype.keyMap_ = null; 1145 1146 1147 /** 1148 * The number of params, or null if it requires computing. 1149 * @type {?number} 1150 * @private 1151 */ 1152 goog.Uri.QueryData.prototype.count_ = null; 1153 1154 1155 /** 1156 * @return {?number} The number of parameters. 1157 */ 1158 goog.Uri.QueryData.prototype.getCount = function() { 1159 this.ensureKeyMapInitialized_(); 1160 return this.count_; 1161 }; 1162 1163 1164 /** 1165 * Adds a key value pair. 1166 * @param {string} key Name. 1167 * @param {*} value Value. 1168 * @return {!goog.Uri.QueryData} Instance of this object. 1169 */ 1170 goog.Uri.QueryData.prototype.add = function(key, value) { 1171 this.ensureKeyMapInitialized_(); 1172 this.invalidateCache_(); 1173 1174 key = this.getKeyName_(key); 1175 var values = this.keyMap_.get(key); 1176 if (!values) { 1177 this.keyMap_.set(key, (values = [])); 1178 } 1179 values.push(value); 1180 this.count_++; 1181 return this; 1182 }; 1183 1184 1185 /** 1186 * Removes all the params with the given key. 1187 * @param {string} key Name. 1188 * @return {boolean} Whether any parameter was removed. 1189 */ 1190 goog.Uri.QueryData.prototype.remove = function(key) { 1191 this.ensureKeyMapInitialized_(); 1192 1193 key = this.getKeyName_(key); 1194 if (this.keyMap_.containsKey(key)) { 1195 this.invalidateCache_(); 1196 1197 // Decrement parameter count. 1198 this.count_ -= this.keyMap_.get(key).length; 1199 return this.keyMap_.remove(key); 1200 } 1201 return false; 1202 }; 1203 1204 1205 /** 1206 * Clears the parameters. 1207 */ 1208 goog.Uri.QueryData.prototype.clear = function() { 1209 this.invalidateCache_(); 1210 this.keyMap_ = null; 1211 this.count_ = 0; 1212 }; 1213 1214 1215 /** 1216 * @return {boolean} Whether we have any parameters. 1217 */ 1218 goog.Uri.QueryData.prototype.isEmpty = function() { 1219 this.ensureKeyMapInitialized_(); 1220 return this.count_ == 0; 1221 }; 1222 1223 1224 /** 1225 * Whether there is a parameter with the given name 1226 * @param {string} key The parameter name to check for. 1227 * @return {boolean} Whether there is a parameter with the given name. 1228 */ 1229 goog.Uri.QueryData.prototype.containsKey = function(key) { 1230 this.ensureKeyMapInitialized_(); 1231 key = this.getKeyName_(key); 1232 return this.keyMap_.containsKey(key); 1233 }; 1234 1235 1236 /** 1237 * Whether there is a parameter with the given value. 1238 * @param {*} value The value to check for. 1239 * @return {boolean} Whether there is a parameter with the given value. 1240 */ 1241 goog.Uri.QueryData.prototype.containsValue = function(value) { 1242 // NOTE(arv): This solution goes through all the params even if it was the 1243 // first param. We can get around this by not reusing code or by switching to 1244 // iterators. 1245 var vals = this.getValues(); 1246 return goog.array.contains(vals, value); 1247 }; 1248 1249 1250 /** 1251 * Returns all the keys of the parameters. If a key is used multiple times 1252 * it will be included multiple times in the returned array 1253 * @return {!Array.<string>} All the keys of the parameters. 1254 */ 1255 goog.Uri.QueryData.prototype.getKeys = function() { 1256 this.ensureKeyMapInitialized_(); 1257 // We need to get the values to know how many keys to add. 1258 var vals = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues()); 1259 var keys = this.keyMap_.getKeys(); 1260 var rv = []; 1261 for (var i = 0; i < keys.length; i++) { 1262 var val = vals[i]; 1263 for (var j = 0; j < val.length; j++) { 1264 rv.push(keys[i]); 1265 } 1266 } 1267 return rv; 1268 }; 1269 1270 1271 /** 1272 * Returns all the values of the parameters with the given name. If the query 1273 * data has no such key this will return an empty array. If no key is given 1274 * all values wil be returned. 1275 * @param {string=} opt_key The name of the parameter to get the values for. 1276 * @return {!Array} All the values of the parameters with the given name. 1277 */ 1278 goog.Uri.QueryData.prototype.getValues = function(opt_key) { 1279 this.ensureKeyMapInitialized_(); 1280 var rv = []; 1281 if (goog.isString(opt_key)) { 1282 if (this.containsKey(opt_key)) { 1283 rv = goog.array.concat(rv, this.keyMap_.get(this.getKeyName_(opt_key))); 1284 } 1285 } else { 1286 // Return all values. 1287 var values = /** @type {Array.<Array|*>} */ (this.keyMap_.getValues()); 1288 for (var i = 0; i < values.length; i++) { 1289 rv = goog.array.concat(rv, values[i]); 1290 } 1291 } 1292 return rv; 1293 }; 1294 1295 1296 /** 1297 * Sets a key value pair and removes all other keys with the same value. 1298 * 1299 * @param {string} key Name. 1300 * @param {*} value Value. 1301 * @return {!goog.Uri.QueryData} Instance of this object. 1302 */ 1303 goog.Uri.QueryData.prototype.set = function(key, value) { 1304 this.ensureKeyMapInitialized_(); 1305 this.invalidateCache_(); 1306 1307 // TODO(user): This could be better written as 1308 // this.remove(key), this.add(key, value), but that would reorder 1309 // the key (since the key is first removed and then added at the 1310 // end) and we would have to fix unit tests that depend on key 1311 // ordering. 1312 key = this.getKeyName_(key); 1313 if (this.containsKey(key)) { 1314 this.count_ -= this.keyMap_.get(key).length; 1315 } 1316 this.keyMap_.set(key, [value]); 1317 this.count_++; 1318 return this; 1319 }; 1320 1321 1322 /** 1323 * Returns the first value associated with the key. If the query data has no 1324 * such key this will return undefined or the optional default. 1325 * @param {string} key The name of the parameter to get the value for. 1326 * @param {*=} opt_default The default value to return if the query data 1327 * has no such key. 1328 * @return {*} The first string value associated with the key, or opt_default 1329 * if there's no value. 1330 */ 1331 goog.Uri.QueryData.prototype.get = function(key, opt_default) { 1332 var values = key ? this.getValues(key) : []; 1333 if (goog.Uri.preserveParameterTypesCompatibilityFlag) { 1334 return values.length > 0 ? values[0] : opt_default; 1335 } else { 1336 return values.length > 0 ? String(values[0]) : opt_default; 1337 } 1338 }; 1339 1340 1341 /** 1342 * Sets the values for a key. If the key already exists, this will 1343 * override all of the existing values that correspond to the key. 1344 * @param {string} key The key to set values for. 1345 * @param {Array} values The values to set. 1346 */ 1347 goog.Uri.QueryData.prototype.setValues = function(key, values) { 1348 this.remove(key); 1349 1350 if (values.length > 0) { 1351 this.invalidateCache_(); 1352 this.keyMap_.set(this.getKeyName_(key), goog.array.clone(values)); 1353 this.count_ += values.length; 1354 } 1355 }; 1356 1357 1358 /** 1359 * @return {string} Encoded query string. 1360 * @override 1361 */ 1362 goog.Uri.QueryData.prototype.toString = function() { 1363 if (this.encodedQuery_) { 1364 return this.encodedQuery_; 1365 } 1366 1367 if (!this.keyMap_) { 1368 return ''; 1369 } 1370 1371 var sb = []; 1372 1373 // In the past, we use this.getKeys() and this.getVals(), but that 1374 // generates a lot of allocations as compared to simply iterating 1375 // over the keys. 1376 var keys = this.keyMap_.getKeys(); 1377 for (var i = 0; i < keys.length; i++) { 1378 var key = keys[i]; 1379 var encodedKey = goog.string.urlEncode(key); 1380 var val = this.getValues(key); 1381 for (var j = 0; j < val.length; j++) { 1382 var param = encodedKey; 1383 // Ensure that null and undefined are encoded into the url as 1384 // literal strings. 1385 if (val[j] !== '') { 1386 param += '=' + goog.string.urlEncode(val[j]); 1387 } 1388 sb.push(param); 1389 } 1390 } 1391 1392 return this.encodedQuery_ = sb.join('&'); 1393 }; 1394 1395 1396 /** 1397 * @return {string} Decoded query string. 1398 */ 1399 goog.Uri.QueryData.prototype.toDecodedString = function() { 1400 return goog.Uri.decodeOrEmpty_(this.toString()); 1401 }; 1402 1403 1404 /** 1405 * Invalidate the cache. 1406 * @private 1407 */ 1408 goog.Uri.QueryData.prototype.invalidateCache_ = function() { 1409 this.encodedQuery_ = null; 1410 }; 1411 1412 1413 /** 1414 * Removes all keys that are not in the provided list. (Modifies this object.) 1415 * @param {Array.<string>} keys The desired keys. 1416 * @return {!goog.Uri.QueryData} a reference to this object. 1417 */ 1418 goog.Uri.QueryData.prototype.filterKeys = function(keys) { 1419 this.ensureKeyMapInitialized_(); 1420 this.keyMap_.forEach( 1421 function(value, key) { 1422 if (!goog.array.contains(keys, key)) { 1423 this.remove(key); 1424 } 1425 }, this); 1426 return this; 1427 }; 1428 1429 1430 /** 1431 * Clone the query data instance. 1432 * @return {!goog.Uri.QueryData} New instance of the QueryData object. 1433 */ 1434 goog.Uri.QueryData.prototype.clone = function() { 1435 var rv = new goog.Uri.QueryData(); 1436 rv.encodedQuery_ = this.encodedQuery_; 1437 if (this.keyMap_) { 1438 rv.keyMap_ = this.keyMap_.clone(); 1439 rv.count_ = this.count_; 1440 } 1441 return rv; 1442 }; 1443 1444 1445 /** 1446 * Helper function to get the key name from a JavaScript object. Converts 1447 * the object to a string, and to lower case if necessary. 1448 * @private 1449 * @param {*} arg The object to get a key name from. 1450 * @return {string} valid key name which can be looked up in #keyMap_. 1451 */ 1452 goog.Uri.QueryData.prototype.getKeyName_ = function(arg) { 1453 var keyName = String(arg); 1454 if (this.ignoreCase_) { 1455 keyName = keyName.toLowerCase(); 1456 } 1457 return keyName; 1458 }; 1459 1460 1461 /** 1462 * Ignore case in parameter names. 1463 * NOTE: If there are already key/value pairs in the QueryData, and 1464 * ignoreCase_ is set to false, the keys will all be lower-cased. 1465 * @param {boolean} ignoreCase whether this goog.Uri should ignore case. 1466 */ 1467 goog.Uri.QueryData.prototype.setIgnoreCase = function(ignoreCase) { 1468 var resetKeys = ignoreCase && !this.ignoreCase_; 1469 if (resetKeys) { 1470 this.ensureKeyMapInitialized_(); 1471 this.invalidateCache_(); 1472 this.keyMap_.forEach( 1473 function(value, key) { 1474 var lowerCase = key.toLowerCase(); 1475 if (key != lowerCase) { 1476 this.remove(key); 1477 this.setValues(lowerCase, value); 1478 } 1479 }, this); 1480 } 1481 this.ignoreCase_ = ignoreCase; 1482 }; 1483 1484 1485 /** 1486 * Extends a query data object with another query data or map like object. This 1487 * operates 'in-place', it does not create a new QueryData object. 1488 * 1489 * @param {...(goog.Uri.QueryData|goog.structs.Map|Object)} var_args The object 1490 * from which key value pairs will be copied. 1491 */ 1492 goog.Uri.QueryData.prototype.extend = function(var_args) { 1493 for (var i = 0; i < arguments.length; i++) { 1494 var data = arguments[i]; 1495 goog.structs.forEach(data, 1496 /** @this {goog.Uri.QueryData} */ 1497 function(value, key) { 1498 this.add(key, value); 1499 }, this); 1500 } 1501 }; lib/goog/uri/utils.js
1 // Copyright 2008 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Simple utilities for dealing with URI strings. 17 * 18 * This is intended to be a lightweight alternative to constructing goog.Uri 19 * objects. Whereas goog.Uri adds several kilobytes to the binary regardless 20 * of how much of its functionality you use, this is designed to be a set of 21 * mostly-independent utilities so that the compiler includes only what is 22 * necessary for the task. Estimated savings of porting is 5k pre-gzip and 23 * 1.5k post-gzip. To ensure the savings remain, future developers should 24 * avoid adding new functionality to existing functions, but instead create 25 * new ones and factor out shared code. 26 * 27 * Many of these utilities have limited functionality, tailored to common 28 * cases. The query parameter utilities assume that the parameter keys are 29 * already encoded, since most keys are compile-time alphanumeric strings. The 30 * query parameter mutation utilities also do not tolerate fragment identifiers. 31 * 32 * By design, these functions can be slower than goog.Uri equivalents. 33 * Repeated calls to some of functions may be quadratic in behavior for IE, 34 * although the effect is somewhat limited given the 2kb limit. 35 * 36 * One advantage of the limited functionality here is that this approach is 37 * less sensitive to differences in URI encodings than goog.Uri, since these 38 * functions modify the strings in place, rather than decoding and 39 * re-encoding. 40 * 41 * Uses features of RFC 3986 for parsing/formatting URIs: 42 * http://www.ietf.org/rfc/rfc3986.txt 43 * 44 * @author gboyer@google.com (Garrett Boyer) - The "lightened" design. 45 * @author msamuel@google.com (Mike Samuel) - Domain knowledge and regexes. 46 */ 47 48 goog.provide('goog.uri.utils'); 49 goog.provide('goog.uri.utils.ComponentIndex'); 50 goog.provide('goog.uri.utils.QueryArray'); 51 goog.provide('goog.uri.utils.QueryValue'); 52 goog.provide('goog.uri.utils.StandardQueryParam'); 53 54 goog.require('goog.asserts'); 55 goog.require('goog.string'); 56 goog.require('goog.userAgent'); 57 58 59 /** 60 * Character codes inlined to avoid object allocations due to charCode. 61 * @enum {number} 62 * @private 63 */ 64 goog.uri.utils.CharCode_ = { 65 AMPERSAND: 38, 66 EQUAL: 61, 67 HASH: 35, 68 QUESTION: 63 69 }; 70 71 72 /** 73 * Builds a URI string from already-encoded parts. 74 * 75 * No encoding is performed. Any component may be omitted as either null or 76 * undefined. 77 * 78 * @param {?string=} opt_scheme The scheme such as 'http'. 79 * @param {?string=} opt_userInfo The user name before the '@'. 80 * @param {?string=} opt_domain The domain such as 'www.google.com', already 81 * URI-encoded. 82 * @param {(string|number|null)=} opt_port The port number. 83 * @param {?string=} opt_path The path, already URI-encoded. If it is not 84 * empty, it must begin with a slash. 85 * @param {?string=} opt_queryData The URI-encoded query data. 86 * @param {?string=} opt_fragment The URI-encoded fragment identifier. 87 * @return {string} The fully combined URI. 88 */ 89 goog.uri.utils.buildFromEncodedParts = function(opt_scheme, opt_userInfo, 90 opt_domain, opt_port, opt_path, opt_queryData, opt_fragment) { 91 var out = ''; 92 93 if (opt_scheme) { 94 out += opt_scheme + ':'; 95 } 96 97 if (opt_domain) { 98 out += '//'; 99 100 if (opt_userInfo) { 101 out += opt_userInfo + '@'; 102 } 103 104 out += opt_domain; 105 106 if (opt_port) { 107 out += ':' + opt_port; 108 } 109 } 110 111 if (opt_path) { 112 out += opt_path; 113 } 114 115 if (opt_queryData) { 116 out += '?' + opt_queryData; 117 } 118 119 if (opt_fragment) { 120 out += '#' + opt_fragment; 121 } 122 123 return out; 124 }; 125 126 127 /** 128 * A regular expression for breaking a URI into its component parts. 129 * 130 * {@link http://www.ietf.org/rfc/rfc3986.txt} says in Appendix B 131 * As the "first-match-wins" algorithm is identical to the "greedy" 132 * disambiguation method used by POSIX regular expressions, it is natural and 133 * commonplace to use a regular expression for parsing the potential five 134 * components of a URI reference. 135 * 136 * The following line is the regular expression for breaking-down a 137 * well-formed URI reference into its components. 138 * 139 * <pre> 140 * ^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\?([^#]*))?(#(.*))? 141 * 12 3 4 5 6 7 8 9 142 * </pre> 143 * 144 * The numbers in the second line above are only to assist readability; they 145 * indicate the reference points for each subexpression (i.e., each paired 146 * parenthesis). We refer to the value matched for subexpression <n> as $<n>. 147 * For example, matching the above expression to 148 * <pre> 149 * http://www.ics.uci.edu/pub/ietf/uri/#Related 150 * </pre> 151 * results in the following subexpression matches: 152 * <pre> 153 * $1 = http: 154 * $2 = http 155 * $3 = //www.ics.uci.edu 156 * $4 = www.ics.uci.edu 157 * $5 = /pub/ietf/uri/ 158 * $6 = <undefined> 159 * $7 = <undefined> 160 * $8 = #Related 161 * $9 = Related 162 * </pre> 163 * where <undefined> indicates that the component is not present, as is the 164 * case for the query component in the above example. Therefore, we can 165 * determine the value of the five components as 166 * <pre> 167 * scheme = $2 168 * authority = $4 169 * path = $5 170 * query = $7 171 * fragment = $9 172 * </pre> 173 * 174 * The regular expression has been modified slightly to expose the 175 * userInfo, domain, and port separately from the authority. 176 * The modified version yields 177 * <pre> 178 * $1 = http scheme 179 * $2 = <undefined> userInfo -\ 180 * $3 = www.ics.uci.edu domain | authority 181 * $4 = <undefined> port -/ 182 * $5 = /pub/ietf/uri/ path 183 * $6 = <undefined> query without ? 184 * $7 = Related fragment without # 185 * </pre> 186 * @type {!RegExp} 187 * @private 188 */ 189 goog.uri.utils.splitRe_ = new RegExp( 190 '^' + 191 '(?:' + 192 '([^:/?#.]+)' + // scheme - ignore special characters 193 // used by other URL parts such as :, 194 // ?, /, #, and . 195 ':)?' + 196 '(?://' + 197 '(?:([^/?#]*)@)?' + // userInfo 198 '([^/#?]*?)' + // domain 199 '(?::([0-9]+))?' + // port 200 '(?=[/#?]|$)' + // authority-terminating character 201 ')?' + 202 '([^?#]+)?' + // path 203 '(?:\\?([^#]*))?' + // query 204 '(?:#(.*))?' + // fragment 205 '$'); 206 207 208 /** 209 * The index of each URI component in the return value of goog.uri.utils.split. 210 * @enum {number} 211 */ 212 goog.uri.utils.ComponentIndex = { 213 SCHEME: 1, 214 USER_INFO: 2, 215 DOMAIN: 3, 216 PORT: 4, 217 PATH: 5, 218 QUERY_DATA: 6, 219 FRAGMENT: 7 220 }; 221 222 223 /** 224 * Splits a URI into its component parts. 225 * 226 * Each component can be accessed via the component indices; for example: 227 * <pre> 228 * goog.uri.utils.split(someStr)[goog.uri.utils.CompontentIndex.QUERY_DATA]; 229 * </pre> 230 * 231 * @param {string} uri The URI string to examine. 232 * @return {!Array.<string|undefined>} Each component still URI-encoded. 233 * Each component that is present will contain the encoded value, whereas 234 * components that are not present will be undefined or empty, depending 235 * on the browser's regular expression implementation. Never null, since 236 * arbitrary strings may still look like path names. 237 */ 238 goog.uri.utils.split = function(uri) { 239 goog.uri.utils.phishingProtection_(); 240 241 // See @return comment -- never null. 242 return /** @type {!Array.<string|undefined>} */ ( 243 uri.match(goog.uri.utils.splitRe_)); 244 }; 245 246 247 /** 248 * Safari has a nasty bug where if you have an http URL with a username, e.g., 249 * http://evil.com%2F@google.com/ 250 * Safari will report that window.location.href is 251 * http://evil.com/google.com/ 252 * so that anyone who tries to parse the domain of that URL will get 253 * the wrong domain. We've seen exploits where people use this to trick 254 * Safari into loading resources from evil domains. 255 * 256 * To work around this, we run a little "Safari phishing check", and throw 257 * an exception if we see this happening. 258 * 259 * There is no convenient place to put this check. We apply it to 260 * anyone doing URI parsing on Webkit. We're not happy about this, but 261 * it fixes the problem. 262 * 263 * This should be removed once Safari fixes their bug. 264 * 265 * Exploit reported by Masato Kinugawa. 266 * 267 * @type {boolean} 268 * @private 269 */ 270 goog.uri.utils.needsPhishingProtection_ = goog.userAgent.WEBKIT; 271 272 273 /** 274 * Check to see if the user is being phished. 275 * @private 276 */ 277 goog.uri.utils.phishingProtection_ = function() { 278 if (goog.uri.utils.needsPhishingProtection_) { 279 // Turn protection off, so that we don't recurse. 280 goog.uri.utils.needsPhishingProtection_ = false; 281 282 // Use quoted access, just in case the user isn't using location externs. 283 var location = goog.global['location']; 284 if (location) { 285 var href = location['href']; 286 if (href) { 287 var domain = goog.uri.utils.getDomain(href); 288 if (domain && domain != location['hostname']) { 289 // Phishing attack 290 goog.uri.utils.needsPhishingProtection_ = true; 291 throw Error(); 292 } 293 } 294 } 295 } 296 }; 297 298 299 /** 300 * @param {?string} uri A possibly null string. 301 * @return {?string} The string URI-decoded, or null if uri is null. 302 * @private 303 */ 304 goog.uri.utils.decodeIfPossible_ = function(uri) { 305 return uri && decodeURIComponent(uri); 306 }; 307 308 309 /** 310 * Gets a URI component by index. 311 * 312 * It is preferred to use the getPathEncoded() variety of functions ahead, 313 * since they are more readable. 314 * 315 * @param {goog.uri.utils.ComponentIndex} componentIndex The component index. 316 * @param {string} uri The URI to examine. 317 * @return {?string} The still-encoded component, or null if the component 318 * is not present. 319 * @private 320 */ 321 goog.uri.utils.getComponentByIndex_ = function(componentIndex, uri) { 322 // Convert undefined, null, and empty string into null. 323 return goog.uri.utils.split(uri)[componentIndex] || null; 324 }; 325 326 327 /** 328 * @param {string} uri The URI to examine. 329 * @return {?string} The protocol or scheme, or null if none. Does not 330 * include trailing colons or slashes. 331 */ 332 goog.uri.utils.getScheme = function(uri) { 333 return goog.uri.utils.getComponentByIndex_( 334 goog.uri.utils.ComponentIndex.SCHEME, uri); 335 }; 336 337 338 /** 339 * Gets the effective scheme for the URL. If the URL is relative then the 340 * scheme is derived from the page's location. 341 * @param {string} uri The URI to examine. 342 * @return {string} The protocol or scheme, always lower case. 343 */ 344 goog.uri.utils.getEffectiveScheme = function(uri) { 345 var scheme = goog.uri.utils.getScheme(uri); 346 if (!scheme && self.location) { 347 var protocol = self.location.protocol; 348 scheme = protocol.substr(0, protocol.length - 1); 349 } 350 // NOTE: When called from a web worker in Firefox 3.5, location maybe null. 351 // All other browsers with web workers support self.location from the worker. 352 return scheme ? scheme.toLowerCase() : ''; 353 }; 354 355 356 /** 357 * @param {string} uri The URI to examine. 358 * @return {?string} The user name still encoded, or null if none. 359 */ 360 goog.uri.utils.getUserInfoEncoded = function(uri) { 361 return goog.uri.utils.getComponentByIndex_( 362 goog.uri.utils.ComponentIndex.USER_INFO, uri); 363 }; 364 365 366 /** 367 * @param {string} uri The URI to examine. 368 * @return {?string} The decoded user info, or null if none. 369 */ 370 goog.uri.utils.getUserInfo = function(uri) { 371 return goog.uri.utils.decodeIfPossible_( 372 goog.uri.utils.getUserInfoEncoded(uri)); 373 }; 374 375 376 /** 377 * @param {string} uri The URI to examine. 378 * @return {?string} The domain name still encoded, or null if none. 379 */ 380 goog.uri.utils.getDomainEncoded = function(uri) { 381 return goog.uri.utils.getComponentByIndex_( 382 goog.uri.utils.ComponentIndex.DOMAIN, uri); 383 }; 384 385 386 /** 387 * @param {string} uri The URI to examine. 388 * @return {?string} The decoded domain, or null if none. 389 */ 390 goog.uri.utils.getDomain = function(uri) { 391 return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getDomainEncoded(uri)); 392 }; 393 394 395 /** 396 * @param {string} uri The URI to examine. 397 * @return {?number} The port number, or null if none. 398 */ 399 goog.uri.utils.getPort = function(uri) { 400 // Coerce to a number. If the result of getComponentByIndex_ is null or 401 // non-numeric, the number coersion yields NaN. This will then return 402 // null for all non-numeric cases (though also zero, which isn't a relevant 403 // port number). 404 return Number(goog.uri.utils.getComponentByIndex_( 405 goog.uri.utils.ComponentIndex.PORT, uri)) || null; 406 }; 407 408 409 /** 410 * @param {string} uri The URI to examine. 411 * @return {?string} The path still encoded, or null if none. Includes the 412 * leading slash, if any. 413 */ 414 goog.uri.utils.getPathEncoded = function(uri) { 415 return goog.uri.utils.getComponentByIndex_( 416 goog.uri.utils.ComponentIndex.PATH, uri); 417 }; 418 419 420 /** 421 * @param {string} uri The URI to examine. 422 * @return {?string} The decoded path, or null if none. Includes the leading 423 * slash, if any. 424 */ 425 goog.uri.utils.getPath = function(uri) { 426 return goog.uri.utils.decodeIfPossible_(goog.uri.utils.getPathEncoded(uri)); 427 }; 428 429 430 /** 431 * @param {string} uri The URI to examine. 432 * @return {?string} The query data still encoded, or null if none. Does not 433 * include the question mark itself. 434 */ 435 goog.uri.utils.getQueryData = function(uri) { 436 return goog.uri.utils.getComponentByIndex_( 437 goog.uri.utils.ComponentIndex.QUERY_DATA, uri); 438 }; 439 440 441 /** 442 * @param {string} uri The URI to examine. 443 * @return {?string} The fragment identifier, or null if none. Does not 444 * include the hash mark itself. 445 */ 446 goog.uri.utils.getFragmentEncoded = function(uri) { 447 // The hash mark may not appear in any other part of the URL. 448 var hashIndex = uri.indexOf('#'); 449 return hashIndex < 0 ? null : uri.substr(hashIndex + 1); 450 }; 451 452 453 /** 454 * @param {string} uri The URI to examine. 455 * @param {?string} fragment The encoded fragment identifier, or null if none. 456 * Does not include the hash mark itself. 457 * @return {string} The URI with the fragment set. 458 */ 459 goog.uri.utils.setFragmentEncoded = function(uri, fragment) { 460 return goog.uri.utils.removeFragment(uri) + (fragment ? '#' + fragment : ''); 461 }; 462 463 464 /** 465 * @param {string} uri The URI to examine. 466 * @return {?string} The decoded fragment identifier, or null if none. Does 467 * not include the hash mark. 468 */ 469 goog.uri.utils.getFragment = function(uri) { 470 return goog.uri.utils.decodeIfPossible_( 471 goog.uri.utils.getFragmentEncoded(uri)); 472 }; 473 474 475 /** 476 * Extracts everything up to the port of the URI. 477 * @param {string} uri The URI string. 478 * @return {string} Everything up to and including the port. 479 */ 480 goog.uri.utils.getHost = function(uri) { 481 var pieces = goog.uri.utils.split(uri); 482 return goog.uri.utils.buildFromEncodedParts( 483 pieces[goog.uri.utils.ComponentIndex.SCHEME], 484 pieces[goog.uri.utils.ComponentIndex.USER_INFO], 485 pieces[goog.uri.utils.ComponentIndex.DOMAIN], 486 pieces[goog.uri.utils.ComponentIndex.PORT]); 487 }; 488 489 490 /** 491 * Extracts the path of the URL and everything after. 492 * @param {string} uri The URI string. 493 * @return {string} The URI, starting at the path and including the query 494 * parameters and fragment identifier. 495 */ 496 goog.uri.utils.getPathAndAfter = function(uri) { 497 var pieces = goog.uri.utils.split(uri); 498 return goog.uri.utils.buildFromEncodedParts(null, null, null, null, 499 pieces[goog.uri.utils.ComponentIndex.PATH], 500 pieces[goog.uri.utils.ComponentIndex.QUERY_DATA], 501 pieces[goog.uri.utils.ComponentIndex.FRAGMENT]); 502 }; 503 504 505 /** 506 * Gets the URI with the fragment identifier removed. 507 * @param {string} uri The URI to examine. 508 * @return {string} Everything preceding the hash mark. 509 */ 510 goog.uri.utils.removeFragment = function(uri) { 511 // The hash mark may not appear in any other part of the URL. 512 var hashIndex = uri.indexOf('#'); 513 return hashIndex < 0 ? uri : uri.substr(0, hashIndex); 514 }; 515 516 517 /** 518 * Ensures that two URI's have the exact same domain, scheme, and port. 519 * 520 * Unlike the version in goog.Uri, this checks protocol, and therefore is 521 * suitable for checking against the browser's same-origin policy. 522 * 523 * @param {string} uri1 The first URI. 524 * @param {string} uri2 The second URI. 525 * @return {boolean} Whether they have the same domain and port. 526 */ 527 goog.uri.utils.haveSameDomain = function(uri1, uri2) { 528 var pieces1 = goog.uri.utils.split(uri1); 529 var pieces2 = goog.uri.utils.split(uri2); 530 return pieces1[goog.uri.utils.ComponentIndex.DOMAIN] == 531 pieces2[goog.uri.utils.ComponentIndex.DOMAIN] && 532 pieces1[goog.uri.utils.ComponentIndex.SCHEME] == 533 pieces2[goog.uri.utils.ComponentIndex.SCHEME] && 534 pieces1[goog.uri.utils.ComponentIndex.PORT] == 535 pieces2[goog.uri.utils.ComponentIndex.PORT]; 536 }; 537 538 539 /** 540 * Asserts that there are no fragment or query identifiers, only in uncompiled 541 * mode. 542 * @param {string} uri The URI to examine. 543 * @private 544 */ 545 goog.uri.utils.assertNoFragmentsOrQueries_ = function(uri) { 546 // NOTE: would use goog.asserts here, but jscompiler doesn't know that 547 // indexOf has no side effects. 548 if (goog.DEBUG && (uri.indexOf('#') >= 0 || uri.indexOf('?') >= 0)) { 549 throw Error('goog.uri.utils: Fragment or query identifiers are not ' + 550 'supported: [' + uri + ']'); 551 } 552 }; 553 554 555 /** 556 * Supported query parameter values by the parameter serializing utilities. 557 * 558 * If a value is null or undefined, the key-value pair is skipped, as an easy 559 * way to omit parameters conditionally. Non-array parameters are converted 560 * to a string and URI encoded. Array values are expanded into multiple 561 * &key=value pairs, with each element stringized and URI-encoded. 562 * 563 * @typedef {*} 564 */ 565 goog.uri.utils.QueryValue; 566 567 568 /** 569 * An array representing a set of query parameters with alternating keys 570 * and values. 571 * 572 * Keys are assumed to be URI encoded already and live at even indices. See 573 * goog.uri.utils.QueryValue for details on how parameter values are encoded. 574 * 575 * Example: 576 * <pre> 577 * var data = [ 578 * // Simple param: ?name=BobBarker 579 * 'name', 'BobBarker', 580 * // Conditional param -- may be omitted entirely. 581 * 'specialDietaryNeeds', hasDietaryNeeds() ? getDietaryNeeds() : null, 582 * // Multi-valued param: &house=LosAngeles&house=NewYork&house=null 583 * 'house', ['LosAngeles', 'NewYork', null] 584 * ]; 585 * </pre> 586 * 587 * @typedef {!Array.<string|goog.uri.utils.QueryValue>} 588 */ 589 goog.uri.utils.QueryArray; 590 591 592 /** 593 * Appends a URI and query data in a string buffer with special preconditions. 594 * 595 * Internal implementation utility, performing very few object allocations. 596 * 597 * @param {!Array.<string|undefined>} buffer A string buffer. The first element 598 * must be the base URI, and may have a fragment identifier. If the array 599 * contains more than one element, the second element must be an ampersand, 600 * and may be overwritten, depending on the base URI. Undefined elements 601 * are treated as empty-string. 602 * @return {string} The concatenated URI and query data. 603 * @private 604 */ 605 goog.uri.utils.appendQueryData_ = function(buffer) { 606 if (buffer[1]) { 607 // At least one query parameter was added. We need to check the 608 // punctuation mark, which is currently an ampersand, and also make sure 609 // there aren't any interfering fragment identifiers. 610 var baseUri = /** @type {string} */ (buffer[0]); 611 var hashIndex = baseUri.indexOf('#'); 612 if (hashIndex >= 0) { 613 // Move the fragment off the base part of the URI into the end. 614 buffer.push(baseUri.substr(hashIndex)); 615 buffer[0] = baseUri = baseUri.substr(0, hashIndex); 616 } 617 var questionIndex = baseUri.indexOf('?'); 618 if (questionIndex < 0) { 619 // No question mark, so we need a question mark instead of an ampersand. 620 buffer[1] = '?'; 621 } else if (questionIndex == baseUri.length - 1) { 622 // Question mark is the very last character of the existing URI, so don't 623 // append an additional delimiter. 624 buffer[1] = undefined; 625 } 626 } 627 628 return buffer.join(''); 629 }; 630 631 632 /** 633 * Appends key=value pairs to an array, supporting multi-valued objects. 634 * @param {string} key The key prefix. 635 * @param {goog.uri.utils.QueryValue} value The value to serialize. 636 * @param {!Array.<string>} pairs The array to which the 'key=value' strings 637 * should be appended. 638 * @private 639 */ 640 goog.uri.utils.appendKeyValuePairs_ = function(key, value, pairs) { 641 if (goog.isArray(value)) { 642 // Convince the compiler it's an array. 643 goog.asserts.assertArray(value); 644 for (var j = 0; j < value.length; j++) { 645 // Convert to string explicitly, to short circuit the null and array 646 // logic in this function -- this ensures that null and undefined get 647 // written as literal 'null' and 'undefined', and arrays don't get 648 // expanded out but instead encoded in the default way. 649 goog.uri.utils.appendKeyValuePairs_(key, String(value[j]), pairs); 650 } 651 } else if (value != null) { 652 // Skip a top-level null or undefined entirely. 653 pairs.push('&', key, 654 // Check for empty string. Zero gets encoded into the url as literal 655 // strings. For empty string, skip the equal sign, to be consistent 656 // with UriBuilder.java. 657 value === '' ? '' : '=', 658 goog.string.urlEncode(value)); 659 } 660 }; 661 662 663 /** 664 * Builds a buffer of query data from a sequence of alternating keys and values. 665 * 666 * @param {!Array.<string|undefined>} buffer A string buffer to append to. The 667 * first element appended will be an '&', and may be replaced by the caller. 668 * @param {goog.uri.utils.QueryArray|Arguments} keysAndValues An array with 669 * alternating keys and values -- see the typedef. 670 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0. 671 * @return {!Array.<string|undefined>} The buffer argument. 672 * @private 673 */ 674 goog.uri.utils.buildQueryDataBuffer_ = function( 675 buffer, keysAndValues, opt_startIndex) { 676 goog.asserts.assert(Math.max(keysAndValues.length - (opt_startIndex || 0), 677 0) % 2 == 0, 'goog.uri.utils: Key/value lists must be even in length.'); 678 679 for (var i = opt_startIndex || 0; i < keysAndValues.length; i += 2) { 680 goog.uri.utils.appendKeyValuePairs_( 681 keysAndValues[i], keysAndValues[i + 1], buffer); 682 } 683 684 return buffer; 685 }; 686 687 688 /** 689 * Builds a query data string from a sequence of alternating keys and values. 690 * Currently generates "&key&" for empty args. 691 * 692 * @param {goog.uri.utils.QueryArray} keysAndValues Alternating keys and 693 * values. See the typedef. 694 * @param {number=} opt_startIndex A start offset into the arary, defaults to 0. 695 * @return {string} The encoded query string, in the form 'a=1&b=2'. 696 */ 697 goog.uri.utils.buildQueryData = function(keysAndValues, opt_startIndex) { 698 var buffer = goog.uri.utils.buildQueryDataBuffer_( 699 [], keysAndValues, opt_startIndex); 700 buffer[0] = ''; // Remove the leading ampersand. 701 return buffer.join(''); 702 }; 703 704 705 /** 706 * Builds a buffer of query data from a map. 707 * 708 * @param {!Array.<string|undefined>} buffer A string buffer to append to. The 709 * first element appended will be an '&', and may be replaced by the caller. 710 * @param {Object.<goog.uri.utils.QueryValue>} map An object where keys are 711 * URI-encoded parameter keys, and the values conform to the contract 712 * specified in the goog.uri.utils.QueryValue typedef. 713 * @return {!Array.<string|undefined>} The buffer argument. 714 * @private 715 */ 716 goog.uri.utils.buildQueryDataBufferFromMap_ = function(buffer, map) { 717 for (var key in map) { 718 goog.uri.utils.appendKeyValuePairs_(key, map[key], buffer); 719 } 720 721 return buffer; 722 }; 723 724 725 /** 726 * Builds a query data string from a map. 727 * Currently generates "&key&" for empty args. 728 * 729 * @param {Object} map An object where keys are URI-encoded parameter keys, 730 * and the values are arbitrary types or arrays. Keys with a null value 731 * are dropped. 732 * @return {string} The encoded query string, in the form 'a=1&b=2'. 733 */ 734 goog.uri.utils.buildQueryDataFromMap = function(map) { 735 var buffer = goog.uri.utils.buildQueryDataBufferFromMap_([], map); 736 buffer[0] = ''; 737 return buffer.join(''); 738 }; 739 740 741 /** 742 * Appends URI parameters to an existing URI. 743 * 744 * The variable arguments may contain alternating keys and values. Keys are 745 * assumed to be already URI encoded. The values should not be URI-encoded, 746 * and will instead be encoded by this function. 747 * <pre> 748 * appendParams('http://www.foo.com?existing=true', 749 * 'key1', 'value1', 750 * 'key2', 'value?willBeEncoded', 751 * 'key3', ['valueA', 'valueB', 'valueC'], 752 * 'key4', null); 753 * result: 'http://www.foo.com?existing=true&' + 754 * 'key1=value1&' + 755 * 'key2=value%3FwillBeEncoded&' + 756 * 'key3=valueA&key3=valueB&key3=valueC' 757 * </pre> 758 * 759 * A single call to this function will not exhibit quadratic behavior in IE, 760 * whereas multiple repeated calls may, although the effect is limited by 761 * fact that URL's generally can't exceed 2kb. 762 * 763 * @param {string} uri The original URI, which may already have query data. 764 * @param {...(goog.uri.utils.QueryArray|string|goog.uri.utils.QueryValue)} var_args 765 * An array or argument list conforming to goog.uri.utils.QueryArray. 766 * @return {string} The URI with all query parameters added. 767 */ 768 goog.uri.utils.appendParams = function(uri, var_args) { 769 return goog.uri.utils.appendQueryData_( 770 arguments.length == 2 ? 771 goog.uri.utils.buildQueryDataBuffer_([uri], arguments[1], 0) : 772 goog.uri.utils.buildQueryDataBuffer_([uri], arguments, 1)); 773 }; 774 775 776 /** 777 * Appends query parameters from a map. 778 * 779 * @param {string} uri The original URI, which may already have query data. 780 * @param {Object} map An object where keys are URI-encoded parameter keys, 781 * and the values are arbitrary types or arrays. Keys with a null value 782 * are dropped. 783 * @return {string} The new parameters. 784 */ 785 goog.uri.utils.appendParamsFromMap = function(uri, map) { 786 return goog.uri.utils.appendQueryData_( 787 goog.uri.utils.buildQueryDataBufferFromMap_([uri], map)); 788 }; 789 790 791 /** 792 * Appends a single URI parameter. 793 * 794 * Repeated calls to this can exhibit quadratic behavior in IE6 due to the 795 * way string append works, though it should be limited given the 2kb limit. 796 * 797 * @param {string} uri The original URI, which may already have query data. 798 * @param {string} key The key, which must already be URI encoded. 799 * @param {*=} opt_value The value, which will be stringized and encoded 800 * (assumed not already to be encoded). If omitted, undefined, or null, the 801 * key will be added as a valueless parameter. 802 * @return {string} The URI with the query parameter added. 803 */ 804 goog.uri.utils.appendParam = function(uri, key, opt_value) { 805 var paramArr = [uri, '&', key]; 806 if (goog.isDefAndNotNull(opt_value)) { 807 paramArr.push('=', goog.string.urlEncode(opt_value)); 808 } 809 return goog.uri.utils.appendQueryData_(paramArr); 810 }; 811 812 813 /** 814 * Finds the next instance of a query parameter with the specified name. 815 * 816 * Does not instantiate any objects. 817 * 818 * @param {string} uri The URI to search. May contain a fragment identifier 819 * if opt_hashIndex is specified. 820 * @param {number} startIndex The index to begin searching for the key at. A 821 * match may be found even if this is one character after the ampersand. 822 * @param {string} keyEncoded The URI-encoded key. 823 * @param {number} hashOrEndIndex Index to stop looking at. If a hash 824 * mark is present, it should be its index, otherwise it should be the 825 * length of the string. 826 * @return {number} The position of the first character in the key's name, 827 * immediately after either a question mark or a dot. 828 * @private 829 */ 830 goog.uri.utils.findParam_ = function( 831 uri, startIndex, keyEncoded, hashOrEndIndex) { 832 var index = startIndex; 833 var keyLength = keyEncoded.length; 834 835 // Search for the key itself and post-filter for surronuding punctuation, 836 // rather than expensively building a regexp. 837 while ((index = uri.indexOf(keyEncoded, index)) >= 0 && 838 index < hashOrEndIndex) { 839 var precedingChar = uri.charCodeAt(index - 1); 840 // Ensure that the preceding character is '&' or '?'. 841 if (precedingChar == goog.uri.utils.CharCode_.AMPERSAND || 842 precedingChar == goog.uri.utils.CharCode_.QUESTION) { 843 // Ensure the following character is '&', '=', '#', or NaN 844 // (end of string). 845 var followingChar = uri.charCodeAt(index + keyLength); 846 if (!followingChar || 847 followingChar == goog.uri.utils.CharCode_.EQUAL || 848 followingChar == goog.uri.utils.CharCode_.AMPERSAND || 849 followingChar == goog.uri.utils.CharCode_.HASH) { 850 return index; 851 } 852 } 853 index += keyLength + 1; 854 } 855 856 return -1; 857 }; 858 859 860 /** 861 * Regular expression for finding a hash mark or end of string. 862 * @type {RegExp} 863 * @private 864 */ 865 goog.uri.utils.hashOrEndRe_ = /#|$/; 866 867 868 /** 869 * Determines if the URI contains a specific key. 870 * 871 * Performs no object instantiations. 872 * 873 * @param {string} uri The URI to process. May contain a fragment 874 * identifier. 875 * @param {string} keyEncoded The URI-encoded key. Case-sensitive. 876 * @return {boolean} Whether the key is present. 877 */ 878 goog.uri.utils.hasParam = function(uri, keyEncoded) { 879 return goog.uri.utils.findParam_(uri, 0, keyEncoded, 880 uri.search(goog.uri.utils.hashOrEndRe_)) >= 0; 881 }; 882 883 884 /** 885 * Gets the first value of a query parameter. 886 * @param {string} uri The URI to process. May contain a fragment. 887 * @param {string} keyEncoded The URI-encoded key. Case-sensitive. 888 * @return {?string} The first value of the parameter (URI-decoded), or null 889 * if the parameter is not found. 890 */ 891 goog.uri.utils.getParamValue = function(uri, keyEncoded) { 892 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); 893 var foundIndex = goog.uri.utils.findParam_( 894 uri, 0, keyEncoded, hashOrEndIndex); 895 896 if (foundIndex < 0) { 897 return null; 898 } else { 899 var endPosition = uri.indexOf('&', foundIndex); 900 if (endPosition < 0 || endPosition > hashOrEndIndex) { 901 endPosition = hashOrEndIndex; 902 } 903 // Progress forth to the end of the "key=" or "key&" substring. 904 foundIndex += keyEncoded.length + 1; 905 // Use substr, because it (unlike substring) will return empty string 906 // if foundIndex > endPosition. 907 return goog.string.urlDecode( 908 uri.substr(foundIndex, endPosition - foundIndex)); 909 } 910 }; 911 912 913 /** 914 * Gets all values of a query parameter. 915 * @param {string} uri The URI to process. May contain a framgnet. 916 * @param {string} keyEncoded The URI-encoded key. Case-snsitive. 917 * @return {!Array.<string>} All URI-decoded values with the given key. 918 * If the key is not found, this will have length 0, but never be null. 919 */ 920 goog.uri.utils.getParamValues = function(uri, keyEncoded) { 921 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); 922 var position = 0; 923 var foundIndex; 924 var result = []; 925 926 while ((foundIndex = goog.uri.utils.findParam_( 927 uri, position, keyEncoded, hashOrEndIndex)) >= 0) { 928 // Find where this parameter ends, either the '&' or the end of the 929 // query parameters. 930 position = uri.indexOf('&', foundIndex); 931 if (position < 0 || position > hashOrEndIndex) { 932 position = hashOrEndIndex; 933 } 934 935 // Progress forth to the end of the "key=" or "key&" substring. 936 foundIndex += keyEncoded.length + 1; 937 // Use substr, because it (unlike substring) will return empty string 938 // if foundIndex > position. 939 result.push(goog.string.urlDecode(uri.substr( 940 foundIndex, position - foundIndex))); 941 } 942 943 return result; 944 }; 945 946 947 /** 948 * Regexp to find trailing question marks and ampersands. 949 * @type {RegExp} 950 * @private 951 */ 952 goog.uri.utils.trailingQueryPunctuationRe_ = /[?&]($|#)/; 953 954 955 /** 956 * Removes all instances of a query parameter. 957 * @param {string} uri The URI to process. Must not contain a fragment. 958 * @param {string} keyEncoded The URI-encoded key. 959 * @return {string} The URI with all instances of the parameter removed. 960 */ 961 goog.uri.utils.removeParam = function(uri, keyEncoded) { 962 var hashOrEndIndex = uri.search(goog.uri.utils.hashOrEndRe_); 963 var position = 0; 964 var foundIndex; 965 var buffer = []; 966 967 // Look for a query parameter. 968 while ((foundIndex = goog.uri.utils.findParam_( 969 uri, position, keyEncoded, hashOrEndIndex)) >= 0) { 970 // Get the portion of the query string up to, but not including, the ? 971 // or & starting the parameter. 972 buffer.push(uri.substring(position, foundIndex)); 973 // Progress to immediately after the '&'. If not found, go to the end. 974 // Avoid including the hash mark. 975 position = Math.min((uri.indexOf('&', foundIndex) + 1) || hashOrEndIndex, 976 hashOrEndIndex); 977 } 978 979 // Append everything that is remaining. 980 buffer.push(uri.substr(position)); 981 982 // Join the buffer, and remove trailing punctuation that remains. 983 return buffer.join('').replace( 984 goog.uri.utils.trailingQueryPunctuationRe_, '$1'); 985 }; 986 987 988 /** 989 * Replaces all existing definitions of a parameter with a single definition. 990 * 991 * Repeated calls to this can exhibit quadratic behavior due to the need to 992 * find existing instances and reconstruct the string, though it should be 993 * limited given the 2kb limit. Consider using appendParams to append multiple 994 * parameters in bulk. 995 * 996 * @param {string} uri The original URI, which may already have query data. 997 * @param {string} keyEncoded The key, which must already be URI encoded. 998 * @param {*} value The value, which will be stringized and encoded (assumed 999 * not already to be encoded). 1000 * @return {string} The URI with the query parameter added. 1001 */ 1002 goog.uri.utils.setParam = function(uri, keyEncoded, value) { 1003 return goog.uri.utils.appendParam( 1004 goog.uri.utils.removeParam(uri, keyEncoded), keyEncoded, value); 1005 }; 1006 1007 1008 /** 1009 * Generates a URI path using a given URI and a path with checks to 1010 * prevent consecutive "//". The baseUri passed in must not contain 1011 * query or fragment identifiers. The path to append may not contain query or 1012 * fragment identifiers. 1013 * 1014 * @param {string} baseUri URI to use as the base. 1015 * @param {string} path Path to append. 1016 * @return {string} Updated URI. 1017 */ 1018 goog.uri.utils.appendPath = function(baseUri, path) { 1019 goog.uri.utils.assertNoFragmentsOrQueries_(baseUri); 1020 1021 // Remove any trailing '/' 1022 if (goog.string.endsWith(baseUri, '/')) { 1023 baseUri = baseUri.substr(0, baseUri.length - 1); 1024 } 1025 // Remove any leading '/' 1026 if (goog.string.startsWith(path, '/')) { 1027 path = path.substr(1); 1028 } 1029 return goog.string.buildString(baseUri, '/', path); 1030 }; 1031 1032 1033 /** 1034 * Replaces the path. 1035 * @param {string} uri URI to use as the base. 1036 * @param {string} path New path. 1037 * @return {string} Updated URI. 1038 */ 1039 goog.uri.utils.setPath = function(uri, path) { 1040 // Add any missing '/'. 1041 if (!goog.string.startsWith(path, '/')) { 1042 path = '/' + path; 1043 } 1044 var parts = goog.uri.utils.split(uri); 1045 return goog.uri.utils.buildFromEncodedParts( 1046 parts[goog.uri.utils.ComponentIndex.SCHEME], 1047 parts[goog.uri.utils.ComponentIndex.USER_INFO], 1048 parts[goog.uri.utils.ComponentIndex.DOMAIN], 1049 parts[goog.uri.utils.ComponentIndex.PORT], 1050 path, 1051 parts[goog.uri.utils.ComponentIndex.QUERY_DATA], 1052 parts[goog.uri.utils.ComponentIndex.FRAGMENT]); 1053 }; 1054 1055 1056 /** 1057 * Standard supported query parameters. 1058 * @enum {string} 1059 */ 1060 goog.uri.utils.StandardQueryParam = { 1061 1062 /** Unused parameter for unique-ifying. */ 1063 RANDOM: 'zx' 1064 }; 1065 1066 1067 /** 1068 * Sets the zx parameter of a URI to a random value. 1069 * @param {string} uri Any URI. 1070 * @return {string} That URI with the "zx" parameter added or replaced to 1071 * contain a random string. 1072 */ 1073 goog.uri.utils.makeUnique = function(uri) { 1074 return goog.uri.utils.setParam(uri, 1075 goog.uri.utils.StandardQueryParam.RANDOM, goog.string.getRandomString()); 1076 }; lib/goog/useragent/product.js
1 // Copyright 2008 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Detects the specific browser and not just the rendering engine. 17 * 18 */ 19 20 goog.provide('goog.userAgent.product'); 21 22 goog.require('goog.userAgent'); 23 24 25 /** 26 * @define {boolean} Whether the code is running on the Firefox web browser. 27 */ 28 goog.define('goog.userAgent.product.ASSUME_FIREFOX', false); 29 30 31 /** 32 * @define {boolean} Whether the code is running on the Camino web browser. 33 */ 34 goog.define('goog.userAgent.product.ASSUME_CAMINO', false); 35 36 37 /** 38 * @define {boolean} Whether we know at compile-time that the product is an 39 * iPhone. 40 */ 41 goog.define('goog.userAgent.product.ASSUME_IPHONE', false); 42 43 44 /** 45 * @define {boolean} Whether we know at compile-time that the product is an 46 * iPad. 47 */ 48 goog.define('goog.userAgent.product.ASSUME_IPAD', false); 49 50 51 /** 52 * @define {boolean} Whether we know at compile-time that the product is an 53 * Android phone. 54 */ 55 goog.define('goog.userAgent.product.ASSUME_ANDROID', false); 56 57 58 /** 59 * @define {boolean} Whether the code is running on the Chrome web browser. 60 */ 61 goog.define('goog.userAgent.product.ASSUME_CHROME', false); 62 63 64 /** 65 * @define {boolean} Whether the code is running on the Safari web browser. 66 */ 67 goog.define('goog.userAgent.product.ASSUME_SAFARI', false); 68 69 70 /** 71 * Whether we know the product type at compile-time. 72 * @type {boolean} 73 * @private 74 */ 75 goog.userAgent.product.PRODUCT_KNOWN_ = 76 goog.userAgent.ASSUME_IE || 77 goog.userAgent.ASSUME_OPERA || 78 goog.userAgent.product.ASSUME_FIREFOX || 79 goog.userAgent.product.ASSUME_CAMINO || 80 goog.userAgent.product.ASSUME_IPHONE || 81 goog.userAgent.product.ASSUME_IPAD || 82 goog.userAgent.product.ASSUME_ANDROID || 83 goog.userAgent.product.ASSUME_CHROME || 84 goog.userAgent.product.ASSUME_SAFARI; 85 86 87 /** 88 * Right now we just focus on Tier 1-3 browsers at: 89 * http://wiki/Nonconf/ProductPlatformGuidelines 90 * As well as the YUI grade A browsers at: 91 * http://developer.yahoo.com/yui/articles/gbs/ 92 * 93 * @private 94 */ 95 goog.userAgent.product.init_ = function() { 96 97 /** 98 * Whether the code is running on the Firefox web browser. 99 * @type {boolean} 100 * @private 101 */ 102 goog.userAgent.product.detectedFirefox_ = false; 103 104 /** 105 * Whether the code is running on the Camino web browser. 106 * @type {boolean} 107 * @private 108 */ 109 goog.userAgent.product.detectedCamino_ = false; 110 111 /** 112 * Whether the code is running on an iPhone or iPod touch. 113 * @type {boolean} 114 * @private 115 */ 116 goog.userAgent.product.detectedIphone_ = false; 117 118 /** 119 * Whether the code is running on an iPad 120 * @type {boolean} 121 * @private 122 */ 123 goog.userAgent.product.detectedIpad_ = false; 124 125 /** 126 * Whether the code is running on the default browser on an Android phone. 127 * @type {boolean} 128 * @private 129 */ 130 goog.userAgent.product.detectedAndroid_ = false; 131 132 /** 133 * Whether the code is running on the Chrome web browser. 134 * @type {boolean} 135 * @private 136 */ 137 goog.userAgent.product.detectedChrome_ = false; 138 139 /** 140 * Whether the code is running on the Safari web browser. 141 * @type {boolean} 142 * @private 143 */ 144 goog.userAgent.product.detectedSafari_ = false; 145 146 var ua = goog.userAgent.getUserAgentString(); 147 if (!ua) { 148 return; 149 } 150 151 // The order of the if-statements in the following code is important. 152 // For example, in the WebKit section, we put Chrome in front of Safari 153 // because the string 'Safari' is present on both of those browsers' 154 // userAgent strings as well as the string we are looking for. 155 // The idea is to prevent accidental detection of more than one client. 156 157 if (ua.indexOf('Firefox') != -1) { 158 goog.userAgent.product.detectedFirefox_ = true; 159 } else if (ua.indexOf('Camino') != -1) { 160 goog.userAgent.product.detectedCamino_ = true; 161 } else if (ua.indexOf('iPhone') != -1 || ua.indexOf('iPod') != -1) { 162 goog.userAgent.product.detectedIphone_ = true; 163 } else if (ua.indexOf('iPad') != -1) { 164 goog.userAgent.product.detectedIpad_ = true; 165 } else if (ua.indexOf('Chrome') != -1) { 166 goog.userAgent.product.detectedChrome_ = true; 167 } else if (ua.indexOf('Android') != -1) { 168 goog.userAgent.product.detectedAndroid_ = true; 169 } else if (ua.indexOf('Safari') != -1) { 170 goog.userAgent.product.detectedSafari_ = true; 171 } 172 }; 173 174 if (!goog.userAgent.product.PRODUCT_KNOWN_) { 175 goog.userAgent.product.init_(); 176 } 177 178 179 /** 180 * Whether the code is running on the Opera web browser. 181 * @type {boolean} 182 */ 183 goog.userAgent.product.OPERA = goog.userAgent.OPERA; 184 185 186 /** 187 * Whether the code is running on an IE web browser. 188 * @type {boolean} 189 */ 190 goog.userAgent.product.IE = goog.userAgent.IE; 191 192 193 /** 194 * Whether the code is running on the Firefox web browser. 195 * @type {boolean} 196 */ 197 goog.userAgent.product.FIREFOX = goog.userAgent.product.PRODUCT_KNOWN_ ? 198 goog.userAgent.product.ASSUME_FIREFOX : 199 goog.userAgent.product.detectedFirefox_; 200 201 202 /** 203 * Whether the code is running on the Camino web browser. 204 * @type {boolean} 205 */ 206 goog.userAgent.product.CAMINO = goog.userAgent.product.PRODUCT_KNOWN_ ? 207 goog.userAgent.product.ASSUME_CAMINO : 208 goog.userAgent.product.detectedCamino_; 209 210 211 /** 212 * Whether the code is running on an iPhone or iPod touch. 213 * @type {boolean} 214 */ 215 goog.userAgent.product.IPHONE = goog.userAgent.product.PRODUCT_KNOWN_ ? 216 goog.userAgent.product.ASSUME_IPHONE : 217 goog.userAgent.product.detectedIphone_; 218 219 220 /** 221 * Whether the code is running on an iPad. 222 * @type {boolean} 223 */ 224 goog.userAgent.product.IPAD = goog.userAgent.product.PRODUCT_KNOWN_ ? 225 goog.userAgent.product.ASSUME_IPAD : 226 goog.userAgent.product.detectedIpad_; 227 228 229 /** 230 * Whether the code is running on the default browser on an Android phone. 231 * @type {boolean} 232 */ 233 goog.userAgent.product.ANDROID = goog.userAgent.product.PRODUCT_KNOWN_ ? 234 goog.userAgent.product.ASSUME_ANDROID : 235 goog.userAgent.product.detectedAndroid_; 236 237 238 /** 239 * Whether the code is running on the Chrome web browser. 240 * @type {boolean} 241 */ 242 goog.userAgent.product.CHROME = goog.userAgent.product.PRODUCT_KNOWN_ ? 243 goog.userAgent.product.ASSUME_CHROME : 244 goog.userAgent.product.detectedChrome_; 245 246 247 /** 248 * Whether the code is running on the Safari web browser. 249 * @type {boolean} 250 */ 251 goog.userAgent.product.SAFARI = goog.userAgent.product.PRODUCT_KNOWN_ ? 252 goog.userAgent.product.ASSUME_SAFARI : 253 goog.userAgent.product.detectedSafari_; lib/goog/useragent/product_isversion.js
1 // Copyright 2009 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Functions for understanding the version of the browser. 17 * This is pulled out of product.js to ensure that only builds that need 18 * this functionality actually get it, without having to rely on the compiler 19 * to strip out unneeded pieces. 20 * 21 * TODO(nnaze): Move to more appropriate filename/namespace. 22 * 23 */ 24 25 26 goog.provide('goog.userAgent.product.isVersion'); 27 28 29 goog.require('goog.userAgent.product'); 30 31 32 /** 33 * @return {string} The string that describes the version number of the user 34 * agent product. This is a string rather than a number because it may 35 * contain 'b', 'a', and so on. 36 * @private 37 */ 38 goog.userAgent.product.determineVersion_ = function() { 39 // All browsers have different ways to detect the version and they all have 40 // different naming schemes. 41 42 if (goog.userAgent.product.FIREFOX) { 43 // Firefox/2.0.0.1 or Firefox/3.5.3 44 return goog.userAgent.product.getFirstRegExpGroup_(/Firefox\/([0-9.]+)/); 45 } 46 47 if (goog.userAgent.product.IE || goog.userAgent.product.OPERA) { 48 return goog.userAgent.VERSION; 49 } 50 51 if (goog.userAgent.product.CHROME) { 52 // Chrome/4.0.223.1 53 return goog.userAgent.product.getFirstRegExpGroup_(/Chrome\/([0-9.]+)/); 54 } 55 56 if (goog.userAgent.product.SAFARI) { 57 // Version/5.0.3 58 // 59 // NOTE: Before version 3, Safari did not report a product version number. 60 // The product version number for these browsers will be the empty string. 61 // They may be differentiated by WebKit version number in goog.userAgent. 62 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/); 63 } 64 65 if (goog.userAgent.product.IPHONE || goog.userAgent.product.IPAD) { 66 // Mozilla/5.0 (iPod; U; CPU like Mac OS X; en) AppleWebKit/420.1 67 // (KHTML, like Gecko) Version/3.0 Mobile/3A100a Safari/419.3 68 // Version is the browser version, Mobile is the build number. We combine 69 // the version string with the build number: 3.0.3A100a for the example. 70 var arr = goog.userAgent.product.execRegExp_( 71 /Version\/(\S+).*Mobile\/(\S+)/); 72 if (arr) { 73 return arr[1] + '.' + arr[2]; 74 } 75 } else if (goog.userAgent.product.ANDROID) { 76 // Mozilla/5.0 (Linux; U; Android 0.5; en-us) AppleWebKit/522+ 77 // (KHTML, like Gecko) Safari/419.3 78 // 79 // Mozilla/5.0 (Linux; U; Android 1.0; en-us; dream) AppleWebKit/525.10+ 80 // (KHTML, like Gecko) Version/3.0.4 Mobile Safari/523.12.2 81 // 82 // Prefer Version number if present, else make do with the OS number 83 var version = goog.userAgent.product.getFirstRegExpGroup_( 84 /Android\s+([0-9.]+)/); 85 if (version) { 86 return version; 87 } 88 89 return goog.userAgent.product.getFirstRegExpGroup_(/Version\/([0-9.]+)/); 90 } else if (goog.userAgent.product.CAMINO) { 91 return goog.userAgent.product.getFirstRegExpGroup_(/Camino\/([0-9.]+)/); 92 } 93 94 return ''; 95 }; 96 97 98 /** 99 * Return the first group of the given regex. 100 * @param {!RegExp} re Regular expression with at least one group. 101 * @return {string} Contents of the first group or an empty string if no match. 102 * @private 103 */ 104 goog.userAgent.product.getFirstRegExpGroup_ = function(re) { 105 var arr = goog.userAgent.product.execRegExp_(re); 106 return arr ? arr[1] : ''; 107 }; 108 109 110 /** 111 * Run regexp's exec() on the userAgent string. 112 * @param {!RegExp} re Regular expression. 113 * @return {Array} A result array, or null for no match. 114 * @private 115 */ 116 goog.userAgent.product.execRegExp_ = function(re) { 117 return re.exec(goog.userAgent.getUserAgentString()); 118 }; 119 120 121 /** 122 * The version of the user agent. This is a string because it might contain 123 * 'b' (as in beta) as well as multiple dots. 124 * @type {string} 125 */ 126 goog.userAgent.product.VERSION = goog.userAgent.product.determineVersion_(); 127 128 129 /** 130 * Whether the user agent product version is higher or the same as the given 131 * version. 132 * 133 * @param {string|number} version The version to check. 134 * @return {boolean} Whether the user agent product version is higher or the 135 * same as the given version. 136 */ 137 goog.userAgent.product.isVersion = function(version) { 138 return goog.string.compareVersions( 139 goog.userAgent.product.VERSION, version) >= 0; 140 }; lib/goog/useragent/useragent.js
1 // Copyright 2006 The Closure Library Authors. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Rendering engine detection. 17 * @see <a href="http://www.useragentstring.com/">User agent strings</a> 18 * For information on the browser brand (such as Safari versus Chrome), see 19 * goog.userAgent.product. 20 * @see ../demos/useragent.html 21 */ 22 23 goog.provide('goog.userAgent'); 24 25 goog.require('goog.labs.userAgent.browser'); 26 goog.require('goog.labs.userAgent.engine'); 27 goog.require('goog.labs.userAgent.util'); 28 goog.require('goog.string'); 29 30 31 /** 32 * @define {boolean} Whether we know at compile-time that the browser is IE. 33 */ 34 goog.define('goog.userAgent.ASSUME_IE', false); 35 36 37 /** 38 * @define {boolean} Whether we know at compile-time that the browser is GECKO. 39 */ 40 goog.define('goog.userAgent.ASSUME_GECKO', false); 41 42 43 /** 44 * @define {boolean} Whether we know at compile-time that the browser is WEBKIT. 45 */ 46 goog.define('goog.userAgent.ASSUME_WEBKIT', false); 47 48 49 /** 50 * @define {boolean} Whether we know at compile-time that the browser is a 51 * mobile device running WebKit e.g. iPhone or Android. 52 */ 53 goog.define('goog.userAgent.ASSUME_MOBILE_WEBKIT', false); 54 55 56 /** 57 * @define {boolean} Whether we know at compile-time that the browser is OPERA. 58 */ 59 goog.define('goog.userAgent.ASSUME_OPERA', false); 60 61 62 /** 63 * @define {boolean} Whether the 64 * {@code goog.userAgent.isVersionOrHigher} 65 * function will return true for any version. 66 */ 67 goog.define('goog.userAgent.ASSUME_ANY_VERSION', false); 68 69 70 /** 71 * Whether we know the browser engine at compile-time. 72 * @type {boolean} 73 * @private 74 */ 75 goog.userAgent.BROWSER_KNOWN_ = 76 goog.userAgent.ASSUME_IE || 77 goog.userAgent.ASSUME_GECKO || 78 goog.userAgent.ASSUME_MOBILE_WEBKIT || 79 goog.userAgent.ASSUME_WEBKIT || 80 goog.userAgent.ASSUME_OPERA; 81 82 83 /** 84 * Returns the userAgent string for the current browser. 85 * 86 * @return {string} The userAgent string. 87 */ 88 goog.userAgent.getUserAgentString = function() { 89 return goog.labs.userAgent.util.getUserAgent(); 90 }; 91 92 93 /** 94 * TODO(nnaze): Change type to "Navigator" and update compilation targets. 95 * @return {Object} The native navigator object. 96 */ 97 goog.userAgent.getNavigator = function() { 98 // Need a local navigator reference instead of using the global one, 99 // to avoid the rare case where they reference different objects. 100 // (in a WorkerPool, for example). 101 return goog.global['navigator'] || null; 102 }; 103 104 105 /** 106 * Whether the user agent is Opera. 107 * @type {boolean} 108 */ 109 goog.userAgent.OPERA = goog.userAgent.BROWSER_KNOWN_ ? 110 goog.userAgent.ASSUME_OPERA : 111 goog.labs.userAgent.browser.isOpera(); 112 113 114 /** 115 * Whether the user agent is Internet Explorer. 116 * @type {boolean} 117 */ 118 goog.userAgent.IE = goog.userAgent.BROWSER_KNOWN_ ? 119 goog.userAgent.ASSUME_IE : 120 goog.labs.userAgent.browser.isIE(); 121 122 123 /** 124 * Whether the user agent is Gecko. Gecko is the rendering engine used by 125 * Mozilla, Firefox, and others. 126 * @type {boolean} 127 */ 128 goog.userAgent.GECKO = goog.userAgent.BROWSER_KNOWN_ ? 129 goog.userAgent.ASSUME_GECKO : 130 goog.labs.userAgent.engine.isGecko(); 131 132 133 /** 134 * Whether the user agent is WebKit. WebKit is the rendering engine that 135 * Safari, Android and others use. 136 * @type {boolean} 137 */ 138 goog.userAgent.WEBKIT = goog.userAgent.BROWSER_KNOWN_ ? 139 goog.userAgent.ASSUME_WEBKIT || goog.userAgent.ASSUME_MOBILE_WEBKIT : 140 goog.labs.userAgent.engine.isWebKit(); 141 142 143 /** 144 * Whether the user agent is running on a mobile device. 145 * 146 * This is a separate function so that the logic can be tested. 147 * 148 * TODO(nnaze): Investigate swapping in goog.labs.userAgent.device.isMobile(). 149 * 150 * @return {boolean} Whether the user agent is running on a mobile device. 151 * @private 152 */ 153 goog.userAgent.isMobile_ = function() { 154 return goog.userAgent.WEBKIT && 155 goog.labs.userAgent.util.matchUserAgent('Mobile'); 156 }; 157 158 159 /** 160 * Whether the user agent is running on a mobile device. 161 * 162 * TODO(nnaze): Consider deprecating MOBILE when labs.userAgent 163 * is promoted as the gecko/webkit logic is likely inaccurate. 164 * 165 * @type {boolean} 166 */ 167 goog.userAgent.MOBILE = goog.userAgent.ASSUME_MOBILE_WEBKIT || 168 goog.userAgent.isMobile_(); 169 170 171 /** 172 * Used while transitioning code to use WEBKIT instead. 173 * @type {boolean} 174 * @deprecated Use {@link goog.userAgent.product.SAFARI} instead. 175 * TODO(nicksantos): Delete this from goog.userAgent. 176 */ 177 goog.userAgent.SAFARI = goog.userAgent.WEBKIT; 178 179 180 /** 181 * @return {string} the platform (operating system) the user agent is running 182 * on. Default to empty string because navigator.platform may not be defined 183 * (on Rhino, for example). 184 * @private 185 */ 186 goog.userAgent.determinePlatform_ = function() { 187 var navigator = goog.userAgent.getNavigator(); 188 return navigator && navigator.platform || ''; 189 }; 190 191 192 /** 193 * The platform (operating system) the user agent is running on. Default to 194 * empty string because navigator.platform may not be defined (on Rhino, for 195 * example). 196 * @type {string} 197 */ 198 goog.userAgent.PLATFORM = goog.userAgent.determinePlatform_(); 199 200 201 /** 202 * @define {boolean} Whether the user agent is running on a Macintosh operating 203 * system. 204 */ 205 goog.define('goog.userAgent.ASSUME_MAC', false); 206 207 208 /** 209 * @define {boolean} Whether the user agent is running on a Windows operating 210 * system. 211 */ 212 goog.define('goog.userAgent.ASSUME_WINDOWS', false); 213 214 215 /** 216 * @define {boolean} Whether the user agent is running on a Linux operating 217 * system. 218 */ 219 goog.define('goog.userAgent.ASSUME_LINUX', false); 220 221 222 /** 223 * @define {boolean} Whether the user agent is running on a X11 windowing 224 * system. 225 */ 226 goog.define('goog.userAgent.ASSUME_X11', false); 227 228 229 /** 230 * @define {boolean} Whether the user agent is running on Android. 231 */ 232 goog.define('goog.userAgent.ASSUME_ANDROID', false); 233 234 235 /** 236 * @define {boolean} Whether the user agent is running on an iPhone. 237 */ 238 goog.define('goog.userAgent.ASSUME_IPHONE', false); 239 240 241 /** 242 * @define {boolean} Whether the user agent is running on an iPad. 243 */ 244 goog.define('goog.userAgent.ASSUME_IPAD', false); 245 246 247 /** 248 * @type {boolean} 249 * @private 250 */ 251 goog.userAgent.PLATFORM_KNOWN_ = 252 goog.userAgent.ASSUME_MAC || 253 goog.userAgent.ASSUME_WINDOWS || 254 goog.userAgent.ASSUME_LINUX || 255 goog.userAgent.ASSUME_X11 || 256 goog.userAgent.ASSUME_ANDROID || 257 goog.userAgent.ASSUME_IPHONE || 258 goog.userAgent.ASSUME_IPAD; 259 260 261 /** 262 * Initialize the goog.userAgent constants that define which platform the user 263 * agent is running on. 264 * @private 265 */ 266 goog.userAgent.initPlatform_ = function() { 267 /** 268 * Whether the user agent is running on a Macintosh operating system. 269 * @type {boolean} 270 * @private 271 */ 272 goog.userAgent.detectedMac_ = goog.string.contains(goog.userAgent.PLATFORM, 273 'Mac'); 274 275 /** 276 * Whether the user agent is running on a Windows operating system. 277 * @type {boolean} 278 * @private 279 */ 280 goog.userAgent.detectedWindows_ = goog.string.contains( 281 goog.userAgent.PLATFORM, 'Win'); 282 283 /** 284 * Whether the user agent is running on a Linux operating system. 285 * @type {boolean} 286 * @private 287 */ 288 goog.userAgent.detectedLinux_ = goog.string.contains(goog.userAgent.PLATFORM, 289 'Linux'); 290 291 /** 292 * Whether the user agent is running on a X11 windowing system. 293 * @type {boolean} 294 * @private 295 */ 296 goog.userAgent.detectedX11_ = !!goog.userAgent.getNavigator() && 297 goog.string.contains(goog.userAgent.getNavigator()['appVersion'] || '', 298 'X11'); 299 300 // Need user agent string for Android/IOS detection 301 var ua = goog.userAgent.getUserAgentString(); 302 303 /** 304 * Whether the user agent is running on Android. 305 * @type {boolean} 306 * @private 307 */ 308 goog.userAgent.detectedAndroid_ = !!ua && 309 goog.string.contains(ua, 'Android'); 310 311 /** 312 * Whether the user agent is running on an iPhone. 313 * @type {boolean} 314 * @private 315 */ 316 goog.userAgent.detectedIPhone_ = !!ua && goog.string.contains(ua, 'iPhone'); 317 318 /** 319 * Whether the user agent is running on an iPad. 320 * @type {boolean} 321 * @private 322 */ 323 goog.userAgent.detectedIPad_ = !!ua && goog.string.contains(ua, 'iPad'); 324 }; 325 326 327 if (!goog.userAgent.PLATFORM_KNOWN_) { 328 goog.userAgent.initPlatform_(); 329 } 330 331 332 /** 333 * Whether the user agent is running on a Macintosh operating system. 334 * @type {boolean} 335 */ 336 goog.userAgent.MAC = goog.userAgent.PLATFORM_KNOWN_ ? 337 goog.userAgent.ASSUME_MAC : goog.userAgent.detectedMac_; 338 339 340 /** 341 * Whether the user agent is running on a Windows operating system. 342 * @type {boolean} 343 */ 344 goog.userAgent.WINDOWS = goog.userAgent.PLATFORM_KNOWN_ ? 345 goog.userAgent.ASSUME_WINDOWS : goog.userAgent.detectedWindows_; 346 347 348 /** 349 * Whether the user agent is running on a Linux operating system. 350 * @type {boolean} 351 */ 352 goog.userAgent.LINUX = goog.userAgent.PLATFORM_KNOWN_ ? 353 goog.userAgent.ASSUME_LINUX : goog.userAgent.detectedLinux_; 354 355 356 /** 357 * Whether the user agent is running on a X11 windowing system. 358 * @type {boolean} 359 */ 360 goog.userAgent.X11 = goog.userAgent.PLATFORM_KNOWN_ ? 361 goog.userAgent.ASSUME_X11 : goog.userAgent.detectedX11_; 362 363 364 /** 365 * Whether the user agent is running on Android. 366 * @type {boolean} 367 */ 368 goog.userAgent.ANDROID = goog.userAgent.PLATFORM_KNOWN_ ? 369 goog.userAgent.ASSUME_ANDROID : goog.userAgent.detectedAndroid_; 370 371 372 /** 373 * Whether the user agent is running on an iPhone. 374 * @type {boolean} 375 */ 376 goog.userAgent.IPHONE = goog.userAgent.PLATFORM_KNOWN_ ? 377 goog.userAgent.ASSUME_IPHONE : goog.userAgent.detectedIPhone_; 378 379 380 /** 381 * Whether the user agent is running on an iPad. 382 * @type {boolean} 383 */ 384 goog.userAgent.IPAD = goog.userAgent.PLATFORM_KNOWN_ ? 385 goog.userAgent.ASSUME_IPAD : goog.userAgent.detectedIPad_; 386 387 388 /** 389 * @return {string} The string that describes the version number of the user 390 * agent. 391 * @private 392 */ 393 goog.userAgent.determineVersion_ = function() { 394 // All browsers have different ways to detect the version and they all have 395 // different naming schemes. 396 397 // version is a string rather than a number because it may contain 'b', 'a', 398 // and so on. 399 var version = '', re; 400 401 if (goog.userAgent.OPERA && goog.global['opera']) { 402 var operaVersion = goog.global['opera'].version; 403 return goog.isFunction(operaVersion) ? operaVersion() : operaVersion; 404 } 405 406 if (goog.userAgent.GECKO) { 407 re = /rv\:([^\);]+)(\)|;)/; 408 } else if (goog.userAgent.IE) { 409 re = /\b(?:MSIE|rv)[: ]([^\);]+)(\)|;)/; 410 } else if (goog.userAgent.WEBKIT) { 411 // WebKit/125.4 412 re = /WebKit\/(\S+)/; 413 } 414 415 if (re) { 416 var arr = re.exec(goog.userAgent.getUserAgentString()); 417 version = arr ? arr[1] : ''; 418 } 419 420 if (goog.userAgent.IE) { 421 // IE9 can be in document mode 9 but be reporting an inconsistent user agent 422 // version. If it is identifying as a version lower than 9 we take the 423 // documentMode as the version instead. IE8 has similar behavior. 424 // It is recommended to set the X-UA-Compatible header to ensure that IE9 425 // uses documentMode 9. 426 var docMode = goog.userAgent.getDocumentMode_(); 427 if (docMode > parseFloat(version)) { 428 return String(docMode); 429 } 430 } 431 432 return version; 433 }; 434 435 436 /** 437 * @return {number|undefined} Returns the document mode (for testing). 438 * @private 439 */ 440 goog.userAgent.getDocumentMode_ = function() { 441 // NOTE(user): goog.userAgent may be used in context where there is no DOM. 442 var doc = goog.global['document']; 443 return doc ? doc['documentMode'] : undefined; 444 }; 445 446 447 /** 448 * The version of the user agent. This is a string because it might contain 449 * 'b' (as in beta) as well as multiple dots. 450 * @type {string} 451 */ 452 goog.userAgent.VERSION = goog.userAgent.determineVersion_(); 453 454 455 /** 456 * Compares two version numbers. 457 * 458 * @param {string} v1 Version of first item. 459 * @param {string} v2 Version of second item. 460 * 461 * @return {number} 1 if first argument is higher 462 * 0 if arguments are equal 463 * -1 if second argument is higher. 464 * @deprecated Use goog.string.compareVersions. 465 */ 466 goog.userAgent.compare = function(v1, v2) { 467 return goog.string.compareVersions(v1, v2); 468 }; 469 470 471 /** 472 * Cache for {@link goog.userAgent.isVersionOrHigher}. 473 * Calls to compareVersions are surprisingly expensive and, as a browser's 474 * version number is unlikely to change during a session, we cache the results. 475 * @const 476 * @private 477 */ 478 goog.userAgent.isVersionOrHigherCache_ = {}; 479 480 481 /** 482 * Whether the user agent version is higher or the same as the given version. 483 * NOTE: When checking the version numbers for Firefox and Safari, be sure to 484 * use the engine's version, not the browser's version number. For example, 485 * Firefox 3.0 corresponds to Gecko 1.9 and Safari 3.0 to Webkit 522.11. 486 * Opera and Internet Explorer versions match the product release number.<br> 487 * @see <a href="http://en.wikipedia.org/wiki/Safari_version_history"> 488 * Webkit</a> 489 * @see <a href="http://en.wikipedia.org/wiki/Gecko_engine">Gecko</a> 490 * 491 * @param {string|number} version The version to check. 492 * @return {boolean} Whether the user agent version is higher or the same as 493 * the given version. 494 */ 495 goog.userAgent.isVersionOrHigher = function(version) { 496 return goog.userAgent.ASSUME_ANY_VERSION || 497 goog.userAgent.isVersionOrHigherCache_[version] || 498 (goog.userAgent.isVersionOrHigherCache_[version] = 499 goog.string.compareVersions(goog.userAgent.VERSION, version) >= 0); 500 }; 501 502 503 /** 504 * Deprecated alias to {@code goog.userAgent.isVersionOrHigher}. 505 * @param {string|number} version The version to check. 506 * @return {boolean} Whether the user agent version is higher or the same as 507 * the given version. 508 * @deprecated Use goog.userAgent.isVersionOrHigher(). 509 */ 510 goog.userAgent.isVersion = goog.userAgent.isVersionOrHigher; 511 512 513 /** 514 * Whether the IE effective document mode is higher or the same as the given 515 * document mode version. 516 * NOTE: Only for IE, return false for another browser. 517 * 518 * @param {number} documentMode The document mode version to check. 519 * @return {boolean} Whether the IE effective document mode is higher or the 520 * same as the given version. 521 */ 522 goog.userAgent.isDocumentModeOrHigher = function(documentMode) { 523 return goog.userAgent.IE && goog.userAgent.DOCUMENT_MODE >= documentMode; 524 }; 525 526 527 /** 528 * Deprecated alias to {@code goog.userAgent.isDocumentModeOrHigher}. 529 * @param {number} version The version to check. 530 * @return {boolean} Whether the IE effective document mode is higher or the 531 * same as the given version. 532 * @deprecated Use goog.userAgent.isDocumentModeOrHigher(). 533 */ 534 goog.userAgent.isDocumentMode = goog.userAgent.isDocumentModeOrHigher; 535 536 537 /** 538 * For IE version < 7, documentMode is undefined, so attempt to use the 539 * CSS1Compat property to see if we are in standards mode. If we are in 540 * standards mode, treat the browser version as the document mode. Otherwise, 541 * IE is emulating version 5. 542 * @type {number|undefined} 543 * @const 544 */ 545 goog.userAgent.DOCUMENT_MODE = (function() { 546 var doc = goog.global['document']; 547 if (!doc || !goog.userAgent.IE) { 548 return undefined; 549 } 550 var mode = goog.userAgent.getDocumentMode_(); 551 return mode || (doc['compatMode'] == 'CSS1Compat' ? 552 parseInt(goog.userAgent.VERSION, 10) : 5); 553 })(); lib/webdriver/abstractbuilder.js
1 // Copyright 2012 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.AbstractBuilder'); 16 17 goog.require('webdriver.Capabilities'); 18 goog.require('webdriver.process'); 19 20 21 22 /** 23 * Creates new {@code webdriver.WebDriver} clients. Upon instantiation, each 24 * Builder will configure itself based on the following environment variables: 25 * <dl> 26 * <dt>{@code webdriver.AbstractBuilder.SERVER_URL_ENV}</dt> 27 * <dd>Defines the remote WebDriver server that should be used for command 28 * command execution; may be overridden using 29 * {@code webdriver.AbstractBuilder.prototype.usingServer}.</dd> 30 * </dl> 31 * @constructor 32 */ 33 webdriver.AbstractBuilder = function() { 34 35 /** 36 * URL of the remote server to use for new clients; initialized from the 37 * value of the {@link webdriver.AbstractBuilder.SERVER_URL_ENV} environment 38 * variable, but may be overridden using 39 * {@link webdriver.AbstractBuilder#usingServer}. 40 * @private {string} 41 */ 42 this.serverUrl_ = webdriver.process.getEnv( 43 webdriver.AbstractBuilder.SERVER_URL_ENV); 44 45 /** 46 * The desired capabilities to use when creating a new session. 47 * @private {!webdriver.Capabilities} 48 */ 49 this.capabilities_ = new webdriver.Capabilities(); 50 }; 51 52 53 /** 54 * Environment variable that defines the URL of the WebDriver server that 55 * should be used for all new WebDriver clients. This setting may be overridden 56 * using {@code #usingServer(url)}. 57 * @type {string} 58 * @const 59 * @see webdriver.process.getEnv 60 */ 61 webdriver.AbstractBuilder.SERVER_URL_ENV = 'wdurl'; 62 63 64 /** 65 * The default URL of the WebDriver server to use if 66 * {@link webdriver.AbstractBuilder.SERVER_URL_ENV} is not set. 67 * @type {string} 68 * @const 69 */ 70 webdriver.AbstractBuilder.DEFAULT_SERVER_URL = 'http://localhost:4444/wd/hub'; 71 72 73 /** 74 * Configures which WebDriver server should be used for new sessions. Overrides 75 * the value loaded from the {@link webdriver.AbstractBuilder.SERVER_URL_ENV} 76 * upon creation of this instance. 77 * @param {string} url URL of the server to use. 78 * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling. 79 */ 80 webdriver.AbstractBuilder.prototype.usingServer = function(url) { 81 this.serverUrl_ = url; 82 return this; 83 }; 84 85 86 /** 87 * @return {string} The URL of the WebDriver server this instance is configured 88 * to use. 89 */ 90 webdriver.AbstractBuilder.prototype.getServerUrl = function() { 91 return this.serverUrl_; 92 }; 93 94 95 /** 96 * Sets the desired capabilities when requesting a new session. This will 97 * overwrite any previously set desired capabilities. 98 * @param {!(Object|webdriver.Capabilities)} capabilities The desired 99 * capabilities for a new session. 100 * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling. 101 */ 102 webdriver.AbstractBuilder.prototype.withCapabilities = function(capabilities) { 103 this.capabilities_ = new webdriver.Capabilities(capabilities); 104 return this; 105 }; 106 107 108 /** 109 * @return {!webdriver.Capabilities} The current desired capabilities for this 110 * builder. 111 */ 112 webdriver.AbstractBuilder.prototype.getCapabilities = function() { 113 return this.capabilities_; 114 }; 115 116 117 /** 118 * Builds a new {@link webdriver.WebDriver} instance using this builder's 119 * current configuration. 120 * @return {!webdriver.WebDriver} A new WebDriver client. 121 */ 122 webdriver.AbstractBuilder.prototype.build = goog.abstractMethod; lib/webdriver/actionsequence.js
1 // Copyright 2012 Selenium comitters 2 // Copyright 2012 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 goog.provide('webdriver.ActionSequence'); 17 18 goog.require('goog.array'); 19 goog.require('webdriver.Button'); 20 goog.require('webdriver.Command'); 21 goog.require('webdriver.CommandName'); 22 goog.require('webdriver.Key'); 23 24 25 26 /** 27 * Class for defining sequences of complex user interactions. Each sequence 28 * will not be executed until {@link #perform} is called. 29 * 30 * <p>Example:<pre><code> 31 * new webdriver.ActionSequence(driver). 32 * keyDown(webdriver.Key.SHIFT). 33 * click(element1). 34 * click(element2). 35 * dragAndDrop(element3, element4). 36 * keyUp(webdriver.Key.SHIFT). 37 * perform(); 38 * </pre></code> 39 * 40 * @param {!webdriver.WebDriver} driver The driver instance to use. 41 * @constructor 42 */ 43 webdriver.ActionSequence = function(driver) { 44 45 /** @private {!webdriver.WebDriver} */ 46 this.driver_ = driver; 47 48 /** @private {!Array.<{description: string, command: !webdriver.Command}>} */ 49 this.actions_ = []; 50 }; 51 52 53 /** 54 * Schedules an action to be executed each time {@link #perform} is called on 55 * this instance. 56 * @param {string} description A description of the command. 57 * @param {!webdriver.Command} command The command. 58 * @private 59 */ 60 webdriver.ActionSequence.prototype.schedule_ = function(description, command) { 61 this.actions_.push({ 62 description: description, 63 command: command 64 }); 65 }; 66 67 68 /** 69 * Executes this action sequence. 70 * @return {!webdriver.promise.Promise} A promise that will be resolved once 71 * this sequence has completed. 72 */ 73 webdriver.ActionSequence.prototype.perform = function() { 74 // Make a protected copy of the scheduled actions. This will protect against 75 // users defining additional commands before this sequence is actually 76 // executed. 77 var actions = goog.array.clone(this.actions_); 78 var driver = this.driver_; 79 return driver.controlFlow().execute(function() { 80 goog.array.forEach(actions, function(action) { 81 driver.schedule(action.command, action.description); 82 }); 83 }, 'ActionSequence.perform'); 84 }; 85 86 87 /** 88 * Moves the mouse. The location to move to may be specified in terms of the 89 * mouse's current location, an offset relative to the top-left corner of an 90 * element, or an element (in which case the middle of the element is used). 91 * @param {(!webdriver.WebElement|{x: number, y: number})} location The 92 * location to drag to, as either another WebElement or an offset in pixels. 93 * @param {{x: number, y: number}=} opt_offset If the target {@code location} 94 * is defined as a {@link webdriver.WebElement}, this parameter defines an 95 * offset within that element. The offset should be specified in pixels 96 * relative to the top-left corner of the element's bounding box. If 97 * omitted, the element's center will be used as the target offset. 98 * @return {!webdriver.ActionSequence} A self reference. 99 */ 100 webdriver.ActionSequence.prototype.mouseMove = function(location, opt_offset) { 101 var command = new webdriver.Command(webdriver.CommandName.MOVE_TO); 102 103 if (goog.isNumber(location.x)) { 104 setOffset(/** @type {{x: number, y: number}} */(location)); 105 } else { 106 // The interactions API expect the element ID to be encoded as a simple 107 // string, not the usual JSON object. 108 var id = /** @type {!webdriver.WebElement} */ (location).toWireValue(). 109 then(function(value) { 110 return value['ELEMENT']; 111 }); 112 command.setParameter('element', id); 113 if (opt_offset) { 114 setOffset(opt_offset); 115 } 116 } 117 118 this.schedule_('mouseMove', command); 119 return this; 120 121 /** @param {{x: number, y: number}} offset The offset to use. */ 122 function setOffset(offset) { 123 command.setParameter('xoffset', offset.x || 0); 124 command.setParameter('yoffset', offset.y || 0); 125 } 126 }; 127 128 129 /** 130 * Schedules a mouse action. 131 * @param {string} description A simple descriptive label for the scheduled 132 * action. 133 * @param {!webdriver.CommandName} commandName The name of the command. 134 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either 135 * the element to interact with or the button to click with. 136 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor 137 * button is specified. 138 * @param {webdriver.Button=} opt_button The button to use. Defaults to 139 * {@link webdriver.Button.LEFT}. Ignored if the previous argument is 140 * provided as a button. 141 * @return {!webdriver.ActionSequence} A self reference. 142 * @private 143 */ 144 webdriver.ActionSequence.prototype.scheduleMouseAction_ = function( 145 description, commandName, opt_elementOrButton, opt_button) { 146 var button; 147 if (goog.isNumber(opt_elementOrButton)) { 148 button = opt_elementOrButton; 149 } else { 150 if (opt_elementOrButton) { 151 this.mouseMove( 152 /** @type {!webdriver.WebElement} */ (opt_elementOrButton)); 153 } 154 button = goog.isDef(opt_button) ? opt_button : webdriver.Button.LEFT; 155 } 156 157 var command = new webdriver.Command(commandName). 158 setParameter('button', button); 159 this.schedule_(description, command); 160 return this; 161 }; 162 163 164 /** 165 * Presses a mouse button. The mouse button will not be released until 166 * {@link #mouseUp} is called, regardless of whether that call is made in this 167 * sequence or another. The behavior for out-of-order events (e.g. mouseDown, 168 * click) is undefined. 169 * 170 * <p>If an element is provided, the mouse will first be moved to the center 171 * of that element. This is equivalent to: 172 * <pre><code>sequence.mouseMove(element).mouseDown()</code></pre> 173 * 174 * <p>Warning: this method currently only supports the left mouse button. See 175 * http://code.google.com/p/selenium/issues/detail?id=4047 176 * 177 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either 178 * the element to interact with or the button to click with. 179 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor 180 * button is specified. 181 * @param {webdriver.Button=} opt_button The button to use. Defaults to 182 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the 183 * first argument. 184 * @return {!webdriver.ActionSequence} A self reference. 185 */ 186 webdriver.ActionSequence.prototype.mouseDown = function(opt_elementOrButton, 187 opt_button) { 188 return this.scheduleMouseAction_('mouseDown', 189 webdriver.CommandName.MOUSE_DOWN, opt_elementOrButton, opt_button); 190 }; 191 192 193 /** 194 * Releases a mouse button. Behavior is undefined for calling this function 195 * without a previous call to {@link #mouseDown}. 196 * 197 * <p>If an element is provided, the mouse will first be moved to the center 198 * of that element. This is equivalent to: 199 * <pre><code>sequence.mouseMove(element).mouseUp()</code></pre> 200 * 201 * <p>Warning: this method currently only supports the left mouse button. See 202 * http://code.google.com/p/selenium/issues/detail?id=4047 203 * 204 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either 205 * the element to interact with or the button to click with. 206 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor 207 * button is specified. 208 * @param {webdriver.Button=} opt_button The button to use. Defaults to 209 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the 210 * first argument. 211 * @return {!webdriver.ActionSequence} A self reference. 212 */ 213 webdriver.ActionSequence.prototype.mouseUp = function(opt_elementOrButton, 214 opt_button) { 215 return this.scheduleMouseAction_('mouseUp', 216 webdriver.CommandName.MOUSE_UP, opt_elementOrButton, opt_button); 217 }; 218 219 220 /** 221 * Convenience function for performing a "drag and drop" manuever. The target 222 * element may be moved to the location of another element, or by an offset (in 223 * pixels). 224 * @param {!webdriver.WebElement} element The element to drag. 225 * @param {(!webdriver.WebElement|{x: number, y: number})} location The 226 * location to drag to, either as another WebElement or an offset in pixels. 227 * @return {!webdriver.ActionSequence} A self reference. 228 */ 229 webdriver.ActionSequence.prototype.dragAndDrop = function(element, location) { 230 return this.mouseDown(element).mouseMove(location).mouseUp(); 231 }; 232 233 234 /** 235 * Clicks a mouse button. 236 * 237 * <p>If an element is provided, the mouse will first be moved to the center 238 * of that element. This is equivalent to: 239 * <pre><code>sequence.mouseMove(element).click()</code></pre> 240 * 241 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either 242 * the element to interact with or the button to click with. 243 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor 244 * button is specified. 245 * @param {webdriver.Button=} opt_button The button to use. Defaults to 246 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the 247 * first argument. 248 * @return {!webdriver.ActionSequence} A self reference. 249 */ 250 webdriver.ActionSequence.prototype.click = function(opt_elementOrButton, 251 opt_button) { 252 return this.scheduleMouseAction_('click', 253 webdriver.CommandName.CLICK, opt_elementOrButton, opt_button); 254 }; 255 256 257 /** 258 * Double-clicks a mouse button. 259 * 260 * <p>If an element is provided, the mouse will first be moved to the center of 261 * that element. This is equivalent to: 262 * <pre><code>sequence.mouseMove(element).doubleClick()</code></pre> 263 * 264 * <p>Warning: this method currently only supports the left mouse button. See 265 * http://code.google.com/p/selenium/issues/detail?id=4047 266 * 267 * @param {(webdriver.WebElement|webdriver.Button)=} opt_elementOrButton Either 268 * the element to interact with or the button to click with. 269 * Defaults to {@link webdriver.Button.LEFT} if neither an element nor 270 * button is specified. 271 * @param {webdriver.Button=} opt_button The button to use. Defaults to 272 * {@link webdriver.Button.LEFT}. Ignored if a button is provided as the 273 * first argument. 274 * @return {!webdriver.ActionSequence} A self reference. 275 */ 276 webdriver.ActionSequence.prototype.doubleClick = function(opt_elementOrButton, 277 opt_button) { 278 return this.scheduleMouseAction_('doubleClick', 279 webdriver.CommandName.DOUBLE_CLICK, opt_elementOrButton, opt_button); 280 }; 281 282 283 /** 284 * Schedules a keyboard action. 285 * @param {string} description A simple descriptive label for the scheduled 286 * action. 287 * @param {!Array.<(string|!webdriver.Key)>} keys The keys to send. 288 * @return {!webdriver.ActionSequence} A self reference. 289 * @private 290 */ 291 webdriver.ActionSequence.prototype.scheduleKeyboardAction_ = function( 292 description, keys) { 293 var command = 294 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ACTIVE_ELEMENT). 295 setParameter('value', keys); 296 this.schedule_(description, command); 297 return this; 298 }; 299 300 301 /** 302 * Checks that a key is a modifier key. 303 * @param {!webdriver.Key} key The key to check. 304 * @throws {Error} If the key is not a modifier key. 305 * @private 306 */ 307 webdriver.ActionSequence.checkModifierKey_ = function(key) { 308 if (key !== webdriver.Key.ALT && key !== webdriver.Key.CONTROL && 309 key !== webdriver.Key.SHIFT && key !== webdriver.Key.COMMAND) { 310 throw Error('Not a modifier key'); 311 } 312 }; 313 314 315 /** 316 * Performs a modifier key press. The modifier key is <em>not released</em> 317 * until {@link #keyUp} or {@link #sendKeys} is called. The key press will be 318 * targetted at the currently focused element. 319 * @param {!webdriver.Key} key The modifier key to push. Must be one of 320 * {ALT, CONTROL, SHIFT, COMMAND, META}. 321 * @return {!webdriver.ActionSequence} A self reference. 322 * @throws {Error} If the key is not a valid modifier key. 323 */ 324 webdriver.ActionSequence.prototype.keyDown = function(key) { 325 webdriver.ActionSequence.checkModifierKey_(key); 326 return this.scheduleKeyboardAction_('keyDown', [key]); 327 }; 328 329 330 /** 331 * Performs a modifier key release. The release is targetted at the currently 332 * focused element. 333 * @param {!webdriver.Key} key The modifier key to release. Must be one of 334 * {ALT, CONTROL, SHIFT, COMMAND, META}. 335 * @return {!webdriver.ActionSequence} A self reference. 336 * @throws {Error} If the key is not a valid modifier key. 337 */ 338 webdriver.ActionSequence.prototype.keyUp = function(key) { 339 webdriver.ActionSequence.checkModifierKey_(key); 340 return this.scheduleKeyboardAction_('keyUp', [key]); 341 }; 342 343 344 /** 345 * Simulates typing multiple keys. Each modifier key encountered in the 346 * sequence will not be released until it is encountered again. All key events 347 * will be targetted at the currently focused element. 348 * @param {...(string|!webdriver.Key|!Array.<(string|!webdriver.Key)>)} var_args 349 * The keys to type. 350 * @return {!webdriver.ActionSequence} A self reference. 351 * @throws {Error} If the key is not a valid modifier key. 352 */ 353 webdriver.ActionSequence.prototype.sendKeys = function(var_args) { 354 var keys = goog.array.flatten(goog.array.slice(arguments, 0)); 355 return this.scheduleKeyboardAction_('sendKeys', keys); 356 }; lib/webdriver/builder.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.Builder'); 16 17 goog.require('goog.userAgent'); 18 goog.require('webdriver.AbstractBuilder'); 19 goog.require('webdriver.FirefoxDomExecutor'); 20 goog.require('webdriver.WebDriver'); 21 goog.require('webdriver.http.CorsClient'); 22 goog.require('webdriver.http.Executor'); 23 goog.require('webdriver.http.XhrClient'); 24 goog.require('webdriver.process'); 25 26 27 28 /** 29 * @constructor 30 * @extends {webdriver.AbstractBuilder} 31 */ 32 webdriver.Builder = function() { 33 goog.base(this); 34 35 /** 36 * ID of an existing WebDriver session that new clients should use. 37 * Initialized from the value of the 38 * {@link webdriver.AbstractBuilder.SESSION_ID_ENV} environment variable, but 39 * may be overridden using 40 * {@link webdriver.AbstractBuilder#usingSession}. 41 * @private {string} 42 */ 43 this.sessionId_ = 44 webdriver.process.getEnv(webdriver.Builder.SESSION_ID_ENV); 45 }; 46 goog.inherits(webdriver.Builder, webdriver.AbstractBuilder); 47 48 49 /** 50 * Environment variable that defines the session ID of an existing WebDriver 51 * session to use when creating clients. If set, all new Builder instances will 52 * default to creating clients that use this session. To create a new session, 53 * use {@code #useExistingSession(boolean)}. The use of this environment 54 * variable requires that {@link webdriver.AbstractBuilder.SERVER_URL_ENV} also 55 * be set. 56 * @type {string} 57 * @const 58 * @see webdriver.process.getEnv 59 */ 60 webdriver.Builder.SESSION_ID_ENV = 'wdsid'; 61 62 63 /** 64 * Configures the builder to create a client that will use an existing WebDriver 65 * session. 66 * @param {string} id The existing session ID to use. 67 * @return {!webdriver.AbstractBuilder} This Builder instance for chain calling. 68 */ 69 webdriver.Builder.prototype.usingSession = function(id) { 70 this.sessionId_ = id; 71 return this; 72 }; 73 74 75 /** 76 * @return {string} The ID of the session, if any, this builder is configured 77 * to reuse. 78 */ 79 webdriver.Builder.prototype.getSession = function() { 80 return this.sessionId_; 81 }; 82 83 84 /** 85 * @override 86 */ 87 webdriver.Builder.prototype.build = function() { 88 if (goog.userAgent.GECKO && document.readyState != 'complete') { 89 throw Error('Cannot create driver instance before window.onload'); 90 } 91 92 var executor; 93 94 if (webdriver.FirefoxDomExecutor.isAvailable()) { 95 executor = new webdriver.FirefoxDomExecutor(); 96 return webdriver.WebDriver.createSession(executor, this.getCapabilities()); 97 } else { 98 var url = this.getServerUrl() || 99 webdriver.AbstractBuilder.DEFAULT_SERVER_URL; 100 var client; 101 if (url[0] == '/') { 102 var origin = window.location.origin || 103 (window.location.protocol + '//' + window.location.host); 104 client = new webdriver.http.XhrClient(origin + url); 105 } else { 106 client = new webdriver.http.CorsClient(url); 107 } 108 executor = new webdriver.http.Executor(client); 109 110 if (this.getSession()) { 111 return webdriver.WebDriver.attachToSession(executor, this.getSession()); 112 } else { 113 throw new Error('Unable to create a new client for this browser. The ' + 114 'WebDriver session ID has not been defined.'); 115 } 116 } 117 }; lib/webdriver/button.js
1 // Copyright 2012 Selenium comitters 2 // Copyright 2012 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 goog.provide('webdriver.Button'); 17 18 19 /** 20 * Enumeration of the buttons used in the advanced interactions API. 21 * @enum {number} 22 */ 23 webdriver.Button = { 24 LEFT: 0, 25 MIDDLE: 1, 26 RIGHT: 2 27 }; lib/webdriver/capabilities.js
1 // Copyright 2013 Software Freedom Conservancy 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Defines the webdriver.Capabilities class. 17 */ 18 19 goog.provide('webdriver.Browser'); 20 goog.provide('webdriver.Capabilities'); 21 goog.provide('webdriver.Capability'); 22 23 24 25 /** 26 * Recognized browser names. 27 * @enum {string} 28 */ 29 webdriver.Browser = { 30 ANDROID: 'android', 31 CHROME: 'chrome', 32 FIREFOX: 'firefox', 33 INTERNET_EXPLORER: 'internet explorer', 34 IPAD: 'iPad', 35 IPHONE: 'iPhone', 36 OPERA: 'opera', 37 PHANTOM_JS: 'phantomjs', 38 SAFARI: 'safari', 39 HTMLUNIT: 'htmlunit' 40 }; 41 42 43 44 /** 45 * Common webdriver capability keys. 46 * @enum {string} 47 */ 48 webdriver.Capability = { 49 50 /** 51 * Indicates whether a driver should accept all SSL certs by default. This 52 * capability only applies when requesting a new session. To query whether 53 * a driver can handle insecure SSL certs, see 54 * {@link webdriver.Capability.SECURE_SSL}. 55 */ 56 ACCEPT_SSL_CERTS: 'acceptSslCerts', 57 58 59 /** 60 * The browser name. Common browser names are defined in the 61 * {@link webdriver.Browser} enum. 62 */ 63 BROWSER_NAME: 'browserName', 64 65 /** 66 * Whether the driver is capable of handling modal alerts (e.g. alert, 67 * confirm, prompt). To define how a driver <i>should</i> handle alerts, 68 * use {@link webdriver.Capability.UNEXPECTED_ALERT_BEHAVIOR}. 69 */ 70 HANDLES_ALERTS: 'handlesAlerts', 71 72 /** 73 * Key for the logging driver logging preferences. 74 */ 75 LOGGING_PREFS: 'loggingPrefs', 76 77 /** 78 * Describes the platform the browser is running on. Will be one of 79 * ANDROID, IOS, LINUX, MAC, UNIX, or WINDOWS. When <i>requesting</i> a 80 * session, ANY may be used to indicate no platform preference (this is 81 * semantically equivalent to omitting the platform capability). 82 */ 83 PLATFORM: 'platform', 84 85 /** 86 * Describes the proxy configuration to use for a new WebDriver session. 87 */ 88 PROXY: 'proxy', 89 90 /** Whether the driver supports changing the brower's orientation. */ 91 ROTATABLE: 'rotatable', 92 93 /** 94 * Whether a driver is only capable of handling secure SSL certs. To request 95 * that a driver accept insecure SSL certs by default, use 96 * {@link webdriver.Capability.ACCEPT_SSL_CERTS}. 97 */ 98 SECURE_SSL: 'secureSsl', 99 100 /** Whether the driver supports manipulating the app cache. */ 101 SUPPORTS_APPLICATION_CACHE: 'applicationCacheEnabled', 102 103 /** 104 * Whether the driver supports controlling the browser's internet 105 * connectivity. 106 */ 107 SUPPORTS_BROWSER_CONNECTION: 'browserConnectionEnabled', 108 109 /** Whether the driver supports locating elements with CSS selectors. */ 110 SUPPORTS_CSS_SELECTORS: 'cssSelectorsEnabled', 111 112 /** Whether the browser supports JavaScript. */ 113 SUPPORTS_JAVASCRIPT: 'javascriptEnabled', 114 115 /** Whether the driver supports controlling the browser's location info. */ 116 SUPPORTS_LOCATION_CONTEXT: 'locationContextEnabled', 117 118 /** Whether the driver supports taking screenshots. */ 119 TAKES_SCREENSHOT: 'takesScreenshot', 120 121 /** 122 * Defines how the driver should handle unexpected alerts. The value should 123 * be one of "accept", "dismiss", or "ignore. 124 */ 125 UNEXPECTED_ALERT_BEHAVIOR: 'unexpectedAlertBehavior', 126 127 /** Defines the browser version. */ 128 VERSION: 'version' 129 }; 130 131 132 133 /** 134 * @param {(webdriver.Capabilities|Object)=} opt_other Another set of 135 * capabilities to merge into this instance. 136 * @constructor 137 */ 138 webdriver.Capabilities = function(opt_other) { 139 140 /** @private {!Object} */ 141 this.caps_ = {}; 142 143 if (opt_other) { 144 this.merge(opt_other); 145 } 146 }; 147 148 149 /** 150 * @return {!webdriver.Capabilities} A basic set of capabilities for Android. 151 */ 152 webdriver.Capabilities.android = function() { 153 return new webdriver.Capabilities(). 154 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.ANDROID). 155 set(webdriver.Capability.PLATFORM, 'ANDROID'); 156 }; 157 158 159 /** 160 * @return {!webdriver.Capabilities} A basic set of capabilities for Chrome. 161 */ 162 webdriver.Capabilities.chrome = function() { 163 return new webdriver.Capabilities(). 164 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.CHROME); 165 }; 166 167 168 /** 169 * @return {!webdriver.Capabilities} A basic set of capabilities for Firefox. 170 */ 171 webdriver.Capabilities.firefox = function() { 172 return new webdriver.Capabilities(). 173 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.FIREFOX); 174 }; 175 176 177 /** 178 * @return {!webdriver.Capabilities} A basic set of capabilities for 179 * Internet Explorer. 180 */ 181 webdriver.Capabilities.ie = function() { 182 return new webdriver.Capabilities(). 183 set(webdriver.Capability.BROWSER_NAME, 184 webdriver.Browser.INTERNET_EXPLORER). 185 set(webdriver.Capability.PLATFORM, 'WINDOWS'); 186 }; 187 188 189 /** 190 * @return {!webdriver.Capabilities} A basic set of capabilities for iPad. 191 */ 192 webdriver.Capabilities.ipad = function() { 193 return new webdriver.Capabilities(). 194 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPAD). 195 set(webdriver.Capability.PLATFORM, 'MAC'); 196 }; 197 198 199 /** 200 * @return {!webdriver.Capabilities} A basic set of capabilities for iPhone. 201 */ 202 webdriver.Capabilities.iphone = function() { 203 return new webdriver.Capabilities(). 204 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.IPHONE). 205 set(webdriver.Capability.PLATFORM, 'MAC'); 206 }; 207 208 209 /** 210 * @return {!webdriver.Capabilities} A basic set of capabilities for Opera. 211 */ 212 webdriver.Capabilities.opera = function() { 213 return new webdriver.Capabilities(). 214 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.OPERA); 215 }; 216 217 218 /** 219 * @return {!webdriver.Capabilities} A basic set of capabilities for 220 * PhantomJS. 221 */ 222 webdriver.Capabilities.phantomjs = function() { 223 return new webdriver.Capabilities(). 224 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.PHANTOM_JS); 225 }; 226 227 228 /** 229 * @return {!webdriver.Capabilities} A basic set of capabilities for Safari. 230 */ 231 webdriver.Capabilities.safari = function() { 232 return new webdriver.Capabilities(). 233 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.SAFARI); 234 }; 235 236 237 /** 238 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit. 239 */ 240 webdriver.Capabilities.htmlunit = function() { 241 return new webdriver.Capabilities(). 242 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT); 243 }; 244 245 246 /** 247 * @return {!webdriver.Capabilities} A basic set of capabilities for HTMLUnit 248 * with enabled Javascript. 249 */ 250 webdriver.Capabilities.htmlunitwithjs = function() { 251 return new webdriver.Capabilities(). 252 set(webdriver.Capability.BROWSER_NAME, webdriver.Browser.HTMLUNIT). 253 set(webdriver.Capability.SUPPORTS_JAVASCRIPT, true); 254 }; 255 256 257 /** @return {!Object} The JSON representation of this instance. */ 258 webdriver.Capabilities.prototype.toJSON = function() { 259 return this.caps_; 260 }; 261 262 263 /** 264 * Merges another set of capabilities into this instance. Any duplicates in 265 * the provided set will override those already set on this instance. 266 * @param {!(webdriver.Capabilities|Object)} other The capabilities to 267 * merge into this instance. 268 * @return {!webdriver.Capabilities} A self reference. 269 */ 270 webdriver.Capabilities.prototype.merge = function(other) { 271 var caps = other instanceof webdriver.Capabilities ? 272 other.caps_ : other; 273 for (var key in caps) { 274 if (caps.hasOwnProperty(key)) { 275 this.set(key, caps[key]); 276 } 277 } 278 return this; 279 }; 280 281 282 /** 283 * @param {string} key The capability to set. 284 * @param {*} value The capability value. Capability values must be JSON 285 * serializable. Pass {@code null} to unset the capability. 286 * @return {!webdriver.Capabilities} A self reference. 287 */ 288 webdriver.Capabilities.prototype.set = function(key, value) { 289 if (goog.isDefAndNotNull(value)) { 290 this.caps_[key] = value; 291 } else { 292 delete this.caps_[key]; 293 } 294 return this; 295 }; 296 297 298 /** 299 * @param {string} key The capability to return. 300 * @return {*} The capability with the given key, or {@code null} if it has 301 * not been set. 302 */ 303 webdriver.Capabilities.prototype.get = function(key) { 304 var val = null; 305 if (this.caps_.hasOwnProperty(key)) { 306 val = this.caps_[key]; 307 } 308 return goog.isDefAndNotNull(val) ? val : null; 309 }; 310 311 312 /** 313 * @param {string} key The capability to check. 314 * @return {boolean} Whether the specified capability is set. 315 */ 316 webdriver.Capabilities.prototype.has = function(key) { 317 return !!this.get(key); 318 }; lib/webdriver/command.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Contains several classes for handling commands. 17 */ 18 19 goog.provide('webdriver.Command'); 20 goog.provide('webdriver.CommandExecutor'); 21 goog.provide('webdriver.CommandName'); 22 23 24 25 /** 26 * Describes a command to be executed by the WebDriverJS framework. 27 * @param {!webdriver.CommandName} name The name of this command. 28 * @constructor 29 */ 30 webdriver.Command = function(name) { 31 32 /** 33 * The name of this command. 34 * @private {!webdriver.CommandName} 35 */ 36 this.name_ = name; 37 38 /** 39 * The parameters to this command. 40 * @private {!Object.<*>} 41 */ 42 this.parameters_ = {}; 43 }; 44 45 46 /** 47 * @return {!webdriver.CommandName} This command's name. 48 */ 49 webdriver.Command.prototype.getName = function() { 50 return this.name_; 51 }; 52 53 54 /** 55 * Sets a parameter to send with this command. 56 * @param {string} name The parameter name. 57 * @param {*} value The parameter value. 58 * @return {!webdriver.Command} A self reference. 59 */ 60 webdriver.Command.prototype.setParameter = function(name, value) { 61 this.parameters_[name] = value; 62 return this; 63 }; 64 65 66 /** 67 * Sets the parameters for this command. 68 * @param {!Object.<*>} parameters The command parameters. 69 * @return {!webdriver.Command} A self reference. 70 */ 71 webdriver.Command.prototype.setParameters = function(parameters) { 72 this.parameters_ = parameters; 73 return this; 74 }; 75 76 77 /** 78 * Returns a named command parameter. 79 * @param {string} key The parameter key to look up. 80 * @return {*} The parameter value, or undefined if it has not been set. 81 */ 82 webdriver.Command.prototype.getParameter = function(key) { 83 return this.parameters_[key]; 84 }; 85 86 87 /** 88 * @return {!Object.<*>} The parameters to send with this command. 89 */ 90 webdriver.Command.prototype.getParameters = function() { 91 return this.parameters_; 92 }; 93 94 95 /** 96 * Enumeration of predefined names command names that all command processors 97 * will support. 98 * @enum {string} 99 */ 100 // TODO: Delete obsolete command names. 101 webdriver.CommandName = { 102 GET_SERVER_STATUS: 'getStatus', 103 104 NEW_SESSION: 'newSession', 105 GET_SESSIONS: 'getSessions', 106 DESCRIBE_SESSION: 'getSessionCapabilities', 107 108 CLOSE: 'close', 109 QUIT: 'quit', 110 111 GET_CURRENT_URL: 'getCurrentUrl', 112 GET: 'get', 113 GO_BACK: 'goBack', 114 GO_FORWARD: 'goForward', 115 REFRESH: 'refresh', 116 117 ADD_COOKIE: 'addCookie', 118 GET_COOKIE: 'getCookie', 119 GET_ALL_COOKIES: 'getCookies', 120 DELETE_COOKIE: 'deleteCookie', 121 DELETE_ALL_COOKIES: 'deleteAllCookies', 122 123 GET_ACTIVE_ELEMENT: 'getActiveElement', 124 FIND_ELEMENT: 'findElement', 125 FIND_ELEMENTS: 'findElements', 126 FIND_CHILD_ELEMENT: 'findChildElement', 127 FIND_CHILD_ELEMENTS: 'findChildElements', 128 129 CLEAR_ELEMENT: 'clearElement', 130 CLICK_ELEMENT: 'clickElement', 131 SEND_KEYS_TO_ELEMENT: 'sendKeysToElement', 132 SUBMIT_ELEMENT: 'submitElement', 133 134 GET_CURRENT_WINDOW_HANDLE: 'getCurrentWindowHandle', 135 GET_WINDOW_HANDLES: 'getWindowHandles', 136 GET_WINDOW_POSITION: 'getWindowPosition', 137 SET_WINDOW_POSITION: 'setWindowPosition', 138 GET_WINDOW_SIZE: 'getWindowSize', 139 SET_WINDOW_SIZE: 'setWindowSize', 140 MAXIMIZE_WINDOW: 'maximizeWindow', 141 142 SWITCH_TO_WINDOW: 'switchToWindow', 143 SWITCH_TO_FRAME: 'switchToFrame', 144 GET_PAGE_SOURCE: 'getPageSource', 145 GET_TITLE: 'getTitle', 146 147 EXECUTE_SCRIPT: 'executeScript', 148 EXECUTE_ASYNC_SCRIPT: 'executeAsyncScript', 149 150 GET_ELEMENT_TEXT: 'getElementText', 151 GET_ELEMENT_TAG_NAME: 'getElementTagName', 152 IS_ELEMENT_SELECTED: 'isElementSelected', 153 IS_ELEMENT_ENABLED: 'isElementEnabled', 154 IS_ELEMENT_DISPLAYED: 'isElementDisplayed', 155 GET_ELEMENT_LOCATION: 'getElementLocation', 156 GET_ELEMENT_LOCATION_IN_VIEW: 'getElementLocationOnceScrolledIntoView', 157 GET_ELEMENT_SIZE: 'getElementSize', 158 GET_ELEMENT_ATTRIBUTE: 'getElementAttribute', 159 GET_ELEMENT_VALUE_OF_CSS_PROPERTY: 'getElementValueOfCssProperty', 160 ELEMENT_EQUALS: 'elementEquals', 161 162 SCREENSHOT: 'screenshot', 163 IMPLICITLY_WAIT: 'implicitlyWait', 164 SET_SCRIPT_TIMEOUT: 'setScriptTimeout', 165 SET_TIMEOUT: 'setTimeout', 166 167 ACCEPT_ALERT: 'acceptAlert', 168 DISMISS_ALERT: 'dismissAlert', 169 GET_ALERT_TEXT: 'getAlertText', 170 SET_ALERT_TEXT: 'setAlertValue', 171 172 EXECUTE_SQL: 'executeSQL', 173 GET_LOCATION: 'getLocation', 174 SET_LOCATION: 'setLocation', 175 GET_APP_CACHE: 'getAppCache', 176 GET_APP_CACHE_STATUS: 'getStatus', 177 CLEAR_APP_CACHE: 'clearAppCache', 178 IS_BROWSER_ONLINE: 'isBrowserOnline', 179 SET_BROWSER_ONLINE: 'setBrowserOnline', 180 181 GET_LOCAL_STORAGE_ITEM: 'getLocalStorageItem', 182 GET_LOCAL_STORAGE_KEYS: 'getLocalStorageKeys', 183 SET_LOCAL_STORAGE_ITEM: 'setLocalStorageItem', 184 REMOVE_LOCAL_STORAGE_ITEM: 'removeLocalStorageItem', 185 CLEAR_LOCAL_STORAGE: 'clearLocalStorage', 186 GET_LOCAL_STORAGE_SIZE: 'getLocalStorageSize', 187 188 GET_SESSION_STORAGE_ITEM: 'getSessionStorageItem', 189 GET_SESSION_STORAGE_KEYS: 'getSessionStorageKey', 190 SET_SESSION_STORAGE_ITEM: 'setSessionStorageItem', 191 REMOVE_SESSION_STORAGE_ITEM: 'removeSessionStorageItem', 192 CLEAR_SESSION_STORAGE: 'clearSessionStorage', 193 GET_SESSION_STORAGE_SIZE: 'getSessionStorageSize', 194 195 SET_SCREEN_ORIENTATION: 'setScreenOrientation', 196 GET_SCREEN_ORIENTATION: 'getScreenOrientation', 197 198 // These belong to the Advanced user interactions - an element is 199 // optional for these commands. 200 CLICK: 'mouseClick', 201 DOUBLE_CLICK: 'mouseDoubleClick', 202 MOUSE_DOWN: 'mouseButtonDown', 203 MOUSE_UP: 'mouseButtonUp', 204 MOVE_TO: 'mouseMoveTo', 205 SEND_KEYS_TO_ACTIVE_ELEMENT: 'sendKeysToActiveElement', 206 207 // These belong to the Advanced Touch API 208 TOUCH_SINGLE_TAP: 'touchSingleTap', 209 TOUCH_DOWN: 'touchDown', 210 TOUCH_UP: 'touchUp', 211 TOUCH_MOVE: 'touchMove', 212 TOUCH_SCROLL: 'touchScroll', 213 TOUCH_DOUBLE_TAP: 'touchDoubleTap', 214 TOUCH_LONG_PRESS: 'touchLongPress', 215 TOUCH_FLICK: 'touchFlick', 216 217 GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes', 218 GET_LOG: 'getLog', 219 GET_SESSION_LOGS: 'getSessionLogs' 220 }; 221 222 223 224 /** 225 * Handles the execution of {@code webdriver.Command} objects. 226 * @interface 227 */ 228 webdriver.CommandExecutor = function() {}; 229 230 231 /** 232 * Executes the given {@code command}. If there is an error executing the 233 * command, the provided callback will be invoked with the offending error. 234 * Otherwise, the callback will be invoked with a null Error and non-null 235 * {@link bot.response.ResponseObject} object. 236 * @param {!webdriver.Command} command The command to execute. 237 * @param {function(Error, !bot.response.ResponseObject=)} callback the function 238 * to invoke when the command response is ready. 239 */ 240 webdriver.CommandExecutor.prototype.execute = goog.abstractMethod; lib/webdriver/events.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview A light weight event system modeled after Node's EventEmitter. 17 */ 18 19 goog.provide('webdriver.EventEmitter'); 20 21 22 23 /** 24 * Object that can emit events for others to listen for. This is used instead 25 * of Closure's event system because it is much more light weight. The API is 26 * based on Node's EventEmitters. 27 * @constructor 28 */ 29 webdriver.EventEmitter = function() { 30 /** 31 * Map of events to registered listeners. 32 * @private {!Object.<!Array.<{fn: !Function, oneshot: boolean, 33 * scope: (Object|undefined)}>>} 34 */ 35 this.events_ = {}; 36 }; 37 38 39 /** 40 * Fires an event and calls all listeners. 41 * @param {string} type The type of event to emit. 42 * @param {...*} var_args Any arguments to pass to each listener. 43 */ 44 webdriver.EventEmitter.prototype.emit = function(type, var_args) { 45 var args = Array.prototype.slice.call(arguments, 1); 46 var listeners = this.events_[type]; 47 if (!listeners) { 48 return; 49 } 50 for (var i = 0; i < listeners.length;) { 51 var listener = listeners[i]; 52 listener.fn.apply(listener.scope, args); 53 if (listeners[i] === listener) { 54 if (listeners[i].oneshot) { 55 listeners.splice(i, 1); 56 } else { 57 i += 1; 58 } 59 } 60 } 61 }; 62 63 64 /** 65 * Returns a mutable list of listeners for a specific type of event. 66 * @param {string} type The type of event to retrieve the listeners for. 67 * @return {!Array.<{fn: !Function, oneshot: boolean, 68 * scope: (Object|undefined)}>} The registered listeners for 69 * the given event type. 70 */ 71 webdriver.EventEmitter.prototype.listeners = function(type) { 72 var listeners = this.events_[type]; 73 if (!listeners) { 74 listeners = this.events_[type] = []; 75 } 76 return listeners; 77 }; 78 79 80 /** 81 * Registers a listener. 82 * @param {string} type The type of event to listen for. 83 * @param {!Function} listenerFn The function to invoke when the event is fired. 84 * @param {Object=} opt_scope The object in whose scope to invoke the listener. 85 * @param {boolean=} opt_oneshot Whether the listener should be removed after 86 * the first event is fired. 87 * @return {!webdriver.EventEmitter} A self reference. 88 * @private 89 */ 90 webdriver.EventEmitter.prototype.addListener_ = function(type, listenerFn, 91 opt_scope, opt_oneshot) { 92 var listeners = this.listeners(type); 93 var n = listeners.length; 94 for (var i = 0; i < n; ++i) { 95 if (listeners[i].fn == listenerFn) { 96 return this; 97 } 98 } 99 100 listeners.push({ 101 fn: listenerFn, 102 scope: opt_scope, 103 oneshot: !!opt_oneshot 104 }); 105 return this; 106 }; 107 108 109 /** 110 * Registers a listener. 111 * @param {string} type The type of event to listen for. 112 * @param {!Function} listenerFn The function to invoke when the event is fired. 113 * @param {Object=} opt_scope The object in whose scope to invoke the listener. 114 * @return {!webdriver.EventEmitter} A self reference. 115 */ 116 webdriver.EventEmitter.prototype.addListener = function(type, listenerFn, 117 opt_scope) { 118 return this.addListener_(type, listenerFn, opt_scope); 119 }; 120 121 122 /** 123 * Registers a one-time listener which will be called only the first time an 124 * event is emitted, after which it will be removed. 125 * @param {string} type The type of event to listen for. 126 * @param {!Function} listenerFn The function to invoke when the event is fired. 127 * @param {Object=} opt_scope The object in whose scope to invoke the listener. 128 * @return {!webdriver.EventEmitter} A self reference. 129 */ 130 webdriver.EventEmitter.prototype.once = function(type, listenerFn, opt_scope) { 131 return this.addListener_(type, listenerFn, opt_scope, true); 132 }; 133 134 135 /** 136 * An alias for {@code #addListener()}. 137 * @param {string} type The type of event to listen for. 138 * @param {!Function} listenerFn The function to invoke when the event is fired. 139 * @param {Object=} opt_scope The object in whose scope to invoke the listener. 140 * @return {!webdriver.EventEmitter} A self reference. 141 */ 142 webdriver.EventEmitter.prototype.on = 143 webdriver.EventEmitter.prototype.addListener; 144 145 146 /** 147 * Removes a previously registered event listener. 148 * @param {string} type The type of event to unregister. 149 * @param {!Function} listenerFn The handler function to remove. 150 * @return {!webdriver.EventEmitter} A self reference. 151 */ 152 webdriver.EventEmitter.prototype.removeListener = function(type, listenerFn) { 153 var listeners = this.events_[type]; 154 if (listeners) { 155 var n = listeners.length; 156 for (var i = 0; i < n; ++i) { 157 if (listeners[i].fn == listenerFn) { 158 listeners.splice(i, 1); 159 return this; 160 } 161 } 162 } 163 return this; 164 }; 165 166 167 /** 168 * Removes all listeners for a specific type of event. If no event is 169 * specified, all listeners across all types will be removed. 170 * @param {string=} opt_type The type of event to remove listeners from. 171 * @return {!webdriver.EventEmitter} A self reference. 172 */ 173 webdriver.EventEmitter.prototype.removeAllListeners = function(opt_type) { 174 goog.isDef(opt_type) ? delete this.events_[opt_type] : this.events_ = {}; 175 return this; 176 }; lib/webdriver/firefoxdomexecutor.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.FirefoxDomExecutor'); 16 17 goog.require('bot.response'); 18 goog.require('goog.json'); 19 goog.require('goog.userAgent.product'); 20 goog.require('webdriver.Command'); 21 goog.require('webdriver.CommandName'); 22 23 24 25 /** 26 * @constructor 27 * @implements {webdriver.CommandExecutor} 28 */ 29 webdriver.FirefoxDomExecutor = function() { 30 if (!webdriver.FirefoxDomExecutor.isAvailable()) { 31 throw Error( 32 'The current environment does not support the FirefoxDomExecutor'); 33 } 34 35 /** @private {!Document} */ 36 this.doc_ = document; 37 38 /** @private {!Element} */ 39 this.docElement_ = document.documentElement; 40 41 this.docElement_.addEventListener( 42 webdriver.FirefoxDomExecutor.EventType_.RESPONSE, 43 goog.bind(this.onResponse_, this), false); 44 }; 45 46 47 /** 48 * @return {boolean} Whether the current environment supports the 49 * FirefoxDomExecutor. 50 */ 51 webdriver.FirefoxDomExecutor.isAvailable = function() { 52 return goog.userAgent.product.FIREFOX && 53 typeof document !== 'undefined' && 54 document.documentElement && 55 goog.isFunction(document.documentElement.hasAttribute) && 56 document.documentElement.hasAttribute('webdriver'); 57 }; 58 59 60 /** 61 * Attributes used to communicate with the FirefoxDriver extension. 62 * @enum {string} 63 * @private 64 */ 65 webdriver.FirefoxDomExecutor.Attribute_ = { 66 COMMAND: 'command', 67 RESPONSE: 'response' 68 }; 69 70 71 /** 72 * Events used to communicate with the FirefoxDriver extension. 73 * @enum {string} 74 * @private 75 */ 76 webdriver.FirefoxDomExecutor.EventType_ = { 77 COMMAND: 'webdriverCommand', 78 RESPONSE: 'webdriverResponse' 79 }; 80 81 82 /** 83 * The pending command, if any. 84 * @private {?{name:string, callback:!Function}} 85 */ 86 webdriver.FirefoxDomExecutor.prototype.pendingCommand_ = null; 87 88 89 /** @override */ 90 webdriver.FirefoxDomExecutor.prototype.execute = function(command, callback) { 91 if (this.pendingCommand_) { 92 throw Error('Currently awaiting a command response!'); 93 } 94 95 this.pendingCommand_ = { 96 name: command.getName(), 97 callback: callback 98 }; 99 100 var parameters = command.getParameters(); 101 102 // There are two means for communicating with the FirefoxDriver: via 103 // HTTP using WebDriver's wire protocol and over the DOM using a custom 104 // JSON protocol. This class uses the latter. When the FirefoxDriver receives 105 // commands over HTTP, it builds a parameters object from the URL parameters. 106 // When an element ID is sent in the URL, it'll be decoded as just id:string 107 // instead of id:{ELEMENT:string}. When switching to a frame by element, 108 // however, the element ID is not sent through the URL, so we must make sure 109 // to encode that parameter properly here. It would be nice if we unified 110 // the two protocols used by the FirefoxDriver... 111 if (parameters['id'] && 112 parameters['id']['ELEMENT'] && 113 command.getName() != webdriver.CommandName.SWITCH_TO_FRAME) { 114 parameters['id'] = parameters['id']['ELEMENT']; 115 } 116 var json = goog.json.serialize({ 117 'name': command.getName(), 118 'sessionId': parameters['sessionId'], 119 'parameters': parameters 120 }); 121 this.docElement_.setAttribute( 122 webdriver.FirefoxDomExecutor.Attribute_.COMMAND, json); 123 124 var event = this.doc_.createEvent('Event'); 125 event.initEvent(webdriver.FirefoxDomExecutor.EventType_.COMMAND, 126 /*canBubble=*/true, /*cancelable=*/true); 127 128 this.docElement_.dispatchEvent(event); 129 }; 130 131 132 /** @private */ 133 webdriver.FirefoxDomExecutor.prototype.onResponse_ = function() { 134 if (!this.pendingCommand_) { 135 return; // Not expecting a response. 136 } 137 138 var command = this.pendingCommand_; 139 this.pendingCommand_ = null; 140 141 var json = this.docElement_.getAttribute( 142 webdriver.FirefoxDomExecutor.Attribute_.RESPONSE); 143 if (!json) { 144 command.callback(Error('Empty command response!')); 145 return; 146 } 147 148 this.docElement_.removeAttribute( 149 webdriver.FirefoxDomExecutor.Attribute_.COMMAND); 150 this.docElement_.removeAttribute( 151 webdriver.FirefoxDomExecutor.Attribute_.RESPONSE); 152 153 try { 154 var response = bot.response.checkResponse( 155 /** @type {!bot.response.ResponseObject} */ (goog.json.parse(json))); 156 } catch (ex) { 157 command.callback(ex); 158 return; 159 } 160 161 // Prior to Selenium 2.35.0, two commands are required to fully create a 162 // session: one to allocate the session, and another to fetch the 163 // capabilities. 164 if (command.name == webdriver.CommandName.NEW_SESSION && 165 goog.isString(response['value'])) { 166 var cmd = new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION). 167 setParameter('sessionId', response['value']); 168 this.execute(cmd, command.callback); 169 } else { 170 command.callback(null, response); 171 } 172 }; lib/webdriver/http/corsclient.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.http.CorsClient'); 16 17 goog.require('goog.json'); 18 goog.require('webdriver.http.Response'); 19 20 21 22 /** 23 * Communicates with a WebDriver server, which may be on a different domain, 24 * using the <a href="http://www.w3.org/TR/cors/">cross-origin resource sharing 25 * </a> (CORS) extension to WebDriver's JSON wire protocol. 26 * 27 * <p>Each command from the standard JSON protocol will be encoded in a 28 * JSON object with the following form: 29 * {method:string, path:string, data:!Object} 30 * 31 * <p>The encoded command is then sent as a POST request to the server's /xdrpc 32 * endpoint. The server will decode the command, re-route it to the appropriate 33 * handler, and then return the command's response as a standard JSON response 34 * object. The JSON responses will <em>always</em> be returned with a 200 35 * response from the server; clients must rely on the response's "status" field 36 * to determine whether the command succeeded. 37 * 38 * <p>This client cannot be used with the standard wire protocol due to 39 * limitations in the various browser implementations of the CORS specification: 40 * <ul> 41 * <li>IE's <a href="http://goo.gl/6l3kA">XDomainRequest</a> object is only 42 * capable of generating the types of requests that may be generated through 43 * a standard <a href="http://goo.gl/vgzAU">HTML form</a> - it can not send 44 * DELETE requests, as is required in the wire protocol. 45 * <li>WebKit's implementation of CORS does not follow the spec and forbids 46 * redirects: https://bugs.webkit.org/show_bug.cgi?id=57600 47 * This limitation appears to be intentional and is documented in WebKit's 48 * Layout tests: 49 * //LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html 50 * <li>If the server does not return a 2xx response, IE and Opera's 51 * implementations will fire the XDomainRequest/XMLHttpRequest object's 52 * onerror handler, but without the corresponding response text returned by 53 * the server. This renders IE and Opera incapable of handling command 54 * failures in the standard JSON protocol. 55 * </ul> 56 * 57 * @param {string} url URL for the WebDriver server to send commands to. 58 * @constructor 59 * @implements {webdriver.http.Client} 60 * @see <a href="http://www.w3.org/TR/cors/">CORS Spec</a> 61 * @see <a href="http://code.google.com/p/selenium/wiki/JsonWireProtocol"> 62 * JSON wire protocol</a> 63 */ 64 webdriver.http.CorsClient = function(url) { 65 if (!webdriver.http.CorsClient.isAvailable()) { 66 throw Error('The current environment does not support cross-origin ' + 67 'resource sharing'); 68 } 69 70 /** @private {string} */ 71 this.url_ = url + webdriver.http.CorsClient.XDRPC_ENDPOINT; 72 }; 73 74 75 /** 76 * Resource URL to send commands to on the server. 77 * @type {string} 78 * @const 79 */ 80 webdriver.http.CorsClient.XDRPC_ENDPOINT = '/xdrpc'; 81 82 83 /** 84 * Tests whether the current environment supports cross-origin resource sharing. 85 * @return {boolean} Whether cross-origin resource sharing is supported. 86 * @see http://www.w3.org/TR/cors/ 87 */ 88 webdriver.http.CorsClient.isAvailable = function() { 89 return typeof XDomainRequest !== 'undefined' || 90 (typeof XMLHttpRequest !== 'undefined' && 91 goog.isBoolean(new XMLHttpRequest().withCredentials)); 92 }; 93 94 95 /** @override */ 96 webdriver.http.CorsClient.prototype.send = function(request, callback) { 97 try { 98 var xhr = new (typeof XDomainRequest !== 'undefined' ? 99 XDomainRequest : XMLHttpRequest); 100 xhr.open('POST', this.url_, true); 101 102 xhr.onload = function() { 103 callback(null, webdriver.http.Response.fromXmlHttpRequest( 104 /** @type {!XMLHttpRequest} */ (xhr))); 105 }; 106 107 var url = this.url_; 108 xhr.onerror = function() { 109 callback(Error([ 110 'Unable to send request: POST ', url, 111 '\nPerhaps the server did not respond to the preflight request ', 112 'with valid access control headers?' 113 ].join(''))); 114 }; 115 116 // Define event handlers for all events on the XDomainRequest. Apparently, 117 // if we don't do this, IE9+10 will silently abort our request. Yay IE. 118 // Note, we're not using goog.nullFunction, because it tends to get 119 // optimized away by the compiler, which leaves us where we were before. 120 xhr.onprogress = xhr.ontimeout = function() {}; 121 122 xhr.send(goog.json.serialize({ 123 'method': request.method, 124 'path': request.path, 125 'data': request.data 126 })); 127 } catch (ex) { 128 callback(ex); 129 } 130 }; lib/webdriver/http/http.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Defines a {@code webdriver.CommandExecutor} that communicates 17 * with a server over HTTP. 18 */ 19 20 goog.provide('webdriver.http.Client'); 21 goog.provide('webdriver.http.Executor'); 22 goog.provide('webdriver.http.Request'); 23 goog.provide('webdriver.http.Response'); 24 25 goog.require('bot.ErrorCode'); 26 goog.require('goog.array'); 27 goog.require('goog.json'); 28 goog.require('webdriver.CommandName'); 29 goog.require('webdriver.promise.Deferred'); 30 31 32 33 /** 34 * Interface used for sending individual HTTP requests to the server. 35 * @interface 36 */ 37 webdriver.http.Client = function() { 38 }; 39 40 41 /** 42 * Sends a request to the server. If an error occurs while sending the request, 43 * such as a failure to connect to the server, the provided callback will be 44 * invoked with a non-null {@code Error} describing the error. Otherwise, when 45 * the server's response has been received, the callback will be invoked with a 46 * null Error and non-null {@code webdriver.http.Response} object. 47 * 48 * @param {!webdriver.http.Request} request The request to send. 49 * @param {function(Error, !webdriver.http.Response=)} callback the function to 50 * invoke when the server's response is ready. 51 */ 52 webdriver.http.Client.prototype.send = function(request, callback) { 53 }; 54 55 56 57 /** 58 * A command executor that communicates with a server using the WebDriver 59 * command protocol. 60 * @param {!webdriver.http.Client} client The client to use when sending 61 * requests to the server. 62 * @constructor 63 * @implements {webdriver.CommandExecutor} 64 */ 65 webdriver.http.Executor = function(client) { 66 67 /** 68 * Client used to communicate with the server. 69 * @private {!webdriver.http.Client} 70 */ 71 this.client_ = client; 72 }; 73 74 75 /** @override */ 76 webdriver.http.Executor.prototype.execute = function(command, callback) { 77 var resource = webdriver.http.Executor.COMMAND_MAP_[command.getName()]; 78 if (!resource) { 79 throw new Error('Unrecognized command: ' + command.getName()); 80 } 81 82 var parameters = command.getParameters(); 83 var path = webdriver.http.Executor.buildPath_(resource.path, parameters); 84 var request = new webdriver.http.Request(resource.method, path, parameters); 85 86 this.client_.send(request, function(e, response) { 87 var responseObj; 88 if (!e) { 89 try { 90 responseObj = webdriver.http.Executor.parseHttpResponse_( 91 /** @type {!webdriver.http.Response} */ (response)); 92 } catch (ex) { 93 e = ex; 94 } 95 } 96 callback(e, responseObj); 97 }); 98 }; 99 100 101 /** 102 * Builds a fully qualified path using the given set of command parameters. Each 103 * path segment prefixed with ':' will be replaced by the value of the 104 * corresponding parameter. All parameters spliced into the path will be 105 * removed from the parameter map. 106 * @param {string} path The original resource path. 107 * @param {!Object.<*>} parameters The parameters object to splice into 108 * the path. 109 * @return {string} The modified path. 110 * @private 111 */ 112 webdriver.http.Executor.buildPath_ = function(path, parameters) { 113 var pathParameters = path.match(/\/:(\w+)\b/g); 114 if (pathParameters) { 115 for (var i = 0; i < pathParameters.length; ++i) { 116 var key = pathParameters[i].substring(2); // Trim the /: 117 if (key in parameters) { 118 var value = parameters[key]; 119 // TODO: move webdriver.WebElement.ELEMENT definition to a 120 // common file so we can reference it here without pulling in all of 121 // webdriver.WebElement's dependencies. 122 if (value && value['ELEMENT']) { 123 // When inserting a WebElement into the URL, only use its ID value, 124 // not the full JSON. 125 value = value['ELEMENT']; 126 } 127 path = path.replace(pathParameters[i], '/' + value); 128 delete parameters[key]; 129 } else { 130 throw new Error('Missing required parameter: ' + key); 131 } 132 } 133 } 134 return path; 135 }; 136 137 138 /** 139 * Callback used to parse {@link webdriver.http.Response} objects from a 140 * {@link webdriver.http.Client}. 141 * @param {!webdriver.http.Response} httpResponse The HTTP response to parse. 142 * @return {!bot.response.ResponseObject} The parsed response. 143 * @private 144 */ 145 webdriver.http.Executor.parseHttpResponse_ = function(httpResponse) { 146 try { 147 return /** @type {!bot.response.ResponseObject} */ (goog.json.parse( 148 httpResponse.body)); 149 } catch (ex) { 150 // Whoops, looks like the server sent us a malformed response. We'll need 151 // to manually build a response object based on the response code. 152 } 153 154 var response = { 155 'status': bot.ErrorCode.SUCCESS, 156 'value': httpResponse.body.replace(/\r\n/g, '\n') 157 }; 158 159 if (!(httpResponse.status > 199 && httpResponse.status < 300)) { 160 // 404 represents an unknown command; anything else is a generic unknown 161 // error. 162 response['status'] = httpResponse.status == 404 ? 163 bot.ErrorCode.UNKNOWN_COMMAND : 164 bot.ErrorCode.UNKNOWN_ERROR; 165 } 166 167 return response; 168 }; 169 170 171 /** 172 * Maps command names to resource locator. 173 * @private {!Object.<{method:string, path:string}>} 174 * @const 175 */ 176 webdriver.http.Executor.COMMAND_MAP_ = (function() { 177 return new Builder(). 178 put(webdriver.CommandName.GET_SERVER_STATUS, get('/status')). 179 put(webdriver.CommandName.NEW_SESSION, post('/session')). 180 put(webdriver.CommandName.GET_SESSIONS, get('/sessions')). 181 put(webdriver.CommandName.DESCRIBE_SESSION, get('/session/:sessionId')). 182 put(webdriver.CommandName.QUIT, del('/session/:sessionId')). 183 put(webdriver.CommandName.CLOSE, del('/session/:sessionId/window')). 184 put(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE, 185 get('/session/:sessionId/window_handle')). 186 put(webdriver.CommandName.GET_WINDOW_HANDLES, 187 get('/session/:sessionId/window_handles')). 188 put(webdriver.CommandName.GET_CURRENT_URL, 189 get('/session/:sessionId/url')). 190 put(webdriver.CommandName.GET, post('/session/:sessionId/url')). 191 put(webdriver.CommandName.GO_BACK, post('/session/:sessionId/back')). 192 put(webdriver.CommandName.GO_FORWARD, 193 post('/session/:sessionId/forward')). 194 put(webdriver.CommandName.REFRESH, 195 post('/session/:sessionId/refresh')). 196 put(webdriver.CommandName.ADD_COOKIE, 197 post('/session/:sessionId/cookie')). 198 put(webdriver.CommandName.GET_ALL_COOKIES, 199 get('/session/:sessionId/cookie')). 200 put(webdriver.CommandName.DELETE_ALL_COOKIES, 201 del('/session/:sessionId/cookie')). 202 put(webdriver.CommandName.DELETE_COOKIE, 203 del('/session/:sessionId/cookie/:name')). 204 put(webdriver.CommandName.FIND_ELEMENT, 205 post('/session/:sessionId/element')). 206 put(webdriver.CommandName.FIND_ELEMENTS, 207 post('/session/:sessionId/elements')). 208 put(webdriver.CommandName.GET_ACTIVE_ELEMENT, 209 post('/session/:sessionId/element/active')). 210 put(webdriver.CommandName.FIND_CHILD_ELEMENT, 211 post('/session/:sessionId/element/:id/element')). 212 put(webdriver.CommandName.FIND_CHILD_ELEMENTS, 213 post('/session/:sessionId/element/:id/elements')). 214 put(webdriver.CommandName.CLEAR_ELEMENT, 215 post('/session/:sessionId/element/:id/clear')). 216 put(webdriver.CommandName.CLICK_ELEMENT, 217 post('/session/:sessionId/element/:id/click')). 218 put(webdriver.CommandName.SEND_KEYS_TO_ELEMENT, 219 post('/session/:sessionId/element/:id/value')). 220 put(webdriver.CommandName.SUBMIT_ELEMENT, 221 post('/session/:sessionId/element/:id/submit')). 222 put(webdriver.CommandName.GET_ELEMENT_TEXT, 223 get('/session/:sessionId/element/:id/text')). 224 put(webdriver.CommandName.GET_ELEMENT_TAG_NAME, 225 get('/session/:sessionId/element/:id/name')). 226 put(webdriver.CommandName.IS_ELEMENT_SELECTED, 227 get('/session/:sessionId/element/:id/selected')). 228 put(webdriver.CommandName.IS_ELEMENT_ENABLED, 229 get('/session/:sessionId/element/:id/enabled')). 230 put(webdriver.CommandName.IS_ELEMENT_DISPLAYED, 231 get('/session/:sessionId/element/:id/displayed')). 232 put(webdriver.CommandName.GET_ELEMENT_LOCATION, 233 get('/session/:sessionId/element/:id/location')). 234 put(webdriver.CommandName.GET_ELEMENT_SIZE, 235 get('/session/:sessionId/element/:id/size')). 236 put(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE, 237 get('/session/:sessionId/element/:id/attribute/:name')). 238 put(webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY, 239 get('/session/:sessionId/element/:id/css/:propertyName')). 240 put(webdriver.CommandName.ELEMENT_EQUALS, 241 get('/session/:sessionId/element/:id/equals/:other')). 242 put(webdriver.CommandName.SWITCH_TO_WINDOW, 243 post('/session/:sessionId/window')). 244 put(webdriver.CommandName.MAXIMIZE_WINDOW, 245 post('/session/:sessionId/window/:windowHandle/maximize')). 246 put(webdriver.CommandName.GET_WINDOW_POSITION, 247 get('/session/:sessionId/window/:windowHandle/position')). 248 put(webdriver.CommandName.SET_WINDOW_POSITION, 249 post('/session/:sessionId/window/:windowHandle/position')). 250 put(webdriver.CommandName.GET_WINDOW_SIZE, 251 get('/session/:sessionId/window/:windowHandle/size')). 252 put(webdriver.CommandName.SET_WINDOW_SIZE, 253 post('/session/:sessionId/window/:windowHandle/size')). 254 put(webdriver.CommandName.SWITCH_TO_FRAME, 255 post('/session/:sessionId/frame')). 256 put(webdriver.CommandName.GET_PAGE_SOURCE, 257 get('/session/:sessionId/source')). 258 put(webdriver.CommandName.GET_TITLE, 259 get('/session/:sessionId/title')). 260 put(webdriver.CommandName.EXECUTE_SCRIPT, 261 post('/session/:sessionId/execute')). 262 put(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT, 263 post('/session/:sessionId/execute_async')). 264 put(webdriver.CommandName.SCREENSHOT, 265 get('/session/:sessionId/screenshot')). 266 put(webdriver.CommandName.SET_TIMEOUT, 267 post('/session/:sessionId/timeouts')). 268 put(webdriver.CommandName.SET_SCRIPT_TIMEOUT, 269 post('/session/:sessionId/timeouts/async_script')). 270 put(webdriver.CommandName.IMPLICITLY_WAIT, 271 post('/session/:sessionId/timeouts/implicit_wait')). 272 put(webdriver.CommandName.MOVE_TO, post('/session/:sessionId/moveto')). 273 put(webdriver.CommandName.CLICK, post('/session/:sessionId/click')). 274 put(webdriver.CommandName.DOUBLE_CLICK, 275 post('/session/:sessionId/doubleclick')). 276 put(webdriver.CommandName.MOUSE_DOWN, 277 post('/session/:sessionId/buttondown')). 278 put(webdriver.CommandName.MOUSE_UP, post('/session/:sessionId/buttonup')). 279 put(webdriver.CommandName.MOVE_TO, post('/session/:sessionId/moveto')). 280 put(webdriver.CommandName.SEND_KEYS_TO_ACTIVE_ELEMENT, 281 post('/session/:sessionId/keys')). 282 put(webdriver.CommandName.ACCEPT_ALERT, 283 post('/session/:sessionId/accept_alert')). 284 put(webdriver.CommandName.DISMISS_ALERT, 285 post('/session/:sessionId/dismiss_alert')). 286 put(webdriver.CommandName.GET_ALERT_TEXT, 287 get('/session/:sessionId/alert_text')). 288 put(webdriver.CommandName.SET_ALERT_TEXT, 289 post('/session/:sessionId/alert_text')). 290 put(webdriver.CommandName.GET_LOG, post('/session/:sessionId/log')). 291 put(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES, 292 get('/session/:sessionId/log/types')). 293 put(webdriver.CommandName.GET_SESSION_LOGS, post('/logs')). 294 build(); 295 296 /** @constructor */ 297 function Builder() { 298 var map = {}; 299 300 this.put = function(name, resource) { 301 map[name] = resource; 302 return this; 303 }; 304 305 this.build = function() { 306 return map; 307 }; 308 } 309 310 function post(path) { return resource('POST', path); } 311 function del(path) { return resource('DELETE', path); } 312 function get(path) { return resource('GET', path); } 313 function resource(method, path) { return {method: method, path: path}; } 314 })(); 315 316 317 /** 318 * Converts a headers object to a HTTP header block string. 319 * @param {!Object.<string>} headers The headers object to convert. 320 * @return {string} The headers as a string. 321 * @private 322 */ 323 webdriver.http.headersToString_ = function(headers) { 324 var ret = []; 325 for (var key in headers) { 326 ret.push(key + ': ' + headers[key]); 327 } 328 return ret.join('\n'); 329 }; 330 331 332 333 /** 334 * Describes a partial HTTP request. This class is a "partial" request and only 335 * defines the path on the server to send a request to. It is each 336 * {@code webdriver.http.Client}'s responsibility to build the full URL for the 337 * final request. 338 * @param {string} method The HTTP method to use for the request. 339 * @param {string} path Path on the server to send the request to. 340 * @param {Object=} opt_data This request's JSON data. 341 * @constructor 342 */ 343 webdriver.http.Request = function(method, path, opt_data) { 344 345 /** 346 * The HTTP method to use for the request. 347 * @type {string} 348 */ 349 this.method = method; 350 351 /** 352 * The path on the server to send the request to. 353 * @type {string} 354 */ 355 this.path = path; 356 357 /** 358 * This request's body. 359 * @type {!Object} 360 */ 361 this.data = opt_data || {}; 362 363 /** 364 * The headers to send with the request. 365 * @type {!Object.<(string|number)>} 366 */ 367 this.headers = {'Accept': 'application/json; charset=utf-8'}; 368 }; 369 370 371 /** @override */ 372 webdriver.http.Request.prototype.toString = function() { 373 return [ 374 this.method + ' ' + this.path + ' HTTP/1.1', 375 webdriver.http.headersToString_(this.headers), 376 '', 377 goog.json.serialize(this.data) 378 ].join('\n'); 379 }; 380 381 382 383 /** 384 * Represents a HTTP response. 385 * @param {number} status The response code. 386 * @param {!Object.<string>} headers The response headers. All header 387 * names will be converted to lowercase strings for consistent lookups. 388 * @param {string} body The response body. 389 * @constructor 390 */ 391 webdriver.http.Response = function(status, headers, body) { 392 393 /** 394 * The HTTP response code. 395 * @type {number} 396 */ 397 this.status = status; 398 399 /** 400 * The response body. 401 * @type {string} 402 */ 403 this.body = body; 404 405 /** 406 * The response body. 407 * @type {!Object.<string>} 408 */ 409 this.headers = {}; 410 for (var header in headers) { 411 this.headers[header.toLowerCase()] = headers[header]; 412 } 413 }; 414 415 416 /** 417 * Builds a {@code webdriver.http.Response} from a {@code XMLHttpRequest} or 418 * {@code XDomainRequest} response object. 419 * @param {!(XDomainRequest|XMLHttpRequest)} xhr The request to parse. 420 * @return {!webdriver.http.Response} The parsed response. 421 */ 422 webdriver.http.Response.fromXmlHttpRequest = function(xhr) { 423 var headers = {}; 424 425 // getAllResponseHeaders is only available on XMLHttpRequest objects. 426 if (xhr.getAllResponseHeaders) { 427 var tmp = xhr.getAllResponseHeaders(); 428 if (tmp) { 429 tmp = tmp.replace(/\r\n/g, '\n').split('\n'); 430 goog.array.forEach(tmp, function(header) { 431 var parts = header.split(/\s*:\s*/, 2); 432 if (parts[0]) { 433 headers[parts[0]] = parts[1] || ''; 434 } 435 }); 436 } 437 } 438 439 // If xhr is a XDomainRequest object, it will not have a status. 440 // However, if we're parsing the response from a XDomainRequest, then 441 // that request must have been a success, so we can assume status == 200. 442 var status = xhr.status || 200; 443 return new webdriver.http.Response(status, headers, 444 xhr.responseText.replace(/\0/g, '')); 445 }; 446 447 448 /** @override */ 449 webdriver.http.Response.prototype.toString = function() { 450 var headers = webdriver.http.headersToString_(this.headers); 451 var ret = ['HTTP/1.1 ' + this.status, headers]; 452 453 if (headers) { 454 ret.push(''); 455 } 456 457 if (this.body) { 458 ret.push(this.body); 459 } 460 461 return ret.join('\n'); 462 }; lib/webdriver/http/xhrclient.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** @fileoverview A XHR client. */ 16 17 goog.provide('webdriver.http.XhrClient'); 18 19 goog.require('goog.json'); 20 goog.require('goog.net.XmlHttp'); 21 goog.require('webdriver.http.Response'); 22 23 24 25 /** 26 * A HTTP client that sends requests using XMLHttpRequests. 27 * @param {string} url URL for the WebDriver server to send commands to. 28 * @constructor 29 * @implements {webdriver.http.Client} 30 */ 31 webdriver.http.XhrClient = function(url) { 32 33 /** @private {string} */ 34 this.url_ = url; 35 }; 36 37 38 /** @override */ 39 webdriver.http.XhrClient.prototype.send = function(request, callback) { 40 try { 41 var xhr = /** @type {!XMLHttpRequest} */ (goog.net.XmlHttp()); 42 var url = this.url_ + request.path; 43 xhr.open(request.method, url, true); 44 45 xhr.onload = function() { 46 callback(null, webdriver.http.Response.fromXmlHttpRequest(xhr)); 47 }; 48 49 xhr.onerror = function() { 50 callback(Error([ 51 'Unable to send request: ', request.method, ' ', url, 52 '\nOriginal request:\n', request 53 ].join(''))); 54 }; 55 56 for (var header in request.headers) { 57 xhr.setRequestHeader(header, request.headers[header] + ''); 58 } 59 60 xhr.send(goog.json.serialize(request.data)); 61 } catch (ex) { 62 callback(ex); 63 } 64 }; lib/webdriver/key.js
1 // Copyright 2012 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.Key'); 16 17 18 /** 19 * Representations of pressable keys that aren't text. These are stored in 20 * the Unicode PUA (Private Use Area) code points, 0xE000-0xF8FF. Refer to 21 * http://www.google.com.au/search?&q=unicode+pua&btnG=Search 22 * 23 * @enum {string} 24 */ 25 webdriver.Key = { 26 NULL: '\uE000', 27 CANCEL: '\uE001', // ^break 28 HELP: '\uE002', 29 BACK_SPACE: '\uE003', 30 TAB: '\uE004', 31 CLEAR: '\uE005', 32 RETURN: '\uE006', 33 ENTER: '\uE007', 34 SHIFT: '\uE008', 35 CONTROL: '\uE009', 36 ALT: '\uE00A', 37 PAUSE: '\uE00B', 38 ESCAPE: '\uE00C', 39 SPACE: '\uE00D', 40 PAGE_UP: '\uE00E', 41 PAGE_DOWN: '\uE00F', 42 END: '\uE010', 43 HOME: '\uE011', 44 ARROW_LEFT: '\uE012', 45 LEFT: '\uE012', 46 ARROW_UP: '\uE013', 47 UP: '\uE013', 48 ARROW_RIGHT: '\uE014', 49 RIGHT: '\uE014', 50 ARROW_DOWN: '\uE015', 51 DOWN: '\uE015', 52 INSERT: '\uE016', 53 DELETE: '\uE017', 54 SEMICOLON: '\uE018', 55 EQUALS: '\uE019', 56 57 NUMPAD0: '\uE01A', // number pad keys 58 NUMPAD1: '\uE01B', 59 NUMPAD2: '\uE01C', 60 NUMPAD3: '\uE01D', 61 NUMPAD4: '\uE01E', 62 NUMPAD5: '\uE01F', 63 NUMPAD6: '\uE020', 64 NUMPAD7: '\uE021', 65 NUMPAD8: '\uE022', 66 NUMPAD9: '\uE023', 67 MULTIPLY: '\uE024', 68 ADD: '\uE025', 69 SEPARATOR: '\uE026', 70 SUBTRACT: '\uE027', 71 DECIMAL: '\uE028', 72 DIVIDE: '\uE029', 73 74 F1: '\uE031', // function keys 75 F2: '\uE032', 76 F3: '\uE033', 77 F4: '\uE034', 78 F5: '\uE035', 79 F6: '\uE036', 80 F7: '\uE037', 81 F8: '\uE038', 82 F9: '\uE039', 83 F10: '\uE03A', 84 F11: '\uE03B', 85 F12: '\uE03C', 86 87 COMMAND: '\uE03D', // Apple command key 88 META: '\uE03D' // alias for Windows key 89 }; lib/webdriver/locators.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Factory methods for the supported locator strategies. 17 */ 18 19 goog.provide('webdriver.By'); 20 goog.provide('webdriver.Locator'); 21 goog.provide('webdriver.Locator.Strategy'); 22 23 goog.require('goog.array'); 24 goog.require('goog.object'); 25 goog.require('goog.string'); 26 27 28 29 /** 30 * An element locator. 31 * @param {string} using The type of strategy to use for this locator. 32 * @param {string} value The search target of this locator. 33 * @constructor 34 */ 35 webdriver.Locator = function(using, value) { 36 37 /** 38 * The search strategy to use when searching for an element. 39 * @type {string} 40 */ 41 this.using = using; 42 43 /** 44 * The search target for this locator. 45 * @type {string} 46 */ 47 this.value = value; 48 }; 49 50 51 /** 52 * Creates a factory function for a {@link webdriver.Locator}. 53 * @param {string} type The type of locator for the factory. 54 * @return {function(string): !webdriver.Locator} The new factory function. 55 * @private 56 */ 57 webdriver.Locator.factory_ = function(type) { 58 return function(value) { 59 return new webdriver.Locator(type, value); 60 }; 61 }; 62 63 64 /** 65 * A collection of factory functions for creating {@link webdriver.Locator} 66 * instances. 67 */ 68 webdriver.By = {}; 69 // Exported to the global scope for legacy reasons. 70 goog.exportSymbol('By', webdriver.By); 71 72 73 /** 74 * Short-hand expressions for the primary element locator strategies. 75 * For example the following two statements are equivalent: 76 * <code><pre> 77 * var e1 = driver.findElement(webdriver.By.id('foo')); 78 * var e2 = driver.findElement({id: 'foo'}); 79 * </pre></code> 80 * 81 * <p>Care should be taken when using JavaScript minifiers (such as the 82 * Closure compiler), as locator hashes will always be parsed using 83 * the un-obfuscated properties listed below. 84 * 85 * @typedef {( 86 * {className: string}| 87 * {css: string}| 88 * {id: string}| 89 * {js: string}| 90 * {linkText: string}| 91 * {name: string}| 92 * {partialLinkText: string}| 93 * {tagName: string}| 94 * {xpath: string})} 95 */ 96 webdriver.By.Hash; 97 98 99 /** 100 * Locates elements that have a specific class name. The returned locator 101 * is equivalent to searching for elements with the CSS selector ".clazz". 102 * 103 * @param {string} className The class name to search for. 104 * @return {!webdriver.Locator} The new locator. 105 * @see http://www.w3.org/TR/2011/WD-html5-20110525/elements.html#classes 106 * @see http://www.w3.org/TR/CSS2/selector.html#class-html 107 */ 108 webdriver.By.className = webdriver.Locator.factory_('class name'); 109 110 111 /** 112 * Locates elements using a CSS selector. For browsers that do not support 113 * CSS selectors, WebDriver implementations may return an 114 * {@link bot.Error.State.INVALID_SELECTOR invalid selector} error. An 115 * implementation may, however, emulate the CSS selector API. 116 * 117 * @param {string} selector The CSS selector to use. 118 * @return {!webdriver.Locator} The new locator. 119 * @see http://www.w3.org/TR/CSS2/selector.html 120 */ 121 webdriver.By.css = webdriver.Locator.factory_('css selector'); 122 123 124 /** 125 * Locates an element by its ID. 126 * 127 * @param {string} id The ID to search for. 128 * @return {!webdriver.Locator} The new locator. 129 */ 130 webdriver.By.id = webdriver.Locator.factory_('id'); 131 132 133 /** 134 * Locates link elements whose {@link webdriver.WebElement#getText visible 135 * text} matches the given string. 136 * 137 * @param {string} text The link text to search for. 138 * @return {!webdriver.Locator} The new locator. 139 */ 140 webdriver.By.linkText = webdriver.Locator.factory_('link text'); 141 142 143 /** 144 * Locates an elements by evaluating a 145 * {@link webdriver.WebDriver#executeScript JavaScript expression}. 146 * The result of this expression must be an element or list of elements. 147 * 148 * @param {!(string|Function)} script The script to execute. 149 * @param {...*} var_args The arguments to pass to the script. 150 * @return {function(!webdriver.WebDriver): !webdriver.promise.Promise} A new, 151 * JavaScript-based locator function. 152 */ 153 webdriver.By.js = function(script, var_args) { 154 var args = goog.array.slice(arguments, 0); 155 return function(driver) { 156 return driver.executeScript.apply(driver, args); 157 }; 158 }; 159 160 161 /** 162 * Locates elements whose {@code name} attribute has the given value. 163 * 164 * @param {string} name The name attribute to search for. 165 * @return {!webdriver.Locator} The new locator. 166 */ 167 webdriver.By.name = webdriver.Locator.factory_('name'); 168 169 170 /** 171 * Locates link elements whose {@link webdriver.WebElement#getText visible 172 * text} contains the given substring. 173 * 174 * @param {string} text The substring to check for in a link's visible text. 175 * @return {!webdriver.Locator} The new locator. 176 */ 177 webdriver.By.partialLinkText = webdriver.Locator.factory_( 178 'partial link text'); 179 180 181 /** 182 * Locates elements with a given tag name. The returned locator is 183 * equivalent to using the {@code getElementsByTagName} DOM function. 184 * 185 * @param {string} text The substring to check for in a link's visible text. 186 * @return {!webdriver.Locator} The new locator. 187 * @see http://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html 188 */ 189 webdriver.By.tagName = webdriver.Locator.factory_('tag name'); 190 191 192 /** 193 * Locates elements matching a XPath selector. Care should be taken when 194 * using an XPath selector with a {@link webdriver.WebElement} as WebDriver 195 * will respect the context in the specified in the selector. For example, 196 * given the selector {@code "//div"}, WebDriver will search from the 197 * document root regardless of whether the locator was used with a 198 * WebElement. 199 * 200 * @param {string} xpath The XPath selector to use. 201 * @return {!webdriver.Locator} The new locator. 202 * @see http://www.w3.org/TR/xpath/ 203 */ 204 webdriver.By.xpath = webdriver.Locator.factory_('xpath'); 205 206 207 /** 208 * Maps {@link webdriver.By.Hash} keys to the appropriate factory function. 209 * @type {!Object.<string, function(string): !(Function|webdriver.Locator)>} 210 * @const 211 */ 212 webdriver.Locator.Strategy = { 213 'className': webdriver.By.className, 214 'css': webdriver.By.css, 215 'id': webdriver.By.id, 216 'js': webdriver.By.js, 217 'linkText': webdriver.By.linkText, 218 'name': webdriver.By.name, 219 'partialLinkText': webdriver.By.partialLinkText, 220 'tagName': webdriver.By.tagName, 221 'xpath': webdriver.By.xpath 222 }; 223 224 225 /** 226 * Verifies that a {@code value} is a valid locator to use for searching for 227 * elements on the page. 228 * 229 * @param {*} value The value to check is a valid locator. 230 * @return {!(webdriver.Locator|Function)} A valid locator object or function. 231 * @throws {TypeError} If the given value is an invalid locator. 232 */ 233 webdriver.Locator.checkLocator = function(value) { 234 if (goog.isFunction(value) || value instanceof webdriver.Locator) { 235 return value; 236 } 237 for (var key in value) { 238 if (value.hasOwnProperty(key) && 239 webdriver.Locator.Strategy.hasOwnProperty(key)) { 240 return webdriver.Locator.Strategy[key](value[key]); 241 } 242 } 243 throw new TypeError('Invalid locator'); 244 }; 245 246 247 248 /** @override */ 249 webdriver.Locator.prototype.toString = function() { 250 return 'By.' + this.using.replace(/ ([a-z])/g, function(all, match) { 251 return match.toUpperCase(); 252 }) + '(' + goog.string.quote(this.value) + ')'; 253 }; lib/webdriver/logging.js
1 // Copyright 2013 Selenium comitters 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 goog.provide('webdriver.logging'); 17 goog.provide('webdriver.logging.Preferences'); 18 19 goog.require('goog.object'); 20 21 22 /** 23 * Log level names from WebDriver's JSON wire protocol. 24 * @enum {string} 25 */ 26 webdriver.logging.LevelName = { 27 ALL: 'ALL', 28 DEBUG: 'DEBUG', 29 INFO: 'INFO', 30 WARNING: 'WARNING', 31 SEVERE: 'SEVERE', 32 OFF: 'OFF' 33 }; 34 35 36 /** 37 * Logging levels. 38 * @enum {{value: number, name: webdriver.logging.LevelName}} 39 */ 40 webdriver.logging.Level = { 41 ALL: {value: Number.MIN_VALUE, name: webdriver.logging.LevelName.ALL}, 42 DEBUG: {value: 700, name: webdriver.logging.LevelName.DEBUG}, 43 INFO: {value: 800, name: webdriver.logging.LevelName.INFO}, 44 WARNING: {value: 900, name: webdriver.logging.LevelName.WARNING}, 45 SEVERE: {value: 1000, name: webdriver.logging.LevelName.SEVERE}, 46 OFF: {value: Number.MAX_VALUE, name: webdriver.logging.LevelName.OFF} 47 }; 48 49 50 /** 51 * Converts a level name or value to a {@link webdriver.logging.Level} value. 52 * If the name/value is not recognized, {@link webdriver.logging.Level.ALL} 53 * will be returned. 54 * @param {(number|string)} nameOrValue The log level name, or value, to 55 * convert . 56 * @return {!webdriver.logging.Level} The converted level. 57 */ 58 webdriver.logging.getLevel = function(nameOrValue) { 59 var predicate = goog.isString(nameOrValue) ? 60 function(val) { return val.name === nameOrValue; } : 61 function(val) { return val.value === nameOrValue; }; 62 63 return goog.object.findValue(webdriver.logging.Level, predicate) || 64 webdriver.logging.Level.ALL; 65 }; 66 67 68 /** 69 * Common log types. 70 * @enum {string} 71 */ 72 webdriver.logging.Type = { 73 /** Logs originating from the browser. */ 74 BROWSER: 'browser', 75 /** Logs from a WebDriver client. */ 76 CLIENT: 'client', 77 /** Logs from a WebDriver implementation. */ 78 DRIVER: 'driver', 79 /** Logs related to performance. */ 80 PERFORMANCE: 'performance', 81 /** Logs from the remote server. */ 82 SERVER: 'server' 83 }; 84 85 86 /** 87 * A hash describing log preferences. 88 * @typedef {Object.<webdriver.logging.Type, webdriver.logging.LevelName>} 89 */ 90 webdriver.logging.Preferences; 91 92 93 /** 94 * A single log entry. 95 * @param {(!webdriver.logging.Level|string)} level The entry level. 96 * @param {string} message The log message. 97 * @param {number=} opt_timestamp The time this entry was generated, in 98 * milliseconds since 0:00:00, January 1, 1970 UTC. If omitted, the 99 * current time will be used. 100 * @param {string=} opt_type The log type, if known. 101 * @constructor 102 */ 103 webdriver.logging.Entry = function(level, message, opt_timestamp, opt_type) { 104 105 /** @type {!webdriver.logging.Level} */ 106 this.level = 107 goog.isString(level) ? webdriver.logging.getLevel(level) : level; 108 109 /** @type {string} */ 110 this.message = message; 111 112 /** @type {number} */ 113 this.timestamp = goog.isNumber(opt_timestamp) ? opt_timestamp : goog.now(); 114 115 /** @type {string} */ 116 this.type = opt_type || ''; 117 }; 118 119 120 /** 121 * @return {{level: string, message: string, timestamp: number, 122 * type: string}} The JSON representation of this entry. 123 */ 124 webdriver.logging.Entry.prototype.toJSON = function() { 125 return { 126 'level': this.level.name, 127 'message': this.message, 128 'timestamp': this.timestamp, 129 'type': this.type 130 }; 131 }; 132 133 134 /** 135 * Converts a {@link goog.debug.LogRecord} into a 136 * {@link webdriver.logging.Entry}. 137 * @param {!goog.debug.LogRecord} logRecord The record to convert. 138 * @param {string=} opt_type The log type. 139 * @return {!webdriver.logging.Entry} The converted entry. 140 */ 141 webdriver.logging.Entry.fromClosureLogRecord = function(logRecord, opt_type) { 142 var closureLevel = logRecord.getLevel(); 143 var level = webdriver.logging.Level.SEVERE; 144 145 if (closureLevel.value <= webdriver.logging.Level.DEBUG.value) { 146 level = webdriver.logging.Level.DEBUG; 147 } else if (closureLevel.value <= webdriver.logging.Level.INFO.value) { 148 level = webdriver.logging.Level.INFO; 149 } else if (closureLevel.value <= webdriver.logging.Level.WARNING.value) { 150 level = webdriver.logging.Level.WARNING; 151 } 152 153 return new webdriver.logging.Entry( 154 level, 155 '[' + logRecord.getLoggerName() + '] ' + logRecord.getMessage(), 156 logRecord.getMillis(), 157 opt_type); 158 }; lib/webdriver/process.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Provides access to the current process' environment variables. 17 * When running in node, this is simply a wrapper for {@code process.env}. 18 * When running in a browser, environment variables are loaded by parsing the 19 * current URL's query string. Variables that have more than one variable will 20 * be initialized to the JSON representation of the array of all values, 21 * otherwise the variable will be initialized to a sole string value. If a 22 * variable does not have any values, but is nonetheless present in the query 23 * string, it will be initialized to an empty string. 24 * After the initial parsing, environment variables must be queried and set 25 * through the API defined in this file. 26 */ 27 28 goog.provide('webdriver.process'); 29 30 goog.require('goog.Uri'); 31 goog.require('goog.array'); 32 goog.require('goog.json'); 33 34 35 /** 36 * @return {boolean} Whether the current process is Node's native process 37 * object. 38 */ 39 webdriver.process.isNative = function() { 40 return webdriver.process.IS_NATIVE_PROCESS_; 41 }; 42 43 44 /** 45 * Queries for a named environment variable. 46 * @param {string} name The name of the environment variable to look up. 47 * @param {string=} opt_default The default value if the named variable is not 48 * defined. 49 * @return {string} The queried environment variable. 50 */ 51 webdriver.process.getEnv = function(name, opt_default) { 52 var value = webdriver.process.PROCESS_.env[name]; 53 return goog.isDefAndNotNull(value) ? value : opt_default; 54 }; 55 56 57 /** 58 * Sets an environment value. If the new value is either null or undefined, the 59 * environment variable will be cleared. 60 * @param {string} name The value to set. 61 * @param {*} value The new value; will be coerced to a string. 62 */ 63 webdriver.process.setEnv = function(name, value) { 64 webdriver.process.PROCESS_.env[name] = 65 goog.isDefAndNotNull(value) ? value + '' : null; 66 }; 67 68 69 /** 70 * Whether the current environment is using Node's native process object. 71 * @private {boolean} 72 * @const 73 */ 74 webdriver.process.IS_NATIVE_PROCESS_ = typeof process !== 'undefined'; 75 76 77 /** 78 * Initializes a process object for use in a browser window. 79 * @param {!Window=} opt_window The window object to initialize the process 80 * from; if not specified, will default to the current window. Should only 81 * be set for unit testing. 82 * @return {!Object} The new process object. 83 * @private 84 */ 85 webdriver.process.initBrowserProcess_ = function(opt_window) { 86 var process = {'env': {}}; 87 88 var win = opt_window; 89 if (!win && typeof window != 'undefined') { 90 win = window; 91 } 92 93 // Initialize the global error handler. 94 if (win) { 95 // Initialize the environment variable map by parsing the current URL query 96 // string. 97 if (win.location) { 98 var data = new goog.Uri(win.location).getQueryData(); 99 goog.array.forEach(data.getKeys(), function(key) { 100 var values = data.getValues(key); 101 process.env[key] = values.length == 0 ? '' : 102 values.length == 1 ? values[0] : 103 goog.json.serialize(values); 104 }); 105 } 106 } 107 108 return process; 109 }; 110 111 112 /** 113 * The global process object to use. Will either be Node's global 114 * {@code process} object, or an approximation of it for use in a browser 115 * environment. 116 * @private {!Object} 117 * @const 118 */ 119 webdriver.process.PROCESS_ = webdriver.process.IS_NATIVE_PROCESS_ ? process : 120 webdriver.process.initBrowserProcess_(); lib/webdriver/promise.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @license Portions of this code are from the Dojo toolkit, received under the 17 * BSD License: 18 * Redistribution and use in source and binary forms, with or without 19 * modification, are permitted provided that the following conditions are met: 20 * 21 * * Redistributions of source code must retain the above copyright notice, 22 * this list of conditions and the following disclaimer. 23 * * Redistributions in binary form must reproduce the above copyright notice, 24 * this list of conditions and the following disclaimer in the documentation 25 * and/or other materials provided with the distribution. 26 * * Neither the name of the Dojo Foundation nor the names of its contributors 27 * may be used to endorse or promote products derived from this software 28 * without specific prior written permission. 29 * 30 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 31 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 32 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 33 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 34 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 35 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 36 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 37 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 38 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 39 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 40 * POSSIBILITY OF SUCH DAMAGE. 41 */ 42 43 /** 44 * @fileoverview A promise implementation based on the CommonJS promise/A and 45 * promise/B proposals. For more information, see 46 * http://wiki.commonjs.org/wiki/Promises. 47 */ 48 49 goog.provide('webdriver.promise'); 50 goog.provide('webdriver.promise.ControlFlow'); 51 goog.provide('webdriver.promise.ControlFlow.Timer'); 52 goog.provide('webdriver.promise.Deferred'); 53 goog.provide('webdriver.promise.Promise'); 54 55 goog.require('goog.array'); 56 goog.require('goog.debug.Error'); 57 goog.require('goog.object'); 58 goog.require('webdriver.EventEmitter'); 59 goog.require('webdriver.stacktrace.Snapshot'); 60 61 62 63 /** 64 * Represents the eventual value of a completed operation. Each promise may be 65 * in one of three states: pending, resolved, or rejected. Each promise starts 66 * in the pending state and may make a single transition to either a 67 * fulfilled or failed state. 68 * 69 * <p/>This class is based on the Promise/A proposal from CommonJS. Additional 70 * functions are provided for API compatibility with Dojo Deferred objects. 71 * 72 * @constructor 73 * @template T 74 * @see http://wiki.commonjs.org/wiki/Promises/A 75 */ 76 webdriver.promise.Promise = function() { 77 }; 78 79 80 /** 81 * Cancels the computation of this promise's value, rejecting the promise in the 82 * process. 83 * @param {*} reason The reason this promise is being cancelled. If not an 84 * {@code Error}, one will be created using the value's string 85 * representation. 86 */ 87 webdriver.promise.Promise.prototype.cancel = function(reason) { 88 throw new TypeError('Unimplemented function: "cancel"'); 89 }; 90 91 92 /** @return {boolean} Whether this promise's value is still being computed. */ 93 webdriver.promise.Promise.prototype.isPending = function() { 94 throw new TypeError('Unimplemented function: "isPending"'); 95 }; 96 97 98 /** 99 * Registers listeners for when this instance is resolved. This function most 100 * overridden by subtypes. 101 * 102 * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback The 103 * function to call if this promise is successfully resolved. The function 104 * should expect a single argument: the promise's resolved value. 105 * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback The 106 * function to call if this promise is rejected. The function should expect 107 * a single argument: the rejection reason. 108 * @return {!webdriver.promise.Promise.<R>} A new promise which will be 109 * resolved with the result of the invoked callback. 110 * @template R 111 */ 112 webdriver.promise.Promise.prototype.then = function( 113 opt_callback, opt_errback) { 114 throw new TypeError('Unimplemented function: "then"'); 115 }; 116 117 118 /** 119 * Registers a listener for when this promise is rejected. This is synonymous 120 * with the {@code catch} clause in a synchronous API: 121 * <pre><code> 122 * // Synchronous API: 123 * try { 124 * doSynchronousWork(); 125 * } catch (ex) { 126 * console.error(ex); 127 * } 128 * 129 * // Asynchronous promise API: 130 * doAsynchronousWork().thenCatch(function(ex) { 131 * console.error(ex); 132 * }); 133 * </code></pre> 134 * 135 * @param {function(*): (R|webdriver.promise.Promise.<R>)} errback The function 136 * to call if this promise is rejected. The function should expect a single 137 * argument: the rejection reason. 138 * @return {!webdriver.promise.Promise.<R>} A new promise which will be 139 * resolved with the result of the invoked callback. 140 * @template R 141 */ 142 webdriver.promise.Promise.prototype.thenCatch = function(errback) { 143 return this.then(null, errback); 144 }; 145 146 147 /** 148 * Registers a listener to invoke when this promise is resolved, regardless 149 * of whether the promise's value was successfully computed. This function 150 * is synonymous with the {@code finally} clause in a synchronous API: 151 * <pre><code> 152 * // Synchronous API: 153 * try { 154 * doSynchronousWork(); 155 * } finally { 156 * cleanUp(); 157 * } 158 * 159 * // Asynchronous promise API: 160 * doAsynchronousWork().thenFinally(cleanUp); 161 * </code></pre> 162 * 163 * <b>Note:</b> similar to the {@code finally} clause, if the registered 164 * callback returns a rejected promise or throws an error, it will silently 165 * replace the rejection error (if any) from this promise: 166 * <pre><code> 167 * try { 168 * throw Error('one'); 169 * } finally { 170 * throw Error('two'); // Hides Error: one 171 * } 172 * 173 * webdriver.promise.rejected(Error('one')) 174 * .thenFinally(function() { 175 * throw Error('two'); // Hides Error: one 176 * }); 177 * </code></pre> 178 * 179 * 180 * @param {function(): (R|webdriver.promise.Promise.<R>)} callback The function 181 * to call when this promise is resolved. 182 * @return {!webdriver.promise.Promise.<R>} A promise that will be fulfilled 183 * with the callback result. 184 * @template R 185 */ 186 webdriver.promise.Promise.prototype.thenFinally = function(callback) { 187 return this.then(callback, function(err) { 188 var value = callback(); 189 if (webdriver.promise.isPromise(value)) { 190 return value.then(function() { 191 throw err; 192 }); 193 } 194 throw err; 195 }); 196 }; 197 198 199 200 /** 201 * Represents a value that will be resolved at some point in the future. This 202 * class represents the protected "producer" half of a Promise - each Deferred 203 * has a {@code promise} property that may be returned to consumers for 204 * registering callbacks, reserving the ability to resolve the deferred to the 205 * producer. 206 * 207 * <p>If this Deferred is rejected and there are no listeners registered before 208 * the next turn of the event loop, the rejection will be passed to the 209 * {@link webdriver.promise.ControlFlow} as an unhandled failure. 210 * 211 * <p>If this Deferred is cancelled, the cancellation reason will be forward to 212 * the Deferred's canceller function (if provided). The canceller may return a 213 * truth-y value to override the reason provided for rejection. 214 * 215 * @param {Function=} opt_canceller Function to call when cancelling the 216 * computation of this instance's value. 217 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow 218 * this instance was created under. This should only be provided during 219 * unit tests. 220 * @constructor 221 * @extends {webdriver.promise.Promise.<T>} 222 * @template T 223 */ 224 webdriver.promise.Deferred = function(opt_canceller, opt_flow) { 225 /* NOTE: This class's implementation diverges from the prototypical style 226 * used in the rest of the atoms library. This was done intentionally to 227 * protect the internal Deferred state from consumers, as outlined by 228 * http://wiki.commonjs.org/wiki/Promises 229 */ 230 goog.base(this); 231 232 var flow = opt_flow || webdriver.promise.controlFlow(); 233 234 /** 235 * The listeners registered with this Deferred. Each element in the list will 236 * be a 3-tuple of the callback function, errback function, and the 237 * corresponding deferred object. 238 * @type {!Array.<!webdriver.promise.Deferred.Listener_>} 239 */ 240 var listeners = []; 241 242 /** 243 * Whether this Deferred's resolution was ever handled by a listener. 244 * If the Deferred is rejected and its value is not handled by a listener 245 * before the next turn of the event loop, the error will be passed to the 246 * global error handler. 247 * @type {boolean} 248 */ 249 var handled = false; 250 251 /** 252 * Key for the timeout used to delay reproting an unhandled rejection to the 253 * parent {@link webdriver.promise.ControlFlow}. 254 * @type {?number} 255 */ 256 var pendingRejectionKey = null; 257 258 /** 259 * This Deferred's current state. 260 * @type {!webdriver.promise.Deferred.State_} 261 */ 262 var state = webdriver.promise.Deferred.State_.PENDING; 263 264 /** 265 * This Deferred's resolved value; set when the state transitions from 266 * {@code webdriver.promise.Deferred.State_.PENDING}. 267 * @type {*} 268 */ 269 var value; 270 271 /** @return {boolean} Whether this promise's value is still pending. */ 272 function isPending() { 273 return state == webdriver.promise.Deferred.State_.PENDING; 274 } 275 276 /** 277 * Removes all of the listeners previously registered on this deferred. 278 * @throws {Error} If this deferred has already been resolved. 279 */ 280 function removeAll() { 281 listeners = []; 282 } 283 284 /** 285 * Resolves this deferred. If the new value is a promise, this function will 286 * wait for it to be resolved before notifying the registered listeners. 287 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new 288 * state. 289 * @param {*} newValue The deferred's new value. 290 */ 291 function resolve(newState, newValue) { 292 if (webdriver.promise.Deferred.State_.PENDING !== state) { 293 return; 294 } 295 296 state = webdriver.promise.Deferred.State_.BLOCKED; 297 298 if (webdriver.promise.isPromise(newValue) && newValue !== self) { 299 var onFulfill = goog.partial(notifyAll, newState); 300 var onReject = goog.partial( 301 notifyAll, webdriver.promise.Deferred.State_.REJECTED); 302 if (newValue instanceof webdriver.promise.Deferred) { 303 newValue.then(onFulfill, onReject); 304 } else { 305 webdriver.promise.asap(newValue, onFulfill, onReject); 306 } 307 308 } else { 309 notifyAll(newState, newValue); 310 } 311 } 312 313 /** 314 * Notifies all of the listeners registered with this Deferred that its state 315 * has changed. 316 * @param {!webdriver.promise.Deferred.State_} newState The deferred's new 317 * state. 318 * @param {*} newValue The deferred's new value. 319 */ 320 function notifyAll(newState, newValue) { 321 if (newState === webdriver.promise.Deferred.State_.REJECTED && 322 // We cannot check instanceof Error since the object may have been 323 // created in a different JS context. 324 goog.isObject(newValue) && goog.isString(newValue.message)) { 325 newValue = flow.annotateError(/** @type {!Error} */(newValue)); 326 } 327 328 state = newState; 329 value = newValue; 330 while (listeners.length) { 331 notify(listeners.shift()); 332 } 333 334 if (!handled && state == webdriver.promise.Deferred.State_.REJECTED) { 335 pendingRejectionKey = propagateError(value); 336 } 337 } 338 339 /** 340 * Propagates an unhandled rejection to the parent ControlFlow in a 341 * future turn of the JavaScript event loop. 342 * @param {*} error The error value to report. 343 * @return {number} The key for the registered timeout. 344 */ 345 function propagateError(error) { 346 flow.pendingRejections_ += 1; 347 return flow.timer.setTimeout(function() { 348 flow.pendingRejections_ -= 1; 349 flow.abortFrame_(error); 350 }, 0); 351 } 352 353 /** 354 * Notifies a single listener of this Deferred's change in state. 355 * @param {!webdriver.promise.Deferred.Listener_} listener The listener to 356 * notify. 357 */ 358 function notify(listener) { 359 var func = state == webdriver.promise.Deferred.State_.RESOLVED ? 360 listener.callback : listener.errback; 361 if (func) { 362 flow.runInNewFrame_(goog.partial(func, value), 363 listener.fulfill, listener.reject); 364 } else if (state == webdriver.promise.Deferred.State_.REJECTED) { 365 listener.reject(value); 366 } else { 367 listener.fulfill(value); 368 } 369 } 370 371 /** 372 * The consumer promise for this instance. Provides protected access to the 373 * callback registering functions. 374 * @type {!webdriver.promise.Promise.<T>} 375 */ 376 var promise = new webdriver.promise.Promise(); 377 378 /** 379 * Registers a callback on this Deferred. 380 * 381 * @param {?(function(T): (R|webdriver.promise.Promise.<R>))=} opt_callback . 382 * @param {?(function(*): (R|webdriver.promise.Promise.<R>))=} opt_errback . 383 * @return {!webdriver.promise.Promise.<R>} A new promise representing the 384 * result of the callback. 385 * @template R 386 * @see webdriver.promise.Promise#then 387 */ 388 function then(opt_callback, opt_errback) { 389 // Avoid unnecessary allocations if we weren't given any callback functions. 390 if (!opt_callback && !opt_errback) { 391 return promise; 392 } 393 394 // The moment a listener is registered, we consider this deferred to be 395 // handled; the callback must handle any rejection errors. 396 handled = true; 397 if (pendingRejectionKey) { 398 flow.pendingRejections_ -= 1; 399 flow.timer.clearTimeout(pendingRejectionKey); 400 } 401 402 var deferred = new webdriver.promise.Deferred(cancel, flow); 403 var listener = { 404 callback: opt_callback, 405 errback: opt_errback, 406 fulfill: deferred.fulfill, 407 reject: deferred.reject 408 }; 409 410 if (state == webdriver.promise.Deferred.State_.PENDING || 411 state == webdriver.promise.Deferred.State_.BLOCKED) { 412 listeners.push(listener); 413 } else { 414 notify(listener); 415 } 416 417 return deferred.promise; 418 } 419 420 var self = this; 421 422 /** 423 * Resolves this promise with the given value. If the value is itself a 424 * promise and not a reference to this deferred, this instance will wait for 425 * it before resolving. 426 * @param {T=} opt_value The fulfilled value. 427 */ 428 function fulfill(opt_value) { 429 resolve(webdriver.promise.Deferred.State_.RESOLVED, opt_value); 430 } 431 432 /** 433 * Rejects this promise. If the error is itself a promise, this instance will 434 * be chained to it and be rejected with the error's resolved value. 435 * @param {*=} opt_error The rejection reason, typically either a 436 * {@code Error} or a {@code string}. 437 */ 438 function reject(opt_error) { 439 resolve(webdriver.promise.Deferred.State_.REJECTED, opt_error); 440 } 441 442 /** 443 * Attempts to cancel the computation of this instance's value. This attempt 444 * will silently fail if this instance has already resolved. 445 * @param {*=} opt_reason The reason for cancelling this promise. 446 */ 447 function cancel(opt_reason) { 448 if (!isPending()) { 449 return; 450 } 451 452 if (opt_canceller) { 453 opt_reason = opt_canceller(opt_reason) || opt_reason; 454 } 455 456 reject(opt_reason); 457 } 458 459 this.promise = promise; 460 this.promise.then = this.then = then; 461 this.promise.cancel = this.cancel = cancel; 462 this.promise.isPending = this.isPending = isPending; 463 this.fulfill = fulfill; 464 this.reject = this.errback = reject; 465 466 // Only expose this function to our internal classes. 467 // TODO: find a cleaner way of handling this. 468 if (this instanceof webdriver.promise.Task_) { 469 this.removeAll = removeAll; 470 } 471 472 // Export symbols necessary for the contract on this object to work in 473 // compiled mode. 474 goog.exportProperty(this, 'then', this.then); 475 goog.exportProperty(this, 'cancel', cancel); 476 goog.exportProperty(this, 'fulfill', fulfill); 477 goog.exportProperty(this, 'reject', reject); 478 goog.exportProperty(this, 'isPending', isPending); 479 goog.exportProperty(this, 'promise', this.promise); 480 goog.exportProperty(this.promise, 'then', this.then); 481 goog.exportProperty(this.promise, 'cancel', cancel); 482 goog.exportProperty(this.promise, 'isPending', isPending); 483 }; 484 goog.inherits(webdriver.promise.Deferred, webdriver.promise.Promise); 485 486 487 /** 488 * Type definition for a listener registered on a Deferred object. 489 * @typedef {{callback:(Function|undefined), 490 * errback:(Function|undefined), 491 * fulfill: function(*), reject: function(*)}} 492 * @private 493 */ 494 webdriver.promise.Deferred.Listener_; 495 496 497 /** 498 * The three states a {@link webdriver.promise.Deferred} object may be in. 499 * @enum {number} 500 * @private 501 */ 502 webdriver.promise.Deferred.State_ = { 503 REJECTED: -1, 504 PENDING: 0, 505 BLOCKED: 1, 506 RESOLVED: 2 507 }; 508 509 510 /** 511 * Tests if a value is an Error-like object. This is more than an straight 512 * instanceof check since the value may originate from another context. 513 * @param {*} value The value to test. 514 * @return {boolean} Whether the value is an error. 515 * @private 516 */ 517 webdriver.promise.isError_ = function(value) { 518 return value instanceof Error || 519 goog.isObject(value) && 520 (Object.prototype.toString.call(value) === '[object Error]' || 521 // A special test for goog.testing.JsUnitException. 522 value.isJsUnitException); 523 524 }; 525 526 527 /** 528 * Determines whether a {@code value} should be treated as a promise. 529 * Any object whose "then" property is a function will be considered a promise. 530 * 531 * @param {*} value The value to test. 532 * @return {boolean} Whether the value is a promise. 533 */ 534 webdriver.promise.isPromise = function(value) { 535 return !!value && goog.isObject(value) && 536 // Use array notation so the Closure compiler does not obfuscate away our 537 // contract. 538 goog.isFunction(value['then']); 539 }; 540 541 542 /** 543 * Creates a promise that will be resolved at a set time in the future. 544 * @param {number} ms The amount of time, in milliseconds, to wait before 545 * resolving the promise. 546 * @return {!webdriver.promise.Promise} The promise. 547 */ 548 webdriver.promise.delayed = function(ms) { 549 var timer = webdriver.promise.controlFlow().timer; 550 var key; 551 var deferred = new webdriver.promise.Deferred(function() { 552 timer.clearTimeout(key); 553 }); 554 key = timer.setTimeout(deferred.fulfill, ms); 555 return deferred.promise; 556 }; 557 558 559 /** 560 * Creates a new deferred object. 561 * @param {Function=} opt_canceller Function to call when cancelling the 562 * computation of this instance's value. 563 * @return {!webdriver.promise.Deferred.<T>} The new deferred object. 564 * @template T 565 */ 566 webdriver.promise.defer = function(opt_canceller) { 567 return new webdriver.promise.Deferred(opt_canceller); 568 }; 569 570 571 /** 572 * Creates a promise that has been resolved with the given value. 573 * @param {T=} opt_value The resolved value. 574 * @return {!webdriver.promise.Promise.<T>} The resolved promise. 575 * @template T 576 */ 577 webdriver.promise.fulfilled = function(opt_value) { 578 if (opt_value instanceof webdriver.promise.Promise) { 579 return opt_value; 580 } 581 var deferred = new webdriver.promise.Deferred(); 582 deferred.fulfill(opt_value); 583 return deferred.promise; 584 }; 585 586 587 /** 588 * Creates a promise that has been rejected with the given reason. 589 * @param {*=} opt_reason The rejection reason; may be any value, but is 590 * usually an Error or a string. 591 * @return {!webdriver.promise.Promise.<T>} The rejected promise. 592 * @template T 593 */ 594 webdriver.promise.rejected = function(opt_reason) { 595 var deferred = new webdriver.promise.Deferred(); 596 deferred.reject(opt_reason); 597 return deferred.promise; 598 }; 599 600 601 /** 602 * Wraps a function that is assumed to be a node-style callback as its final 603 * argument. This callback takes two arguments: an error value (which will be 604 * null if the call succeeded), and the success value as the second argument. 605 * If the call fails, the returned promise will be rejected, otherwise it will 606 * be resolved with the result. 607 * @param {!Function} fn The function to wrap. 608 * @return {!webdriver.promise.Promise} A promise that will be resolved with the 609 * result of the provided function's callback. 610 */ 611 webdriver.promise.checkedNodeCall = function(fn) { 612 var deferred = new webdriver.promise.Deferred(function() { 613 throw Error('This Deferred may not be cancelled'); 614 }); 615 try { 616 fn(function(error, value) { 617 error ? deferred.reject(error) : deferred.fulfill(value); 618 }); 619 } catch (ex) { 620 deferred.reject(ex); 621 } 622 return deferred.promise; 623 }; 624 625 626 /** 627 * Registers an observer on a promised {@code value}, returning a new promise 628 * that will be resolved when the value is. If {@code value} is not a promise, 629 * then the return promise will be immediately resolved. 630 * @param {*} value The value to observe. 631 * @param {Function=} opt_callback The function to call when the value is 632 * resolved successfully. 633 * @param {Function=} opt_errback The function to call when the value is 634 * rejected. 635 * @return {!webdriver.promise.Promise} A new promise. 636 */ 637 webdriver.promise.when = function(value, opt_callback, opt_errback) { 638 if (value instanceof webdriver.promise.Promise) { 639 return value.then(opt_callback, opt_errback); 640 } 641 642 var deferred = new webdriver.promise.Deferred(); 643 644 webdriver.promise.asap(value, deferred.fulfill, deferred.reject); 645 646 return deferred.then(opt_callback, opt_errback); 647 }; 648 649 650 /** 651 * Invokes the appropriate callback function as soon as a promised 652 * {@code value} is resolved. This function is similar to 653 * {@link webdriver.promise.when}, except it does not return a new promise. 654 * @param {*} value The value to observe. 655 * @param {Function} callback The function to call when the value is 656 * resolved successfully. 657 * @param {Function=} opt_errback The function to call when the value is 658 * rejected. 659 */ 660 webdriver.promise.asap = function(value, callback, opt_errback) { 661 if (webdriver.promise.isPromise(value)) { 662 value.then(callback, opt_errback); 663 664 // Maybe a Dojo-like deferred object? 665 } else if (!!value && goog.isObject(value) && 666 goog.isFunction(value.addCallbacks)) { 667 value.addCallbacks(callback, opt_errback); 668 669 // A raw value, return a resolved promise. 670 } else if (callback) { 671 callback(value); 672 } 673 }; 674 675 676 /** 677 * Given an array of promises, will return a promise that will be fulfilled 678 * with the fulfillment values of the input array's values. If any of the 679 * input array's promises are rejected, the returned promise will be rejected 680 * with the same reason. 681 * 682 * @param {!Array.<(T|!webdriver.promise.Promise.<T>)>} arr An array of 683 * promises to wait on. 684 * @return {!webdriver.promise.Promise.<!Array.<T>>} A promise that is 685 * fulfilled with an array containing the fulfilled values of the 686 * input array, or rejected with the same reason as the first 687 * rejected value. 688 * @template T 689 */ 690 webdriver.promise.all = function(arr) { 691 var n = arr.length; 692 if (!n) { 693 return webdriver.promise.fulfilled([]); 694 } 695 696 var toFulfill = n; 697 var result = webdriver.promise.defer(); 698 var values = []; 699 700 var onFulfill = function(index, value) { 701 values[index] = value; 702 toFulfill--; 703 if (toFulfill == 0) { 704 result.fulfill(values); 705 } 706 }; 707 708 for (var i = 0; i < n; ++i) { 709 webdriver.promise.asap( 710 arr[i], goog.partial(onFulfill, i), result.reject); 711 } 712 713 return result.promise; 714 }; 715 716 717 /** 718 * Calls a function for each element in an array and inserts the result into a 719 * new array, which is used as the fulfillment value of the promise returned 720 * by this function. 721 * 722 * <p>If the return value of the mapping function is a promise, this function 723 * will wait for it to be fulfilled before inserting it into the new array. 724 * 725 * <p>If the mapping function throws or returns a rejected promise, the 726 * promise returned by this function will be rejected with the same reason. 727 * Only the first failure will be reported; all subsequent errors will be 728 * silently ignored. 729 * 730 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The 731 * array to iterator over, or a promise that will resolve to said array. 732 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ?} fn The 733 * function to call for each element in the array. This function should 734 * expect three arguments (the element, the index, and the array itself. 735 * @param {SELF=} opt_self The object to be used as the value of 'this' within 736 * {@code fn}. 737 * @template TYPE, SELF 738 */ 739 webdriver.promise.map = function(arr, fn, opt_self) { 740 return webdriver.promise.when(arr, function(arr) { 741 var result = goog.array.map(arr, fn, opt_self); 742 return webdriver.promise.all(result); 743 }); 744 }; 745 746 747 /** 748 * Calls a function for each element in an array, and if the function returns 749 * true adds the element to a new array. 750 * 751 * <p>If the return value of the filter function is a promise, this function 752 * will wait for it to be fulfilled before determining whether to insert the 753 * element into the new array. 754 * 755 * <p>If the filter function throws or returns a rejected promise, the promise 756 * returned by this function will be rejected with the same reason. Only the 757 * first failure will be reported; all subsequent errors will be silently 758 * ignored. 759 * 760 * @param {!(Array.<TYPE>|webdriver.promise.Promise.<!Array.<TYPE>>)} arr The 761 * array to iterator over, or a promise that will resolve to said array. 762 * @param {function(this: SELF, TYPE, number, !Array.<TYPE>): ( 763 * boolean|webdriver.promise.Promise.<boolean>)} fn The function 764 * to call for each element in the array. 765 * @param {SELF=} opt_self The object to be used as the value of 'this' within 766 * {@code fn}. 767 * @template TYPE, SELF 768 */ 769 webdriver.promise.filter = function(arr, fn, opt_self) { 770 return webdriver.promise.when(arr, function(arr) { 771 var originalValues = goog.array.clone(arr); 772 return webdriver.promise.map(arr, fn, opt_self).then(function(include) { 773 return goog.array.filter(originalValues, function(value, index) { 774 return include[index]; 775 }); 776 }); 777 }); 778 }; 779 780 781 /** 782 * Returns a promise that will be resolved with the input value in a 783 * fully-resolved state. If the value is an array, each element will be fully 784 * resolved. Likewise, if the value is an object, all keys will be fully 785 * resolved. In both cases, all nested arrays and objects will also be 786 * fully resolved. All fields are resolved in place; the returned promise will 787 * resolve on {@code value} and not a copy. 788 * 789 * Warning: This function makes no checks against objects that contain 790 * cyclical references: 791 * <pre><code> 792 * var value = {}; 793 * value['self'] = value; 794 * webdriver.promise.fullyResolved(value); // Stack overflow. 795 * </code></pre> 796 * 797 * @param {*} value The value to fully resolve. 798 * @return {!webdriver.promise.Promise} A promise for a fully resolved version 799 * of the input value. 800 */ 801 webdriver.promise.fullyResolved = function(value) { 802 if (webdriver.promise.isPromise(value)) { 803 return webdriver.promise.when(value, webdriver.promise.fullyResolveValue_); 804 } 805 return webdriver.promise.fullyResolveValue_(value); 806 }; 807 808 809 /** 810 * @param {*} value The value to fully resolve. If a promise, assumed to 811 * already be resolved. 812 * @return {!webdriver.promise.Promise} A promise for a fully resolved version 813 * of the input value. 814 * @private 815 */ 816 webdriver.promise.fullyResolveValue_ = function(value) { 817 switch (goog.typeOf(value)) { 818 case 'array': 819 return webdriver.promise.fullyResolveKeys_( 820 /** @type {!Array} */ (value)); 821 822 case 'object': 823 if (webdriver.promise.isPromise(value)) { 824 // We get here when the original input value is a promise that 825 // resolves to itself. When the user provides us with such a promise, 826 // trust that it counts as a "fully resolved" value and return it. 827 // Of course, since it's already a promise, we can just return it 828 // to the user instead of wrapping it in another promise. 829 return /** @type {!webdriver.promise.Promise} */ (value); 830 } 831 832 if (goog.isNumber(value.nodeType) && 833 goog.isObject(value.ownerDocument) && 834 goog.isNumber(value.ownerDocument.nodeType)) { 835 // DOM node; return early to avoid infinite recursion. Should we 836 // only support objects with a certain level of nesting? 837 return webdriver.promise.fulfilled(value); 838 } 839 840 return webdriver.promise.fullyResolveKeys_( 841 /** @type {!Object} */ (value)); 842 843 default: // boolean, function, null, number, string, undefined 844 return webdriver.promise.fulfilled(value); 845 } 846 }; 847 848 849 /** 850 * @param {!(Array|Object)} obj the object to resolve. 851 * @return {!webdriver.promise.Promise} A promise that will be resolved with the 852 * input object once all of its values have been fully resolved. 853 * @private 854 */ 855 webdriver.promise.fullyResolveKeys_ = function(obj) { 856 var isArray = goog.isArray(obj); 857 var numKeys = isArray ? obj.length : goog.object.getCount(obj); 858 if (!numKeys) { 859 return webdriver.promise.fulfilled(obj); 860 } 861 862 var numResolved = 0; 863 var deferred = new webdriver.promise.Deferred(); 864 865 // In pre-IE9, goog.array.forEach will not iterate properly over arrays 866 // containing undefined values because "index in array" returns false 867 // when array[index] === undefined (even for x = [undefined, 1]). To get 868 // around this, we need to use our own forEach implementation. 869 // DO NOT REMOVE THIS UNTIL WE NO LONGER SUPPORT IE8. This cannot be 870 // reproduced in IE9 by changing the browser/document modes, it requires an 871 // actual pre-IE9 browser. Yay, IE! 872 var forEachKey = !isArray ? goog.object.forEach : function(arr, fn) { 873 var n = arr.length; 874 for (var i = 0; i < n; ++i) { 875 fn.call(null, arr[i], i, arr); 876 } 877 }; 878 879 forEachKey(obj, function(partialValue, key) { 880 var type = goog.typeOf(partialValue); 881 if (type != 'array' && type != 'object') { 882 maybeResolveValue(); 883 return; 884 } 885 886 webdriver.promise.fullyResolved(partialValue).then( 887 function(resolvedValue) { 888 obj[key] = resolvedValue; 889 maybeResolveValue(); 890 }, 891 deferred.reject); 892 }); 893 894 return deferred.promise; 895 896 function maybeResolveValue() { 897 if (++numResolved == numKeys) { 898 deferred.fulfill(obj); 899 } 900 } 901 }; 902 903 904 ////////////////////////////////////////////////////////////////////////////// 905 // 906 // webdriver.promise.ControlFlow 907 // 908 ////////////////////////////////////////////////////////////////////////////// 909 910 911 912 /** 913 * Handles the execution of scheduled tasks, each of which may be an 914 * asynchronous operation. The control flow will ensure tasks are executed in 915 * the ordered scheduled, starting each task only once those before it have 916 * completed. 917 * 918 * <p>Each task scheduled within this flow may return a 919 * {@link webdriver.promise.Promise} to indicate it is an asynchronous 920 * operation. The ControlFlow will wait for such promises to be resolved before 921 * marking the task as completed. 922 * 923 * <p>Tasks and each callback registered on a {@link webdriver.promise.Deferred} 924 * will be run in their own ControlFlow frame. Any tasks scheduled within a 925 * frame will have priority over previously scheduled tasks. Furthermore, if 926 * any of the tasks in the frame fails, the remainder of the tasks in that frame 927 * will be discarded and the failure will be propagated to the user through the 928 * callback/task's promised result. 929 * 930 * <p>Each time a ControlFlow empties its task queue, it will fire an 931 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Conversely, 932 * whenever the flow terminates due to an unhandled error, it will remove all 933 * remaining tasks in its queue and fire an 934 * {@link webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION} event. If 935 * there are no listeners registered with the flow, the error will be 936 * rethrown to the global error handler. 937 * 938 * @param {webdriver.promise.ControlFlow.Timer=} opt_timer The timer object 939 * to use. Should only be set for testing. 940 * @constructor 941 * @extends {webdriver.EventEmitter} 942 */ 943 webdriver.promise.ControlFlow = function(opt_timer) { 944 webdriver.EventEmitter.call(this); 945 946 /** 947 * The timer used by this instance. 948 * @type {webdriver.promise.ControlFlow.Timer} 949 */ 950 this.timer = opt_timer || webdriver.promise.ControlFlow.defaultTimer; 951 952 /** 953 * A list of recent tasks. Each time a new task is started, or a frame is 954 * completed, the previously recorded task is removed from this list. If 955 * there are multiple tasks, task N+1 is considered a sub-task of task 956 * N. 957 * @private {!Array.<!webdriver.promise.Task_>} 958 */ 959 this.history_ = []; 960 }; 961 goog.inherits(webdriver.promise.ControlFlow, webdriver.EventEmitter); 962 963 964 /** 965 * @typedef {{clearInterval: function(number), 966 * clearTimeout: function(number), 967 * setInterval: function(!Function, number): number, 968 * setTimeout: function(!Function, number): number}} 969 */ 970 webdriver.promise.ControlFlow.Timer; 971 972 973 /** 974 * The default timer object, which uses the global timer functions. 975 * @type {webdriver.promise.ControlFlow.Timer} 976 */ 977 webdriver.promise.ControlFlow.defaultTimer = (function() { 978 // The default timer functions may be defined as free variables for the 979 // current context, so do not reference them using "window" or 980 // "goog.global". Also, we must invoke them in a closure, and not using 981 // bind(), so we do not get "TypeError: Illegal invocation" (WebKit) or 982 // "Invalid calling object" (IE) errors. 983 return { 984 clearInterval: wrap(clearInterval), 985 clearTimeout: wrap(clearTimeout), 986 setInterval: wrap(setInterval), 987 setTimeout: wrap(setTimeout) 988 }; 989 990 function wrap(fn) { 991 return function() { 992 // Cannot use .call() or .apply() since we do not know which variable 993 // the function is bound to, and using the wrong one will generate 994 // an error. 995 return fn(arguments[0], arguments[1]); 996 }; 997 } 998 })(); 999 1000 1001 /** 1002 * Events that may be emitted by an {@link webdriver.promise.ControlFlow}. 1003 * @enum {string} 1004 */ 1005 webdriver.promise.ControlFlow.EventType = { 1006 1007 /** Emitted when all tasks have been successfully executed. */ 1008 IDLE: 'idle', 1009 1010 /** Emitted whenever a new task has been scheduled. */ 1011 SCHEDULE_TASK: 'scheduleTask', 1012 1013 /** 1014 * Emitted whenever a control flow aborts due to an unhandled promise 1015 * rejection. This event will be emitted along with the offending rejection 1016 * reason. Upon emitting this event, the control flow will empty its task 1017 * queue and revert to its initial state. 1018 */ 1019 UNCAUGHT_EXCEPTION: 'uncaughtException' 1020 }; 1021 1022 1023 /** 1024 * How often, in milliseconds, the event loop should run. 1025 * @type {number} 1026 * @const 1027 */ 1028 webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY = 10; 1029 1030 1031 /** 1032 * Tracks the active execution frame for this instance. Lazily initialized 1033 * when the first task is scheduled. 1034 * @private {webdriver.promise.Frame_} 1035 */ 1036 webdriver.promise.ControlFlow.prototype.activeFrame_ = null; 1037 1038 1039 /** 1040 * A reference to the frame in which new tasks should be scheduled. If 1041 * {@code null}, tasks will be scheduled within the active frame. When forcing 1042 * a function to run in the context of a new frame, this pointer is used to 1043 * ensure tasks are scheduled within the newly created frame, even though it 1044 * won't be active yet. 1045 * @private {webdriver.promise.Frame_} 1046 * @see {#runInNewFrame_} 1047 */ 1048 webdriver.promise.ControlFlow.prototype.schedulingFrame_ = null; 1049 1050 1051 /** 1052 * Timeout ID set when the flow is about to shutdown without any errors 1053 * being detected. Upon shutting down, the flow will emit an 1054 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event. Idle events 1055 * always follow a brief timeout in order to catch latent errors from the last 1056 * completed task. If this task had a callback registered, but no errback, and 1057 * the task fails, the unhandled failure would not be reported by the promise 1058 * system until the next turn of the event loop: 1059 * 1060 * // Schedule 1 task that fails. 1061 * var result = webriver.promise.controlFlow().schedule('example', 1062 * function() { return webdriver.promise.rejected('failed'); }); 1063 * // Set a callback on the result. This delays reporting the unhandled 1064 * // failure for 1 turn of the event loop. 1065 * result.then(goog.nullFunction); 1066 * 1067 * @private {?number} 1068 */ 1069 webdriver.promise.ControlFlow.prototype.shutdownId_ = null; 1070 1071 1072 /** 1073 * Interval ID for this instance's event loop. 1074 * @private {?number} 1075 */ 1076 webdriver.promise.ControlFlow.prototype.eventLoopId_ = null; 1077 1078 1079 /** 1080 * The number of "pending" promise rejections. 1081 * 1082 * <p>Each time a promise is rejected and is not handled by a listener, it will 1083 * schedule a 0-based timeout to check if it is still unrejected in the next 1084 * turn of the JS-event loop. This allows listeners to attach to, and handle, 1085 * the rejected promise at any point in same turn of the event loop that the 1086 * promise was rejected. 1087 * 1088 * <p>When this flow's own event loop triggers, it will not run if there 1089 * are any outstanding promise rejections. This allows unhandled promises to 1090 * be reported before a new task is started, ensuring the error is reported to 1091 * the current task queue. 1092 * 1093 * @private {number} 1094 */ 1095 webdriver.promise.ControlFlow.prototype.pendingRejections_ = 0; 1096 1097 1098 /** 1099 * The number of aborted frames since the last time a task was executed or a 1100 * frame completed successfully. 1101 * @private {number} 1102 */ 1103 webdriver.promise.ControlFlow.prototype.numAbortedFrames_ = 0; 1104 1105 1106 /** 1107 * Resets this instance, clearing its queue and removing all event listeners. 1108 */ 1109 webdriver.promise.ControlFlow.prototype.reset = function() { 1110 this.activeFrame_ = null; 1111 this.clearHistory(); 1112 this.removeAllListeners(); 1113 this.cancelShutdown_(); 1114 this.cancelEventLoop_(); 1115 }; 1116 1117 1118 /** 1119 * Returns a summary of the recent task activity for this instance. This 1120 * includes the most recently completed task, as well as any parent tasks. In 1121 * the returned summary, the task at index N is considered a sub-task of the 1122 * task at index N+1. 1123 * @return {!Array.<string>} A summary of this instance's recent task 1124 * activity. 1125 */ 1126 webdriver.promise.ControlFlow.prototype.getHistory = function() { 1127 var pendingTasks = []; 1128 var currentFrame = this.activeFrame_; 1129 while (currentFrame) { 1130 var task = currentFrame.getPendingTask(); 1131 if (task) { 1132 pendingTasks.push(task); 1133 } 1134 // A frame's parent node will always be another frame. 1135 currentFrame = 1136 /** @type {webdriver.promise.Frame_} */ (currentFrame.getParent()); 1137 } 1138 1139 var fullHistory = goog.array.concat(this.history_, pendingTasks); 1140 return goog.array.map(fullHistory, function(task) { 1141 return task.toString(); 1142 }); 1143 }; 1144 1145 1146 /** Clears this instance's task history. */ 1147 webdriver.promise.ControlFlow.prototype.clearHistory = function() { 1148 this.history_ = []; 1149 }; 1150 1151 1152 /** 1153 * Removes a completed task from this instance's history record. If any 1154 * tasks remain from aborted frames, those will be removed as well. 1155 * @private 1156 */ 1157 webdriver.promise.ControlFlow.prototype.trimHistory_ = function() { 1158 if (this.numAbortedFrames_) { 1159 goog.array.splice(this.history_, 1160 this.history_.length - this.numAbortedFrames_, 1161 this.numAbortedFrames_); 1162 this.numAbortedFrames_ = 0; 1163 } 1164 this.history_.pop(); 1165 }; 1166 1167 1168 /** 1169 * Property used to track whether an error has been annotated by 1170 * {@link webdriver.promise.ControlFlow#annotateError}. 1171 * @private {string} 1172 * @const 1173 */ 1174 webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_ = 1175 'webdriver_promise_error_'; 1176 1177 1178 /** 1179 * Appends a summary of this instance's recent task history to the given 1180 * error's stack trace. This function will also ensure the error's stack trace 1181 * is in canonical form. 1182 * @param {!(Error|goog.testing.JsUnitException)} e The error to annotate. 1183 * @return {!(Error|goog.testing.JsUnitException)} The annotated error. 1184 */ 1185 webdriver.promise.ControlFlow.prototype.annotateError = function(e) { 1186 if (!!e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_]) { 1187 return e; 1188 } 1189 1190 var history = this.getHistory(); 1191 if (history.length) { 1192 e = webdriver.stacktrace.format(e); 1193 1194 /** @type {!Error} */(e).stack += [ 1195 '\n==== async task ====\n', 1196 history.join('\n==== async task ====\n') 1197 ].join(''); 1198 1199 e[webdriver.promise.ControlFlow.ANNOTATION_PROPERTY_] = true; 1200 } 1201 1202 return e; 1203 }; 1204 1205 1206 /** 1207 * @return {string} The scheduled tasks still pending with this instance. 1208 */ 1209 webdriver.promise.ControlFlow.prototype.getSchedule = function() { 1210 return this.activeFrame_ ? this.activeFrame_.getRoot().toString() : '[]'; 1211 }; 1212 1213 1214 /** 1215 * Schedules a task for execution. If there is nothing currently in the 1216 * queue, the task will be executed in the next turn of the event loop. 1217 * 1218 * @param {function(): (T|webdriver.promise.Promise.<T>)} fn The function to 1219 * call to start the task. If the function returns a 1220 * {@link webdriver.promise.Promise}, this instance will wait for it to be 1221 * resolved before starting the next task. 1222 * @param {string=} opt_description A description of the task. 1223 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved 1224 * with the result of the action. 1225 * @template T 1226 */ 1227 webdriver.promise.ControlFlow.prototype.execute = function( 1228 fn, opt_description) { 1229 this.cancelShutdown_(); 1230 1231 if (!this.activeFrame_) { 1232 this.activeFrame_ = new webdriver.promise.Frame_(this); 1233 } 1234 1235 // Trim an extra frame off the generated stack trace for the call to this 1236 // function. 1237 var snapshot = new webdriver.stacktrace.Snapshot(1); 1238 var task = new webdriver.promise.Task_( 1239 this, fn, opt_description || '', snapshot); 1240 var scheduleIn = this.schedulingFrame_ || this.activeFrame_; 1241 scheduleIn.addChild(task); 1242 1243 this.emit(webdriver.promise.ControlFlow.EventType.SCHEDULE_TASK, opt_description); 1244 1245 this.scheduleEventLoopStart_(); 1246 return task.promise; 1247 }; 1248 1249 1250 /** 1251 * Inserts a {@code setTimeout} into the command queue. This is equivalent to 1252 * a thread sleep in a synchronous programming language. 1253 * 1254 * @param {number} ms The timeout delay, in milliseconds. 1255 * @param {string=} opt_description A description to accompany the timeout. 1256 * @return {!webdriver.promise.Promise} A promise that will be resolved with 1257 * the result of the action. 1258 */ 1259 webdriver.promise.ControlFlow.prototype.timeout = function( 1260 ms, opt_description) { 1261 return this.execute(function() { 1262 return webdriver.promise.delayed(ms); 1263 }, opt_description); 1264 }; 1265 1266 1267 /** 1268 * Schedules a task that shall wait for a condition to hold. Each condition 1269 * function may return any value, but it will always be evaluated as a boolean. 1270 * 1271 * <p>Condition functions may schedule sub-tasks with this instance, however, 1272 * their execution time will be factored into whether a wait has timed out. 1273 * 1274 * <p>In the event a condition returns a Promise, the polling loop will wait for 1275 * it to be resolved before evaluating whether the condition has been satisfied. 1276 * The resolution time for a promise is factored into whether a wait has timed 1277 * out. 1278 * 1279 * <p>If the condition function throws, or returns a rejected promise, the 1280 * wait task will fail. 1281 * 1282 * @param {!Function} condition The condition function to poll. 1283 * @param {number} timeout How long to wait, in milliseconds, for the condition 1284 * to hold before timing out. 1285 * @param {string=} opt_message An optional error message to include if the 1286 * wait times out; defaults to the empty string. 1287 * @return {!webdriver.promise.Promise} A promise that will be resolved when the 1288 * condition has been satisified. The promise shall be rejected if the wait 1289 * times out waiting for the condition. 1290 */ 1291 webdriver.promise.ControlFlow.prototype.wait = function( 1292 condition, timeout, opt_message) { 1293 var sleep = Math.min(timeout, 100); 1294 var self = this; 1295 1296 return this.execute(function() { 1297 var startTime = goog.now(); 1298 var waitResult = new webdriver.promise.Deferred(); 1299 var waitFrame = self.activeFrame_; 1300 waitFrame.isWaiting = true; 1301 pollCondition(); 1302 return waitResult.promise; 1303 1304 function pollCondition() { 1305 self.runInNewFrame_(condition, function(value) { 1306 var elapsed = goog.now() - startTime; 1307 if (!!value) { 1308 waitFrame.isWaiting = false; 1309 waitResult.fulfill(value); 1310 } else if (elapsed >= timeout) { 1311 waitResult.reject(new Error((opt_message ? opt_message + '\n' : '') + 1312 'Wait timed out after ' + elapsed + 'ms')); 1313 } else { 1314 self.timer.setTimeout(pollCondition, sleep); 1315 } 1316 }, waitResult.reject, true); 1317 } 1318 }, opt_message); 1319 }; 1320 1321 1322 /** 1323 * Schedules a task that will wait for another promise to resolve. The resolved 1324 * promise's value will be returned as the task result. 1325 * @param {!webdriver.promise.Promise} promise The promise to wait on. 1326 * @return {!webdriver.promise.Promise} A promise that will resolve when the 1327 * task has completed. 1328 */ 1329 webdriver.promise.ControlFlow.prototype.await = function(promise) { 1330 return this.execute(function() { 1331 return promise; 1332 }); 1333 }; 1334 1335 1336 /** 1337 * Schedules the interval for this instance's event loop, if necessary. 1338 * @private 1339 */ 1340 webdriver.promise.ControlFlow.prototype.scheduleEventLoopStart_ = function() { 1341 if (!this.eventLoopId_) { 1342 this.eventLoopId_ = this.timer.setInterval( 1343 goog.bind(this.runEventLoop_, this), 1344 webdriver.promise.ControlFlow.EVENT_LOOP_FREQUENCY); 1345 } 1346 }; 1347 1348 1349 /** 1350 * Cancels the event loop, if necessary. 1351 * @private 1352 */ 1353 webdriver.promise.ControlFlow.prototype.cancelEventLoop_ = function() { 1354 if (this.eventLoopId_) { 1355 this.timer.clearInterval(this.eventLoopId_); 1356 this.eventLoopId_ = null; 1357 } 1358 }; 1359 1360 1361 /** 1362 * Executes the next task for the current frame. If the current frame has no 1363 * more tasks, the frame's result will be resolved, returning control to the 1364 * frame's creator. This will terminate the flow if the completed frame was at 1365 * the top of the stack. 1366 * @private 1367 */ 1368 webdriver.promise.ControlFlow.prototype.runEventLoop_ = function() { 1369 // If we get here and there are pending promise rejections, then those 1370 // promises are queued up to run as soon as this (JS) event loop terminates. 1371 // Short-circuit our loop to give those promises a chance to run. Otherwise, 1372 // we might start a new task only to have it fail because of one of these 1373 // pending rejections. 1374 if (this.pendingRejections_) { 1375 return; 1376 } 1377 1378 // If the flow aborts due to an unhandled exception after we've scheduled 1379 // another turn of the execution loop, we can end up in here with no tasks 1380 // left. This is OK, just quietly return. 1381 if (!this.activeFrame_) { 1382 this.commenceShutdown_(); 1383 return; 1384 } 1385 1386 var task; 1387 if (this.activeFrame_.getPendingTask() || !(task = this.getNextTask_())) { 1388 // Either the current frame is blocked on a pending task, or we don't have 1389 // a task to finish because we've completed a frame. When completing a 1390 // frame, we must abort the event loop to allow the frame's promise's 1391 // callbacks to execute. 1392 return; 1393 } 1394 1395 var activeFrame = this.activeFrame_; 1396 activeFrame.setPendingTask(task); 1397 var markTaskComplete = goog.bind(function() { 1398 this.history_.push(/** @type {!webdriver.promise.Task_} */ (task)); 1399 activeFrame.setPendingTask(null); 1400 }, this); 1401 1402 this.trimHistory_(); 1403 var self = this; 1404 this.runInNewFrame_(task.execute, function(result) { 1405 markTaskComplete(); 1406 task.fulfill(result); 1407 }, function(error) { 1408 markTaskComplete(); 1409 1410 if (!webdriver.promise.isError_(error) && 1411 !webdriver.promise.isPromise(error)) { 1412 error = Error(error); 1413 } 1414 1415 task.reject(self.annotateError(/** @type {!Error} */ (error))); 1416 }, true); 1417 }; 1418 1419 1420 /** 1421 * @return {webdriver.promise.Task_} The next task to execute, or 1422 * {@code null} if a frame was resolved. 1423 * @private 1424 */ 1425 webdriver.promise.ControlFlow.prototype.getNextTask_ = function() { 1426 var firstChild = this.activeFrame_.getFirstChild(); 1427 if (!firstChild) { 1428 if (!this.activeFrame_.isWaiting) { 1429 this.resolveFrame_(this.activeFrame_); 1430 } 1431 return null; 1432 } 1433 1434 if (firstChild instanceof webdriver.promise.Frame_) { 1435 this.activeFrame_ = firstChild; 1436 return this.getNextTask_(); 1437 } 1438 1439 firstChild.getParent().removeChild(firstChild); 1440 return firstChild; 1441 }; 1442 1443 1444 /** 1445 * @param {!webdriver.promise.Frame_} frame The frame to resolve. 1446 * @private 1447 */ 1448 webdriver.promise.ControlFlow.prototype.resolveFrame_ = function(frame) { 1449 if (this.activeFrame_ === frame) { 1450 // Frame parent is always another frame, but the compiler is not smart 1451 // enough to recognize this. 1452 this.activeFrame_ = 1453 /** @type {webdriver.promise.Frame_} */ (frame.getParent()); 1454 } 1455 1456 if (frame.getParent()) { 1457 frame.getParent().removeChild(frame); 1458 } 1459 this.trimHistory_(); 1460 frame.fulfill(); 1461 1462 if (!this.activeFrame_) { 1463 this.commenceShutdown_(); 1464 } 1465 }; 1466 1467 1468 /** 1469 * Aborts the current frame. The frame, and all of the tasks scheduled within it 1470 * will be discarded. If this instance does not have an active frame, it will 1471 * immediately terminate all execution. 1472 * @param {*} error The reason the frame is being aborted; typically either 1473 * an Error or string. 1474 * @private 1475 */ 1476 webdriver.promise.ControlFlow.prototype.abortFrame_ = function(error) { 1477 // Annotate the error value if it is Error-like. 1478 if (webdriver.promise.isError_(error)) { 1479 this.annotateError(/** @type {!Error} */ (error)); 1480 } 1481 this.numAbortedFrames_++; 1482 1483 if (!this.activeFrame_) { 1484 this.abortNow_(error); 1485 return; 1486 } 1487 1488 // Frame parent is always another frame, but the compiler is not smart 1489 // enough to recognize this. 1490 var parent = /** @type {webdriver.promise.Frame_} */ ( 1491 this.activeFrame_.getParent()); 1492 if (parent) { 1493 parent.removeChild(this.activeFrame_); 1494 } 1495 1496 var frame = this.activeFrame_; 1497 this.activeFrame_ = parent; 1498 frame.reject(error); 1499 }; 1500 1501 1502 /** 1503 * Executes a function in a new frame. If the function does not schedule any new 1504 * tasks, the frame will be discarded and the function's result returned 1505 * immediately. Otherwise, a promise will be returned. This promise will be 1506 * resolved with the function's result once all of the tasks scheduled within 1507 * the function have been completed. If the function's frame is aborted, the 1508 * returned promise will be rejected. 1509 * 1510 * @param {!Function} fn The function to execute. 1511 * @param {function(*)} callback The function to call with a successful result. 1512 * @param {function(*)} errback The function to call if there is an error. 1513 * @param {boolean=} opt_activate Whether the active frame should be updated to 1514 * the newly created frame so tasks are treated as sub-tasks. 1515 * @private 1516 */ 1517 webdriver.promise.ControlFlow.prototype.runInNewFrame_ = function( 1518 fn, callback, errback, opt_activate) { 1519 var newFrame = new webdriver.promise.Frame_(this), 1520 self = this, 1521 oldFrame = this.activeFrame_; 1522 1523 try { 1524 if (!this.activeFrame_) { 1525 this.activeFrame_ = newFrame; 1526 } else { 1527 this.activeFrame_.addChild(newFrame); 1528 } 1529 1530 // Activate the new frame to force tasks to be treated as sub-tasks of 1531 // the parent frame. 1532 if (opt_activate) { 1533 this.activeFrame_ = newFrame; 1534 } 1535 1536 try { 1537 this.schedulingFrame_ = newFrame; 1538 webdriver.promise.pushFlow_(this); 1539 var result = fn(); 1540 } finally { 1541 webdriver.promise.popFlow_(); 1542 this.schedulingFrame_ = null; 1543 } 1544 newFrame.lockFrame(); 1545 1546 // If there was nothing scheduled in the new frame we can discard the 1547 // frame and return immediately. 1548 if (!newFrame.children_.length) { 1549 removeNewFrame(); 1550 webdriver.promise.asap(result, callback, errback); 1551 return; 1552 } 1553 1554 newFrame.then(function() { 1555 webdriver.promise.asap(result, callback, errback); 1556 }, function(e) { 1557 if (result instanceof webdriver.promise.Promise && result.isPending()) { 1558 result.cancel(e); 1559 e = result; 1560 } 1561 errback(e); 1562 }); 1563 } catch (ex) { 1564 removeNewFrame(new webdriver.promise.CanceledTaskError_(ex)); 1565 errback(ex); 1566 } 1567 1568 /** 1569 * @param {webdriver.promise.CanceledTaskError_=} opt_err If provided, the 1570 * error that triggered the removal of this frame. 1571 */ 1572 function removeNewFrame(opt_err) { 1573 var parent = newFrame.getParent(); 1574 if (parent) { 1575 parent.removeChild(newFrame); 1576 } 1577 1578 if (opt_err) { 1579 newFrame.cancelRemainingTasks(opt_err); 1580 } 1581 self.activeFrame_ = oldFrame; 1582 } 1583 }; 1584 1585 1586 /** 1587 * Commences the shutdown sequence for this instance. After one turn of the 1588 * event loop, this object will emit the 1589 * {@link webdriver.promise.ControlFlow.EventType.IDLE} event to signal 1590 * listeners that it has completed. During this wait, if another task is 1591 * scheduled, the shutdown will be aborted. 1592 * @private 1593 */ 1594 webdriver.promise.ControlFlow.prototype.commenceShutdown_ = function() { 1595 if (!this.shutdownId_) { 1596 // Go ahead and stop the event loop now. If we're in here, then there are 1597 // no more frames with tasks to execute. If we waited to cancel the event 1598 // loop in our timeout below, the event loop could trigger *before* the 1599 // timeout, generating an error from there being no frames. 1600 // If #execute is called before the timeout below fires, it will cancel 1601 // the timeout and restart the event loop. 1602 this.cancelEventLoop_(); 1603 1604 var self = this; 1605 self.shutdownId_ = self.timer.setTimeout(function() { 1606 self.shutdownId_ = null; 1607 self.emit(webdriver.promise.ControlFlow.EventType.IDLE); 1608 }, 0); 1609 } 1610 }; 1611 1612 1613 /** 1614 * Cancels the shutdown sequence if it is currently scheduled. 1615 * @private 1616 */ 1617 webdriver.promise.ControlFlow.prototype.cancelShutdown_ = function() { 1618 if (this.shutdownId_) { 1619 this.timer.clearTimeout(this.shutdownId_); 1620 this.shutdownId_ = null; 1621 } 1622 }; 1623 1624 1625 /** 1626 * Aborts this flow, abandoning all remaining tasks. If there are 1627 * listeners registered, an {@code UNCAUGHT_EXCEPTION} will be emitted with the 1628 * offending {@code error}, otherwise, the {@code error} will be rethrown to the 1629 * global error handler. 1630 * @param {*} error Object describing the error that caused the flow to 1631 * abort; usually either an Error or string value. 1632 * @private 1633 */ 1634 webdriver.promise.ControlFlow.prototype.abortNow_ = function(error) { 1635 this.activeFrame_ = null; 1636 this.cancelShutdown_(); 1637 this.cancelEventLoop_(); 1638 1639 var listeners = this.listeners( 1640 webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION); 1641 if (!listeners.length) { 1642 this.timer.setTimeout(function() { 1643 throw error; 1644 }, 0); 1645 } else { 1646 this.emit(webdriver.promise.ControlFlow.EventType.UNCAUGHT_EXCEPTION, 1647 error); 1648 } 1649 }; 1650 1651 1652 1653 /** 1654 * A single node in an {@link webdriver.promise.ControlFlow}'s task tree. 1655 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs 1656 * to. 1657 * @constructor 1658 * @extends {webdriver.promise.Deferred} 1659 * @private 1660 */ 1661 webdriver.promise.Node_ = function(flow) { 1662 webdriver.promise.Deferred.call(this, null, flow); 1663 }; 1664 goog.inherits(webdriver.promise.Node_, webdriver.promise.Deferred); 1665 1666 1667 /** 1668 * This node's parent. 1669 * @private {webdriver.promise.Node_} 1670 */ 1671 webdriver.promise.Node_.prototype.parent_ = null; 1672 1673 1674 /** @return {webdriver.promise.Node_} This node's parent. */ 1675 webdriver.promise.Node_.prototype.getParent = function() { 1676 return this.parent_; 1677 }; 1678 1679 1680 /** 1681 * @param {webdriver.promise.Node_} parent This node's new parent. 1682 */ 1683 webdriver.promise.Node_.prototype.setParent = function(parent) { 1684 this.parent_ = parent; 1685 }; 1686 1687 1688 /** 1689 * @return {!webdriver.promise.Node_} The root of this node's tree. 1690 */ 1691 webdriver.promise.Node_.prototype.getRoot = function() { 1692 var root = this; 1693 while (root.parent_) { 1694 root = root.parent_; 1695 } 1696 return root; 1697 }; 1698 1699 1700 1701 /** 1702 * An execution frame within a {@link webdriver.promise.ControlFlow}. Each 1703 * frame represents the execution context for either a 1704 * {@link webdriver.promise.Task_} or a callback on a 1705 * {@link webdriver.promise.Deferred}. 1706 * 1707 * <p>Each frame may contain sub-frames. If child N is a sub-frame, then the 1708 * items queued within it are given priority over child N+1. 1709 * 1710 * @param {!webdriver.promise.ControlFlow} flow The flow this instance belongs 1711 * to. 1712 * @constructor 1713 * @extends {webdriver.promise.Node_} 1714 * @private 1715 */ 1716 webdriver.promise.Frame_ = function(flow) { 1717 webdriver.promise.Node_.call(this, flow); 1718 1719 var reject = goog.bind(this.reject, this); 1720 var cancelRemainingTasks = goog.bind(this.cancelRemainingTasks, this); 1721 1722 /** @override */ 1723 this.reject = function(e) { 1724 cancelRemainingTasks(new webdriver.promise.CanceledTaskError_(e)); 1725 reject(e); 1726 }; 1727 1728 /** 1729 * @private {!Array.<!(webdriver.promise.Frame_|webdriver.promise.Task_)>} 1730 */ 1731 this.children_ = []; 1732 }; 1733 goog.inherits(webdriver.promise.Frame_, webdriver.promise.Node_); 1734 1735 1736 /** 1737 * The task currently being executed within this frame. 1738 * @private {webdriver.promise.Task_} 1739 */ 1740 webdriver.promise.Frame_.prototype.pendingTask_ = null; 1741 1742 1743 /** 1744 * Whether this frame is active. A frame is considered active once one of its 1745 * descendants has been removed for execution. 1746 * 1747 * Adding a sub-frame as a child to an active frame is an indication that 1748 * a callback to a {@link webdriver.promise.Deferred} is being invoked and any 1749 * tasks scheduled within it should have priority over previously scheduled 1750 * tasks: 1751 * <code><pre> 1752 * var flow = webdriver.promise.controlFlow(); 1753 * flow.execute('start here', goog.nullFunction).then(function() { 1754 * flow.execute('this should execute 2nd', goog.nullFunction); 1755 * }); 1756 * flow.execute('this should execute last', goog.nullFunction); 1757 * </pre></code> 1758 * 1759 * @private {boolean} 1760 */ 1761 webdriver.promise.Frame_.prototype.isActive_ = false; 1762 1763 1764 /** 1765 * Whether this frame is currently locked. A locked frame represents a callback 1766 * or task function which has run to completion and scheduled all of its tasks. 1767 * 1768 * <p>Once a frame becomes {@link #isActive_ active}, any new frames which are 1769 * added represent callbacks on a {@link webdriver.promise.Deferred}, whose 1770 * tasks must be given priority over previously scheduled tasks. 1771 * 1772 * @private {boolean} 1773 */ 1774 webdriver.promise.Frame_.prototype.isLocked_ = false; 1775 1776 1777 /** 1778 * A reference to the last node inserted in this frame. 1779 * @private {webdriver.promise.Node_} 1780 */ 1781 webdriver.promise.Frame_.prototype.lastInsertedChild_ = null; 1782 1783 1784 /** 1785 * Marks all of the tasks that are descendants of this frame in the execution 1786 * tree as cancelled. This is necessary for callbacks scheduled asynchronous. 1787 * For example: 1788 * 1789 * var someResult; 1790 * webdriver.promise.createFlow(function(flow) { 1791 * someResult = flow.execute(function() {}); 1792 * throw Error(); 1793 * }).addErrback(function(err) { 1794 * console.log('flow failed: ' + err); 1795 * someResult.then(function() { 1796 * console.log('task succeeded!'); 1797 * }, function(err) { 1798 * console.log('task failed! ' + err); 1799 * }); 1800 * }); 1801 * // flow failed: Error: boom 1802 * // task failed! CanceledTaskError: Task discarded due to a previous 1803 * // task failure: Error: boom 1804 * 1805 * @param {!webdriver.promise.CanceledTaskError_} error The cancellation 1806 * error. 1807 */ 1808 webdriver.promise.Frame_.prototype.cancelRemainingTasks = function(error) { 1809 goog.array.forEach(this.children_, function(child) { 1810 if (child instanceof webdriver.promise.Frame_) { 1811 child.cancelRemainingTasks(error); 1812 } else { 1813 // None of the previously registered listeners should be notified that 1814 // the task is being canceled, however, we need at least one errback 1815 // to prevent the cancellation from bubbling up. 1816 child.removeAll(); 1817 child.thenCatch(goog.nullFunction); 1818 child.cancel(error); 1819 } 1820 }); 1821 }; 1822 1823 1824 /** 1825 * @return {webdriver.promise.Task_} The task currently executing 1826 * within this frame, if any. 1827 */ 1828 webdriver.promise.Frame_.prototype.getPendingTask = function() { 1829 return this.pendingTask_; 1830 }; 1831 1832 1833 /** 1834 * @param {webdriver.promise.Task_} task The task currently 1835 * executing within this frame, if any. 1836 */ 1837 webdriver.promise.Frame_.prototype.setPendingTask = function(task) { 1838 this.pendingTask_ = task; 1839 }; 1840 1841 1842 /** Locks this frame. */ 1843 webdriver.promise.Frame_.prototype.lockFrame = function() { 1844 this.isLocked_ = true; 1845 }; 1846 1847 1848 /** 1849 * Adds a new node to this frame. 1850 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} node 1851 * The node to insert. 1852 */ 1853 webdriver.promise.Frame_.prototype.addChild = function(node) { 1854 if (this.lastInsertedChild_ && 1855 this.lastInsertedChild_ instanceof webdriver.promise.Frame_ && 1856 !this.lastInsertedChild_.isLocked_) { 1857 this.lastInsertedChild_.addChild(node); 1858 return; 1859 } 1860 1861 node.setParent(this); 1862 1863 if (this.isActive_ && node instanceof webdriver.promise.Frame_) { 1864 var index = 0; 1865 if (this.lastInsertedChild_ instanceof 1866 webdriver.promise.Frame_) { 1867 index = goog.array.indexOf(this.children_, this.lastInsertedChild_) + 1; 1868 } 1869 goog.array.insertAt(this.children_, node, index); 1870 this.lastInsertedChild_ = node; 1871 return; 1872 } 1873 1874 this.lastInsertedChild_ = node; 1875 this.children_.push(node); 1876 }; 1877 1878 1879 /** 1880 * @return {(webdriver.promise.Frame_|webdriver.promise.Task_)} This frame's 1881 * fist child. 1882 */ 1883 webdriver.promise.Frame_.prototype.getFirstChild = function() { 1884 this.isActive_ = true; 1885 this.lastInsertedChild_ = null; 1886 return this.children_[0]; 1887 }; 1888 1889 1890 /** 1891 * Removes a child from this frame. 1892 * @param {!(webdriver.promise.Frame_|webdriver.promise.Task_)} child 1893 * The child to remove. 1894 */ 1895 webdriver.promise.Frame_.prototype.removeChild = function(child) { 1896 var index = goog.array.indexOf(this.children_, child); 1897 child.setParent(null); 1898 goog.array.removeAt(this.children_, index); 1899 if (this.lastInsertedChild_ === child) { 1900 this.lastInsertedChild_ = null; 1901 } 1902 }; 1903 1904 1905 /** @override */ 1906 webdriver.promise.Frame_.prototype.toString = function() { 1907 return '[' + goog.array.map(this.children_, function(child) { 1908 return child.toString(); 1909 }).join(', ') + ']'; 1910 }; 1911 1912 1913 1914 /** 1915 * A task to be executed by a {@link webdriver.promise.ControlFlow}. 1916 * 1917 * @param {!webdriver.promise.ControlFlow} flow The flow this instances belongs 1918 * to. 1919 * @param {!Function} fn The function to call when the task executes. If it 1920 * returns a {@code webdriver.promise.Promise}, the flow will wait 1921 * for it to be resolved before starting the next task. 1922 * @param {string} description A description of the task for debugging. 1923 * @param {!webdriver.stacktrace.Snapshot} snapshot A snapshot of the stack 1924 * when this task was scheduled. 1925 * @constructor 1926 * @extends {webdriver.promise.Node_} 1927 * @private 1928 */ 1929 webdriver.promise.Task_ = function(flow, fn, description, snapshot) { 1930 webdriver.promise.Node_.call(this, flow); 1931 1932 /** 1933 * Executes this task. 1934 * @type {!Function} 1935 */ 1936 this.execute = fn; 1937 1938 /** @private {string} */ 1939 this.description_ = description; 1940 1941 /** @private {!webdriver.stacktrace.Snapshot} */ 1942 this.snapshot_ = snapshot; 1943 }; 1944 goog.inherits(webdriver.promise.Task_, webdriver.promise.Node_); 1945 1946 1947 /** @return {string} This task's description. */ 1948 webdriver.promise.Task_.prototype.getDescription = function() { 1949 return this.description_; 1950 }; 1951 1952 1953 /** @override */ 1954 webdriver.promise.Task_.prototype.toString = function() { 1955 var stack = this.snapshot_.getStacktrace(); 1956 var ret = this.description_; 1957 if (stack.length) { 1958 if (this.description_) { 1959 ret += '\n'; 1960 } 1961 ret += stack.join('\n'); 1962 } 1963 return ret; 1964 }; 1965 1966 1967 1968 /** 1969 * Special error used to signal when a task is canceled because a previous 1970 * task in the same frame failed. 1971 * @param {*} err The error that caused the task cancellation. 1972 * @constructor 1973 * @extends {goog.debug.Error} 1974 * @private 1975 */ 1976 webdriver.promise.CanceledTaskError_ = function(err) { 1977 goog.base(this, 'Task discarded due to a previous task failure: ' + err); 1978 }; 1979 goog.inherits(webdriver.promise.CanceledTaskError_, goog.debug.Error); 1980 1981 1982 /** @override */ 1983 webdriver.promise.CanceledTaskError_.prototype.name = 'CanceledTaskError'; 1984 1985 1986 1987 /** 1988 * The default flow to use if no others are active. 1989 * @private {!webdriver.promise.ControlFlow} 1990 */ 1991 webdriver.promise.defaultFlow_ = new webdriver.promise.ControlFlow(); 1992 1993 1994 /** 1995 * A stack of active control flows, with the top of the stack used to schedule 1996 * commands. When there are multiple flows on the stack, the flow at index N 1997 * represents a callback triggered within a task owned by the flow at index 1998 * N-1. 1999 * @private {!Array.<!webdriver.promise.ControlFlow>} 2000 */ 2001 webdriver.promise.activeFlows_ = []; 2002 2003 2004 /** 2005 * Changes the default flow to use when no others are active. 2006 * @param {!webdriver.promise.ControlFlow} flow The new default flow. 2007 * @throws {Error} If the default flow is not currently active. 2008 */ 2009 webdriver.promise.setDefaultFlow = function(flow) { 2010 if (webdriver.promise.activeFlows_.length) { 2011 throw Error('You may only change the default flow while it is active'); 2012 } 2013 webdriver.promise.defaultFlow_ = flow; 2014 }; 2015 2016 2017 /** 2018 * @return {!webdriver.promise.ControlFlow} The currently active control flow. 2019 */ 2020 webdriver.promise.controlFlow = function() { 2021 return /** @type {!webdriver.promise.ControlFlow} */ ( 2022 goog.array.peek(webdriver.promise.activeFlows_) || 2023 webdriver.promise.defaultFlow_); 2024 }; 2025 2026 2027 /** 2028 * @param {!webdriver.promise.ControlFlow} flow The new flow. 2029 * @private 2030 */ 2031 webdriver.promise.pushFlow_ = function(flow) { 2032 webdriver.promise.activeFlows_.push(flow); 2033 }; 2034 2035 2036 /** @private */ 2037 webdriver.promise.popFlow_ = function() { 2038 webdriver.promise.activeFlows_.pop(); 2039 }; 2040 2041 2042 /** 2043 * Creates a new control flow. The provided callback will be invoked as the 2044 * first task within the new flow, with the flow as its sole argument. Returns 2045 * a promise that resolves to the callback result. 2046 * @param {function(!webdriver.promise.ControlFlow)} callback The entry point 2047 * to the newly created flow. 2048 * @return {!webdriver.promise.Promise} A promise that resolves to the callback 2049 * result. 2050 */ 2051 webdriver.promise.createFlow = function(callback) { 2052 var flow = new webdriver.promise.ControlFlow( 2053 webdriver.promise.defaultFlow_.timer); 2054 return flow.execute(function() { 2055 return callback(flow); 2056 }); 2057 }; lib/webdriver/session.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 goog.provide('webdriver.Session'); 16 17 goog.require('webdriver.Capabilities'); 18 19 20 21 /** 22 * Contains information about a WebDriver session. 23 * @param {string} id The session ID. 24 * @param {!(Object|webdriver.Capabilities)} capabilities The session 25 * capabilities. 26 * @constructor 27 */ 28 webdriver.Session = function(id, capabilities) { 29 30 /** @private {string} */ 31 this.id_ = id; 32 33 /** @private {!webdriver.Capabilities} */ 34 this.caps_ = new webdriver.Capabilities().merge(capabilities); 35 }; 36 37 38 /** 39 * @return {string} This session's ID. 40 */ 41 webdriver.Session.prototype.getId = function() { 42 return this.id_; 43 }; 44 45 46 /** 47 * @return {!webdriver.Capabilities} This session's capabilities. 48 */ 49 webdriver.Session.prototype.getCapabilities = function() { 50 return this.caps_; 51 }; 52 53 54 /** 55 * Retrieves the value of a specific capability. 56 * @param {string} key The capability to retrieve. 57 * @return {*} The capability value. 58 */ 59 webdriver.Session.prototype.getCapability = function(key) { 60 return this.caps_.get(key); 61 }; 62 63 64 /** 65 * Returns the JSON representation of this object, which is just the string 66 * session ID. 67 * @return {string} The JSON representation of this Session. 68 */ 69 webdriver.Session.prototype.toJSON = function() { 70 return this.getId(); 71 }; lib/webdriver/stacktrace.js
1 // Copyright 2009 The Closure Library Authors. All Rights Reserved. 2 // Copyright 2012 Selenium comitters 3 // Copyright 2012 Software Freedom Conservancy 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS-IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 /** 18 * @fileoverview Tools for parsing and pretty printing error stack traces. This 19 * file is based on goog.testing.stacktrace. 20 */ 21 22 goog.provide('webdriver.stacktrace'); 23 goog.provide('webdriver.stacktrace.Snapshot'); 24 25 goog.require('goog.array'); 26 goog.require('goog.string'); 27 goog.require('goog.userAgent'); 28 29 30 31 /** 32 * Stores a snapshot of the stack trace at the time this instance was created. 33 * The stack trace will always be adjusted to exclude this function call. 34 * @param {number=} opt_slice The number of frames to remove from the top of 35 * the generated stack trace. 36 * @constructor 37 */ 38 webdriver.stacktrace.Snapshot = function(opt_slice) { 39 40 /** @private {number} */ 41 this.slice_ = opt_slice || 0; 42 43 var error; 44 if (webdriver.stacktrace.CAN_CAPTURE_STACK_TRACE_) { 45 error = Error(); 46 Error.captureStackTrace(error, webdriver.stacktrace.Snapshot); 47 } else { 48 // Remove 1 extra frame for the call to this constructor. 49 this.slice_ += 1; 50 // IE will only create a stack trace when the Error is thrown. 51 // We use null.x() to throw an exception instead of throw this.error_ 52 // because the closure compiler may optimize throws to a function call 53 // in an attempt to minimize the binary size which in turn has the side 54 // effect of adding an unwanted stack frame. 55 try { 56 null.x(); 57 } catch (e) { 58 error = e; 59 } 60 } 61 62 /** 63 * The error's stacktrace. This must be accessed immediately to ensure Opera 64 * computes the context correctly. 65 * @private {string} 66 */ 67 this.stack_ = webdriver.stacktrace.getStack_(error); 68 }; 69 70 71 /** 72 * Whether the current environment supports the Error.captureStackTrace 73 * function (as of 10/17/2012, only V8). 74 * @private {boolean} 75 * @const 76 */ 77 webdriver.stacktrace.CAN_CAPTURE_STACK_TRACE_ = 78 goog.isFunction(Error.captureStackTrace); 79 80 81 /** 82 * Whether the current browser supports stack traces. 83 * 84 * @type {boolean} 85 * @const 86 */ 87 webdriver.stacktrace.BROWSER_SUPPORTED = 88 webdriver.stacktrace.CAN_CAPTURE_STACK_TRACE_ || (function() { 89 try { 90 throw Error(); 91 } catch (e) { 92 return !!e.stack; 93 } 94 })(); 95 96 97 /** 98 * The parsed stack trace. This list is lazily generated the first time it is 99 * accessed. 100 * @private {Array.<!webdriver.stacktrace.Frame>} 101 */ 102 webdriver.stacktrace.Snapshot.prototype.parsedStack_ = null; 103 104 105 /** 106 * @return {!Array.<!webdriver.stacktrace.Frame>} The parsed stack trace. 107 */ 108 webdriver.stacktrace.Snapshot.prototype.getStacktrace = function() { 109 if (goog.isNull(this.parsedStack_)) { 110 this.parsedStack_ = webdriver.stacktrace.parse_(this.stack_); 111 if (this.slice_) { 112 this.parsedStack_ = goog.array.slice(this.parsedStack_, this.slice_); 113 } 114 delete this.slice_; 115 delete this.stack_; 116 } 117 return this.parsedStack_; 118 }; 119 120 121 122 /** 123 * Class representing one stack frame. 124 * @param {(string|undefined)} context Context object, empty in case of global 125 * functions or if the browser doesn't provide this information. 126 * @param {(string|undefined)} name Function name, empty in case of anonymous 127 * functions. 128 * @param {(string|undefined)} alias Alias of the function if available. For 129 * example the function name will be 'c' and the alias will be 'b' if the 130 * function is defined as <code>a.b = function c() {};</code>. 131 * @param {(string|undefined)} path File path or URL including line number and 132 * optionally column number separated by colons. 133 * @constructor 134 */ 135 webdriver.stacktrace.Frame = function(context, name, alias, path) { 136 137 /** @private {string} */ 138 this.context_ = context || ''; 139 140 /** @private {string} */ 141 this.name_ = name || ''; 142 143 /** @private {string} */ 144 this.alias_ = alias || ''; 145 146 /** @private {string} */ 147 this.path_ = path || ''; 148 149 /** @private {string} */ 150 this.url_ = this.path_; 151 152 /** @private {number} */ 153 this.line_ = -1; 154 155 /** @private {number} */ 156 this.column_ = -1; 157 158 if (path) { 159 var match = /:(\d+)(?::(\d+))?$/.exec(path); 160 if (match) { 161 this.line_ = Number(match[1]); 162 this.column = Number(match[2] || -1); 163 this.url_ = path.substr(0, match.index); 164 } 165 } 166 }; 167 168 169 /** 170 * Constant for an anonymous frame. 171 * @private {!webdriver.stacktrace.Frame} 172 * @const 173 */ 174 webdriver.stacktrace.ANONYMOUS_FRAME_ = 175 new webdriver.stacktrace.Frame('', '', '', ''); 176 177 178 /** 179 * @return {string} The function name or empty string if the function is 180 * anonymous and the object field which it's assigned to is unknown. 181 */ 182 webdriver.stacktrace.Frame.prototype.getName = function() { 183 return this.name_; 184 }; 185 186 187 /** 188 * @return {string} The url or empty string if it is unknown. 189 */ 190 webdriver.stacktrace.Frame.prototype.getUrl = function() { 191 return this.url_; 192 }; 193 194 195 /** 196 * @return {number} The line number if known or -1 if it is unknown. 197 */ 198 webdriver.stacktrace.Frame.prototype.getLine = function() { 199 return this.line_; 200 }; 201 202 203 /** 204 * @return {number} The column number if known and -1 if it is unknown. 205 */ 206 webdriver.stacktrace.Frame.prototype.getColumn = function() { 207 return this.column_; 208 }; 209 210 211 /** 212 * @return {boolean} Whether the stack frame contains an anonymous function. 213 */ 214 webdriver.stacktrace.Frame.prototype.isAnonymous = function() { 215 return !this.name_ || this.context_ == '[object Object]'; 216 }; 217 218 219 /** 220 * Converts this frame to its string representation using V8's stack trace 221 * format: http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi 222 * @return {string} The string representation of this frame. 223 * @override 224 */ 225 webdriver.stacktrace.Frame.prototype.toString = function() { 226 var context = this.context_; 227 if (context && context !== 'new ') { 228 context += '.'; 229 } 230 context += this.name_; 231 context += this.alias_ ? ' [as ' + this.alias_ + ']' : ''; 232 233 var path = this.path_ || '<anonymous>'; 234 return ' at ' + (context ? context + ' (' + path + ')' : path); 235 }; 236 237 238 /** 239 * Maximum length of a string that can be matched with a RegExp on 240 * Firefox 3x. Exceeding this approximate length will cause string.match 241 * to exceed Firefox's stack quota. This situation can be encountered 242 * when goog.globalEval is invoked with a long argument; such as 243 * when loading a module. 244 * @private {number} 245 * @const 246 */ 247 webdriver.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_ = 500000; 248 249 250 /** 251 * RegExp pattern for JavaScript identifiers. We don't support Unicode 252 * identifiers defined in ECMAScript v3. 253 * @private {string} 254 * @const 255 */ 256 webdriver.stacktrace.IDENTIFIER_PATTERN_ = '[a-zA-Z_$][\\w$]*'; 257 258 259 /** 260 * Pattern for a matching the type on a fully-qualified name. Forms an 261 * optional sub-match on the type. For example, in "foo.bar.baz", will match on 262 * "foo.bar". 263 * @private {string} 264 * @const 265 */ 266 webdriver.stacktrace.CONTEXT_PATTERN_ = 267 '(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + 268 '(?:\\.' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')*)\\.'; 269 270 271 /** 272 * Pattern for matching a fully qualified name. Will create two sub-matches: 273 * the type (optional), and the name. For example, in "foo.bar.baz", will 274 * match on ["foo.bar", "baz"]. 275 * @private {string} 276 * @const 277 */ 278 webdriver.stacktrace.QUALIFIED_NAME_PATTERN_ = 279 '(?:' + webdriver.stacktrace.CONTEXT_PATTERN_ + ')?' + 280 '(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')'; 281 282 283 /** 284 * RegExp pattern for function name alias in the V8 stack trace. 285 * @private {string} 286 * @const 287 */ 288 webdriver.stacktrace.V8_ALIAS_PATTERN_ = 289 '(?: \\[as (' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')\\])?'; 290 291 292 /** 293 * RegExp pattern for function names and constructor calls in the V8 stack 294 * trace. 295 * @private {string} 296 * @const 297 */ 298 webdriver.stacktrace.V8_FUNCTION_NAME_PATTERN_ = 299 '(?:' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + '|<anonymous>)'; 300 301 302 /** 303 * RegExp pattern for the context of a function call in V8. Creates two 304 * submatches, only one of which will ever match: either the namespace 305 * identifier (with optional "new" keyword in the case of a constructor call), 306 * or just the "new " phrase for a top level constructor call. 307 * @private {string} 308 * @const 309 */ 310 webdriver.stacktrace.V8_CONTEXT_PATTERN_ = 311 '(?:((?:new )?(?:\\[object Object\\]|' + 312 webdriver.stacktrace.IDENTIFIER_PATTERN_ + 313 '(?:\\.' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')*)' + 314 ')\\.|(new ))'; 315 316 317 /** 318 * RegExp pattern for function call in the V8 stack trace. 319 * Creates 3 submatches with context object (optional), function name and 320 * function alias (optional). 321 * @private {string} 322 * @const 323 */ 324 webdriver.stacktrace.V8_FUNCTION_CALL_PATTERN_ = 325 ' (?:' + webdriver.stacktrace.V8_CONTEXT_PATTERN_ + ')?' + 326 '(' + webdriver.stacktrace.V8_FUNCTION_NAME_PATTERN_ + ')' + 327 webdriver.stacktrace.V8_ALIAS_PATTERN_; 328 329 330 /** 331 * RegExp pattern for an URL + position inside the file. 332 * @private {string} 333 * @const 334 */ 335 webdriver.stacktrace.URL_PATTERN_ = 336 '((?:http|https|file)://[^\\s]+|javascript:.*)'; 337 338 339 /** 340 * RegExp pattern for a location string in a V8 stack frame. Creates two 341 * submatches for the location, one for enclosed in parentheticals and on 342 * where the location appears alone (which will only occur if the location is 343 * the only information in the frame). 344 * @private {string} 345 * @const 346 * @see http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi 347 */ 348 webdriver.stacktrace.V8_LOCATION_PATTERN_ = ' (?:\\((.*)\\)|(.*))'; 349 350 351 /** 352 * Regular expression for parsing one stack frame in V8. 353 * @private {!RegExp} 354 * @const 355 */ 356 webdriver.stacktrace.V8_STACK_FRAME_REGEXP_ = new RegExp('^ at' + 357 '(?:' + webdriver.stacktrace.V8_FUNCTION_CALL_PATTERN_ + ')?' + 358 webdriver.stacktrace.V8_LOCATION_PATTERN_ + '$'); 359 360 361 /** 362 * RegExp pattern for function names in the Firefox stack trace. 363 * Firefox has extended identifiers to deal with inner functions and anonymous 364 * functions: https://bugzilla.mozilla.org/show_bug.cgi?id=433529#c9 365 * @private {string} 366 * @const 367 */ 368 webdriver.stacktrace.FIREFOX_FUNCTION_NAME_PATTERN_ = 369 webdriver.stacktrace.IDENTIFIER_PATTERN_ + '[\\w./<$]*'; 370 371 372 /** 373 * RegExp pattern for function call in the Firefox stack trace. 374 * Creates a submatch for the function name. 375 * @private {string} 376 * @const 377 */ 378 webdriver.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ = 379 '(' + webdriver.stacktrace.FIREFOX_FUNCTION_NAME_PATTERN_ + ')?' + 380 '(?:\\(.*\\))?@'; 381 382 383 /** 384 * Regular expression for parsing one stack frame in Firefox. 385 * @private {!RegExp} 386 * @const 387 */ 388 webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_ = new RegExp('^' + 389 webdriver.stacktrace.FIREFOX_FUNCTION_CALL_PATTERN_ + 390 '(?::0|' + webdriver.stacktrace.URL_PATTERN_ + ')$'); 391 392 393 /** 394 * RegExp pattern for an anonymous function call in an Opera stack frame. 395 * Creates 2 (optional) submatches: the context object and function name. 396 * @private {string} 397 * @const 398 */ 399 webdriver.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ = 400 '<anonymous function(?:\\: ' + 401 webdriver.stacktrace.QUALIFIED_NAME_PATTERN_ + ')?>'; 402 403 404 /** 405 * RegExp pattern for a function call in an Opera stack frame. 406 * Creates 3 (optional) submatches: the function name (if not anonymous), 407 * the aliased context object and the function name (if anonymous). 408 * @private {string} 409 * @const 410 */ 411 webdriver.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ = 412 '(?:(?:(' + webdriver.stacktrace.IDENTIFIER_PATTERN_ + ')|' + 413 webdriver.stacktrace.OPERA_ANONYMOUS_FUNCTION_NAME_PATTERN_ + 414 ')(?:\\(.*\\)))?@'; 415 416 417 /** 418 * Regular expression for parsing on stack frame in Opera 11.68+ 419 * @private {!RegExp} 420 * @const 421 */ 422 webdriver.stacktrace.OPERA_STACK_FRAME_REGEXP_ = new RegExp('^' + 423 webdriver.stacktrace.OPERA_FUNCTION_CALL_PATTERN_ + 424 webdriver.stacktrace.URL_PATTERN_ + '?$'); 425 426 427 /** 428 * RegExp pattern for function call in a Chakra (IE) stack trace. This 429 * expression allows for identifiers like 'Anonymous function', 'eval code', 430 * and 'Global code'. 431 * @private {string} 432 * @const 433 */ 434 webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ = '(' + 435 webdriver.stacktrace.IDENTIFIER_PATTERN_ + '(?:\\s+\\w+)*)'; 436 437 438 /** 439 * Regular expression for parsing on stack frame in Chakra (IE). 440 * @private {!RegExp} 441 * @const 442 */ 443 webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_ = new RegExp('^ at ' + 444 webdriver.stacktrace.CHAKRA_FUNCTION_CALL_PATTERN_ + 445 '\\s*(?:\\((.*)\\))$'); 446 447 448 /** 449 * Placeholder for an unparsable frame in a stack trace generated by 450 * {@link goog.testing.stacktrace}. 451 * @private {string} 452 * @const 453 */ 454 webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ = '> (unknown)'; 455 456 457 /** 458 * Representation of an anonymous frame in a stack trace generated by 459 * {@link goog.testing.stacktrace}. 460 * @private {string} 461 * @const 462 */ 463 webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_ = '> anonymous'; 464 465 466 /** 467 * Pattern for a function call in a Closure stack trace. Creates three optional 468 * submatches: the context, function name, and alias. 469 * @private {string} 470 * @const 471 */ 472 webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ = 473 webdriver.stacktrace.QUALIFIED_NAME_PATTERN_ + 474 '(?:\\(.*\\))?' + // Ignore arguments if present. 475 webdriver.stacktrace.V8_ALIAS_PATTERN_; 476 477 478 /** 479 * Regular expression for parsing a stack frame generated by Closure's 480 * {@link goog.testing.stacktrace}. 481 * @private {!RegExp} 482 * @const 483 */ 484 webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_ = new RegExp('^> ' + 485 '(?:' + webdriver.stacktrace.CLOSURE_FUNCTION_CALL_PATTERN_ + 486 '(?: at )?)?' + 487 '(?:(.*:\\d+:\\d+)|' + webdriver.stacktrace.URL_PATTERN_ + ')?$'); 488 489 490 /** 491 * Parses one stack frame. 492 * @param {string} frameStr The stack frame as string. 493 * @return {webdriver.stacktrace.Frame} Stack frame object or null if the 494 * parsing failed. 495 * @private 496 */ 497 webdriver.stacktrace.parseStackFrame_ = function(frameStr) { 498 var m = frameStr.match(webdriver.stacktrace.V8_STACK_FRAME_REGEXP_); 499 if (m) { 500 return new webdriver.stacktrace.Frame( 501 m[1] || m[2], m[3], m[4], m[5] || m[6]); 502 } 503 504 if (frameStr.length > 505 webdriver.stacktrace.MAX_FIREFOX_FRAMESTRING_LENGTH_) { 506 return webdriver.stacktrace.parseLongFirefoxFrame_(frameStr); 507 } 508 509 m = frameStr.match(webdriver.stacktrace.FIREFOX_STACK_FRAME_REGEXP_); 510 if (m) { 511 return new webdriver.stacktrace.Frame('', m[1], '', m[2]); 512 } 513 514 m = frameStr.match(webdriver.stacktrace.OPERA_STACK_FRAME_REGEXP_); 515 if (m) { 516 return new webdriver.stacktrace.Frame(m[2], m[1] || m[3], '', m[4]); 517 } 518 519 m = frameStr.match(webdriver.stacktrace.CHAKRA_STACK_FRAME_REGEXP_); 520 if (m) { 521 return new webdriver.stacktrace.Frame('', m[1], '', m[2]); 522 } 523 524 if (frameStr == webdriver.stacktrace.UNKNOWN_CLOSURE_FRAME_ || 525 frameStr == webdriver.stacktrace.ANONYMOUS_CLOSURE_FRAME_) { 526 return webdriver.stacktrace.ANONYMOUS_FRAME_; 527 } 528 529 m = frameStr.match(webdriver.stacktrace.CLOSURE_STACK_FRAME_REGEXP_); 530 if (m) { 531 return new webdriver.stacktrace.Frame(m[1], m[2], m[3], m[4] || m[5]); 532 } 533 534 return null; 535 }; 536 537 538 /** 539 * Parses a long firefox stack frame. 540 * @param {string} frameStr The stack frame as string. 541 * @return {!webdriver.stacktrace.Frame} Stack frame object. 542 * @private 543 */ 544 webdriver.stacktrace.parseLongFirefoxFrame_ = function(frameStr) { 545 var firstParen = frameStr.indexOf('('); 546 var lastAmpersand = frameStr.lastIndexOf('@'); 547 var lastColon = frameStr.lastIndexOf(':'); 548 var functionName = ''; 549 if ((firstParen >= 0) && (firstParen < lastAmpersand)) { 550 functionName = frameStr.substring(0, firstParen); 551 } 552 var loc = ''; 553 if ((lastAmpersand >= 0) && (lastAmpersand + 1 < lastColon)) { 554 loc = frameStr.substring(lastAmpersand + 1); 555 } 556 return new webdriver.stacktrace.Frame('', functionName, '', loc); 557 }; 558 559 560 /** 561 * Get an error's stack trace with the error string trimmed. 562 * V8 prepends the string representation of an error to its stack trace. 563 * This function trims the string so that the stack trace can be parsed 564 * consistently with the other JS engines. 565 * @param {(Error|goog.testing.JsUnitException)} error The error. 566 * @return {string} The stack trace string. 567 * @private 568 */ 569 webdriver.stacktrace.getStack_ = function(error) { 570 if (!error) { 571 return ''; 572 } 573 var stack = error.stack || error.stackTrace || ''; 574 var errorStr = error + '\n'; 575 if (goog.string.startsWith(stack, errorStr)) { 576 stack = stack.substring(errorStr.length); 577 } 578 return stack; 579 }; 580 581 582 /** 583 * Formats an error's stack trace. 584 * @param {!(Error|goog.testing.JsUnitException)} error The error to format. 585 * @return {!(Error|goog.testing.JsUnitException)} The formatted error. 586 */ 587 webdriver.stacktrace.format = function(error) { 588 var stack = webdriver.stacktrace.getStack_(error); 589 var frames = webdriver.stacktrace.parse_(stack); 590 591 // Older versions of IE simply return [object Error] for toString(), so 592 // only use that as a last resort. 593 var errorStr = ''; 594 if (error.message) { 595 errorStr = (error.name ? error.name + ': ' : '') + error.message; 596 } else { 597 errorStr = error.toString(); 598 } 599 600 // Ensure the error is in the V8 style with the error's string representation 601 // prepended to the stack. 602 error.stack = errorStr + '\n' + frames.join('\n'); 603 return error; 604 }; 605 606 607 /** 608 * Parses an Error object's stack trace. 609 * @param {string} stack The stack trace. 610 * @return {!Array.<!webdriver.stacktrace.Frame>} Stack frames. The 611 * unrecognized frames will be nulled out. 612 * @private 613 */ 614 webdriver.stacktrace.parse_ = function(stack) { 615 if (!stack) { 616 return []; 617 } 618 619 var lines = stack. 620 replace(/\s*$/, ''). 621 split('\n'); 622 var frames = []; 623 for (var i = 0; i < lines.length; i++) { 624 var frame = webdriver.stacktrace.parseStackFrame_(lines[i]); 625 // The first two frames will be: 626 // webdriver.stacktrace.Snapshot() 627 // webdriver.stacktrace.get() 628 // In the case of Opera, sometimes an extra frame is injected in the next 629 // frame with a reported line number of zero. The next line detects that 630 // case and skips that frame. 631 if (!(goog.userAgent.OPERA && i == 2 && frame.getLine() == 0)) { 632 frames.push(frame || webdriver.stacktrace.ANONYMOUS_FRAME_); 633 } 634 } 635 return frames; 636 }; 637 638 639 /** 640 * Gets the native stack trace if available otherwise follows the call chain. 641 * The generated trace will exclude all frames up to and including the call to 642 * this function. 643 * @return {!Array.<!webdriver.stacktrace.Frame>} The frames of the stack trace. 644 */ 645 webdriver.stacktrace.get = function() { 646 return new webdriver.stacktrace.Snapshot(1).getStacktrace(); 647 }; lib/webdriver/testing/asserts.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Assertions and expectation utilities for use in WebDriver test 17 * cases. 18 */ 19 20 goog.provide('webdriver.testing.Assertion'); 21 goog.provide('webdriver.testing.ContainsMatcher'); 22 goog.provide('webdriver.testing.NegatedAssertion'); 23 goog.provide('webdriver.testing.assert'); 24 goog.provide('webdriver.testing.asserts'); 25 26 goog.require('goog.array'); 27 goog.require('goog.labs.testing.CloseToMatcher'); 28 goog.require('goog.labs.testing.EndsWithMatcher'); 29 goog.require('goog.labs.testing.EqualToMatcher'); 30 goog.require('goog.labs.testing.EqualsMatcher'); 31 goog.require('goog.labs.testing.GreaterThanEqualToMatcher'); 32 goog.require('goog.labs.testing.GreaterThanMatcher'); 33 goog.require('goog.labs.testing.LessThanEqualToMatcher'); 34 goog.require('goog.labs.testing.LessThanMatcher'); 35 goog.require('goog.labs.testing.InstanceOfMatcher'); 36 goog.require('goog.labs.testing.IsNotMatcher'); 37 goog.require('goog.labs.testing.IsNullMatcher'); 38 goog.require('goog.labs.testing.IsNullOrUndefinedMatcher'); 39 goog.require('goog.labs.testing.IsUndefinedMatcher'); 40 goog.require('goog.labs.testing.Matcher'); 41 goog.require('goog.labs.testing.ObjectEqualsMatcher'); 42 goog.require('goog.labs.testing.RegexMatcher'); 43 goog.require('goog.labs.testing.StartsWithMatcher'); 44 goog.require('goog.labs.testing.assertThat'); 45 goog.require('goog.string'); 46 goog.require('webdriver.promise'); 47 48 49 /** 50 * Accepts strins or array-like structures that contain {@code value}. 51 * @param {*} value The value to check for. 52 * @constructor 53 * @implements {goog.labs.testing.Matcher} 54 */ 55 webdriver.testing.ContainsMatcher = function(value) { 56 /** @private {*} */ 57 this.value_ = value; 58 }; 59 60 61 /** @override */ 62 webdriver.testing.ContainsMatcher.prototype.matches = function(actualValue) { 63 if (goog.isString(actualValue)) { 64 return goog.string.contains( 65 actualValue, /** @type {string} */(this.value_)); 66 } else { 67 return goog.array.contains( 68 /** @type {goog.array.ArrayLike} */(actualValue), this.value_); 69 } 70 }; 71 72 73 /** @override */ 74 webdriver.testing.ContainsMatcher.prototype.describe = function(actualValue) { 75 return actualValue + ' does not contain ' + this.value_; 76 }; 77 78 79 80 /** 81 * Utility for performing assertions against a given {@code value}. If the 82 * value is a {@link webdriver.promise.Promise}, this assertion will wait 83 * for it to resolve before applying any matchers. 84 * @param {*} value The value to wrap and apply matchers to. 85 * @constructor 86 */ 87 webdriver.testing.Assertion = function(value) { 88 89 /** @private {*} */ 90 this.value_ = value; 91 92 if (!(this instanceof webdriver.testing.NegatedAssertion)) { 93 /** 94 * A self reference provided for writing fluent assertions: 95 * webdriver.testing.assert(x).is.equalTo(y); 96 * @type {!webdriver.testing.Assertion} 97 */ 98 this.is = this; 99 100 /** 101 * Negates any matchers applied to this instance's value: 102 * webdriver.testing.assert(x).not.equalTo(y); 103 * @type {!webdriver.testing.NegatedAssertion} 104 */ 105 this.not = new webdriver.testing.NegatedAssertion(value); 106 } 107 }; 108 109 110 /** 111 * Wraps an object literal implementing the Matcher interface. This is used 112 * to appease the Closure compiler, which will not treat an object literal as 113 * implementing an interface. 114 * @param {{matches: function(*): boolean, describe: function(): string}} obj 115 * The object literal to delegate to. 116 * @constructor 117 * @implements {goog.labs.testing.Matcher} 118 * @private 119 */ 120 webdriver.testing.Assertion.DelegatingMatcher_ = function(obj) { 121 122 /** @override */ 123 this.matches = function(value) { 124 return obj.matches(value); 125 }; 126 127 /** @override */ 128 this.describe = function() { 129 return obj.describe(); 130 }; 131 }; 132 133 134 /** 135 * Asserts that the given {@code matcher} accepts the value wrapped by this 136 * instance. If the wrapped value is a promise, this function will defer 137 * applying the assertion until the value has been resolved. Otherwise, it 138 * will be applied immediately. 139 * @param {!goog.labs.testing.Matcher} matcher The matcher to apply 140 * @param {string=} opt_message A message to include if the matcher does not 141 * accept the value wrapped by this assertion. 142 * @return {webdriver.promise.Promise} The deferred assertion result, or 143 * {@code null} if the assertion was immediately applied. 144 * @protected 145 */ 146 webdriver.testing.Assertion.prototype.apply = function(matcher, opt_message) { 147 var result = null; 148 if (webdriver.promise.isPromise(this.value_)) { 149 result = webdriver.promise.when(this.value_, function(value) { 150 goog.labs.testing.assertThat(value, matcher, opt_message); 151 }); 152 } else { 153 goog.labs.testing.assertThat(this.value_, matcher, opt_message); 154 } 155 return result; 156 }; 157 158 159 /** 160 * Asserts that the value managed by this assertion is a number strictly 161 * greater than {@code value}. 162 * @param {number} value The minimum value. 163 * @param {string=} opt_message A message to include if the matcher does not 164 * accept the value wrapped by this assertion. 165 * @return {webdriver.promise.Promise} The assertion result. 166 */ 167 webdriver.testing.Assertion.prototype.greaterThan = function( 168 value, opt_message) { 169 return this.apply( 170 new goog.labs.testing.GreaterThanMatcher(value), opt_message); 171 }; 172 173 174 /** 175 * Asserts that the value managed by this assertion is a number >= the given 176 * value. 177 * @param {number} value The minimum value. 178 * @param {string=} opt_message A message to include if the matcher does not 179 * accept the value wrapped by this assertion. 180 * @return {webdriver.promise.Promise} The assertion result. 181 */ 182 webdriver.testing.Assertion.prototype.greaterThanEqualTo = function( 183 value, opt_message) { 184 return this.apply( 185 new goog.labs.testing.GreaterThanEqualToMatcher(value), opt_message); 186 }; 187 188 189 /** 190 * Asserts that the value managed by this assertion is a number strictly less 191 * than the given value. 192 * @param {number} value The maximum value. 193 * @param {string=} opt_message A message to include if the matcher does not 194 * accept the value wrapped by this assertion. 195 * @return {webdriver.promise.Promise} The assertion result. 196 */ 197 webdriver.testing.Assertion.prototype.lessThan = function(value, opt_message) { 198 return this.apply( 199 new goog.labs.testing.LessThanMatcher(value), opt_message); 200 }; 201 202 203 /** 204 * Asserts that the value managed by this assertion is a number <= the given 205 * value. 206 * @param {number} value The maximum value. 207 * @param {string=} opt_message A message to include if the matcher does not 208 * accept the value wrapped by this assertion. 209 * @return {webdriver.promise.Promise} The assertion result. 210 */ 211 webdriver.testing.Assertion.prototype.lessThanEqualTo = function( 212 value, opt_message) { 213 return this.apply( 214 new goog.labs.testing.LessThanEqualToMatcher(value), opt_message); 215 }; 216 217 218 /** 219 * Asserts that the wrapped value is a number within a given distance of an 220 * expected value. 221 * @param {number} value The expected value. 222 * @param {number} range The maximum amount the actual value is permitted to 223 * differ from the expected value. 224 * @param {string=} opt_message A message to include if the matcher does not 225 * accept the value wrapped by this assertion. 226 * @return {webdriver.promise.Promise} The assertion result. 227 */ 228 webdriver.testing.Assertion.prototype.closeTo = function( 229 value, range, opt_message) { 230 return this.apply( 231 new goog.labs.testing.CloseToMatcher(value, range), opt_message); 232 }; 233 234 235 /** 236 * Asserts that the wrapped value is an instance of the given class. 237 * @param {!Function} ctor The expected class constructor. 238 * @param {string=} opt_message A message to include if the matcher does not 239 * accept the value wrapped by this assertion. 240 * @return {webdriver.promise.Promise} The assertion result. 241 */ 242 webdriver.testing.Assertion.prototype.instanceOf = function(ctor, opt_message) { 243 return this.apply( 244 new goog.labs.testing.InstanceOfMatcher(ctor), opt_message); 245 }; 246 247 248 /** 249 * Asserts that the wrapped value is null. 250 * @param {string=} opt_message A message to include if the matcher does not 251 * accept the value wrapped by this assertion. 252 * @return {webdriver.promise.Promise} The assertion result. 253 */ 254 webdriver.testing.Assertion.prototype.isNull = function(opt_message) { 255 return this.apply(new goog.labs.testing.IsNullMatcher(), opt_message); 256 }; 257 258 259 /** 260 * Asserts that the wrapped value is undefined. 261 * @param {string=} opt_message A message to include if the matcher does not 262 * accept the value wrapped by this assertion. 263 * @return {webdriver.promise.Promise} The assertion result. 264 */ 265 webdriver.testing.Assertion.prototype.isUndefined = function(opt_message) { 266 return this.apply(new goog.labs.testing.IsUndefinedMatcher(), opt_message); 267 }; 268 269 270 /** 271 * Asserts that the wrapped value is null or undefined. 272 * @param {string=} opt_message A message to include if the matcher does not 273 * accept the value wrapped by this assertion. 274 * @return {webdriver.promise.Promise} The assertion result. 275 */ 276 webdriver.testing.Assertion.prototype.isNullOrUndefined = function( 277 opt_message) { 278 return this.apply( 279 new goog.labs.testing.IsNullOrUndefinedMatcher(), opt_message); 280 }; 281 282 283 /** 284 * Asserts that the wrapped value is a string or array-like structure 285 * containing the given value. 286 * @param {*} value The expected value. 287 * @param {string=} opt_message A message to include if the matcher does not 288 * accept the value wrapped by this assertion. 289 * @return {webdriver.promise.Promise} The assertion result. 290 */ 291 webdriver.testing.Assertion.prototype.contains = function(value, opt_message) { 292 return this.apply( 293 new webdriver.testing.ContainsMatcher(value), opt_message); 294 }; 295 296 297 /** 298 * Asserts that the wrapped value is a string ending with the given suffix. 299 * @param {string} suffix The expected suffix. 300 * @param {string=} opt_message A message to include if the matcher does not 301 * accept the value wrapped by this assertion. 302 * @return {webdriver.promise.Promise} The assertion result. 303 */ 304 webdriver.testing.Assertion.prototype.endsWith = function( 305 suffix, opt_message) { 306 return this.apply( 307 new goog.labs.testing.EndsWithMatcher(suffix), opt_message); 308 }; 309 310 311 /** 312 * Asserts that the wrapped value is a string starting with the given prefix. 313 * @param {string} prefix The expected prefix. 314 * @param {string=} opt_message A message to include if the matcher does not 315 * accept the value wrapped by this assertion. 316 * @return {webdriver.promise.Promise} The assertion result. 317 */ 318 webdriver.testing.Assertion.prototype.startsWith = function( 319 prefix, opt_message) { 320 return this.apply( 321 new goog.labs.testing.StartsWithMatcher(prefix), opt_message); 322 }; 323 324 325 /** 326 * Asserts that the wrapped value is a string that matches the given RegExp. 327 * @param {!RegExp} regex The regex to test. 328 * @param {string=} opt_message A message to include if the matcher does not 329 * accept the value wrapped by this assertion. 330 * @return {webdriver.promise.Promise} The assertion result. 331 */ 332 webdriver.testing.Assertion.prototype.matches = function(regex, opt_message) { 333 return this.apply(new goog.labs.testing.RegexMatcher(regex), opt_message); 334 }; 335 336 337 /** 338 * Asserts that the value managed by this assertion is strictly equal to the 339 * given {@code value}. 340 * @param {*} value The expected value. 341 * @param {string=} opt_message A message to include if the matcher does not 342 * accept the value wrapped by this assertion. 343 * @return {webdriver.promise.Promise} The assertion result. 344 */ 345 webdriver.testing.Assertion.prototype.equalTo = function(value, opt_message) { 346 return this.apply(webdriver.testing.asserts.equalTo(value), opt_message); 347 }; 348 349 350 /** 351 * Asserts that the value managed by this assertion is strictly true. 352 * @return {webdriver.promise.Promise} The assertion result. 353 */ 354 webdriver.testing.Assertion.prototype.isTrue = function() { 355 return this.equalTo(true); 356 }; 357 358 359 /** 360 * Asserts that the value managed by this assertion is strictly false. 361 * @return {webdriver.promise.Promise} The assertion result. 362 */ 363 webdriver.testing.Assertion.prototype.isFalse = function() { 364 return this.equalTo(false); 365 }; 366 367 368 369 /** 370 * An assertion that negates any applied matchers. 371 * @param {*} value The value to perform assertions on. 372 * @constructor 373 * @extends {webdriver.testing.Assertion} 374 */ 375 webdriver.testing.NegatedAssertion = function(value) { 376 goog.base(this, value); 377 this.value = value; 378 }; 379 goog.inherits( 380 webdriver.testing.NegatedAssertion, webdriver.testing.Assertion); 381 382 383 /** @override */ 384 webdriver.testing.NegatedAssertion.prototype.apply = function( 385 matcher, opt_message) { 386 matcher = new goog.labs.testing.IsNotMatcher(matcher); 387 return goog.base(this, 'apply', matcher, opt_message); 388 }; 389 390 391 392 /** 393 * Creates a new assertion. 394 * @param {*} value The value to perform an assertion on. 395 * @return {!webdriver.testing.Assertion} The new assertion. 396 */ 397 webdriver.testing.assert = function(value) { 398 return new webdriver.testing.Assertion(value); 399 }; 400 401 402 /** 403 * Registers a new assertion to expose from the 404 * {@link webdriver.testing.Assertion} prototype. 405 * @param {string} name The assertion name. 406 * @param {(function(new: goog.labs.testing.Matcher, *)| 407 * {matches: function(*): boolean, 408 * describe: function(): string})} matcherTemplate Either the 409 * matcher constructor to use, or an object literal defining a matcher. 410 */ 411 webdriver.testing.assert.register = function(name, matcherTemplate) { 412 webdriver.testing.Assertion.prototype[name] = function(value, opt_message) { 413 var matcher; 414 if (goog.isFunction(matcherTemplate)) { 415 var ctor = /** @type {function(new: goog.labs.testing.Matcher, *)} */ ( 416 matcherTemplate); 417 matcher = new ctor(value); 418 } else { 419 matcher = new webdriver.testing.Assertion.DelegatingMatcher_(value); 420 } 421 return this.apply(matcher, opt_message); 422 }; 423 }; 424 425 426 /** 427 * Asserts that a matcher accepts a given value. This function has two 428 * signatures based on the number of arguments: 429 * 430 * Two arguments: 431 * assertThat(actualValue, matcher) 432 * Three arguments: 433 * assertThat(failureMessage, actualValue, matcher) 434 * 435 * @param {*} failureMessageOrActualValue Either a failure message or the value 436 * to apply to the given matcher. 437 * @param {*} actualValueOrMatcher Either the value to apply to the given 438 * matcher, or the matcher itself. 439 * @param {goog.labs.testing.Matcher=} opt_matcher The matcher to use; 440 * ignored unless this function is invoked with three arguments. 441 * @return {!webdriver.promise.Promise} The assertion result. 442 * @deprecated Use webdriver.testing.asserts.assert instead. 443 */ 444 webdriver.testing.asserts.assertThat = function( 445 failureMessageOrActualValue, actualValueOrMatcher, opt_matcher) { 446 var args = goog.array.slice(arguments, 0); 447 448 var message = args.length > 2 ? args.shift() : ''; 449 if (message) message += '\n'; 450 451 var actualValue = args.shift(); 452 var matcher = args.shift(); 453 454 return webdriver.promise.when(actualValue, function(value) { 455 goog.labs.testing.assertThat(value, matcher, message); 456 }); 457 }; 458 459 460 /** 461 * Creates an equality matcher. 462 * @param {*} expected The expected value. 463 * @return {!goog.labs.testing.Matcher} The new matcher. 464 */ 465 webdriver.testing.asserts.equalTo = function(expected) { 466 if (goog.isString(expected)) { 467 return new goog.labs.testing.EqualsMatcher(expected); 468 } else if (goog.isNumber(expected)) { 469 return new goog.labs.testing.EqualToMatcher(expected); 470 } else { 471 return new goog.labs.testing.ObjectEqualsMatcher( 472 /** @type {!Object} */ (expected)); 473 } 474 }; 475 476 477 goog.exportSymbol('assertThat', webdriver.testing.asserts.assertThat); 478 // Mappings for goog.labs.testing matcher functions to the legacy 479 // webdriver.testing.asserts matchers. 480 goog.exportSymbol('contains', containsString); 481 goog.exportSymbol('equalTo', webdriver.testing.asserts.equalTo); 482 goog.exportSymbol('equals', webdriver.testing.asserts.equalTo); 483 goog.exportSymbol('is', webdriver.testing.asserts.equalTo); 484 goog.exportSymbol('not', isNot); 485 goog.exportSymbol('or', anyOf); lib/webdriver/webdriver.js
1 // Copyright 2011 Software Freedom Conservancy. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview The heart of the WebDriver JavaScript API. 17 */ 18 19 goog.provide('webdriver.Alert'); 20 goog.provide('webdriver.UnhandledAlertError'); 21 goog.provide('webdriver.WebDriver'); 22 goog.provide('webdriver.WebElement'); 23 24 goog.require('bot.Error'); 25 goog.require('bot.ErrorCode'); 26 goog.require('bot.response'); 27 goog.require('goog.array'); 28 goog.require('goog.object'); 29 goog.require('webdriver.ActionSequence'); 30 goog.require('webdriver.Command'); 31 goog.require('webdriver.CommandName'); 32 goog.require('webdriver.Key'); 33 goog.require('webdriver.Locator'); 34 goog.require('webdriver.Session'); 35 goog.require('webdriver.logging'); 36 goog.require('webdriver.promise'); 37 38 39 ////////////////////////////////////////////////////////////////////////////// 40 // 41 // webdriver.WebDriver 42 // 43 ////////////////////////////////////////////////////////////////////////////// 44 45 46 47 /** 48 * Creates a new WebDriver client, which provides control over a browser. 49 * 50 * Every WebDriver command returns a {@code webdriver.promise.Promise} that 51 * represents the result of that command. Callbacks may be registered on this 52 * object to manipulate the command result or catch an expected error. Any 53 * commands scheduled with a callback are considered sub-commands and will 54 * execute before the next command in the current frame. For example: 55 * <pre><code> 56 * var message = []; 57 * driver.call(message.push, message, 'a').then(function() { 58 * driver.call(message.push, message, 'b'); 59 * }); 60 * driver.call(message.push, message, 'c'); 61 * driver.call(function() { 62 * alert('message is abc? ' + (message.join('') == 'abc')); 63 * }); 64 * </code></pre> 65 * 66 * @param {!(webdriver.Session|webdriver.promise.Promise)} session Either a 67 * known session or a promise that will be resolved to a session. 68 * @param {!webdriver.CommandExecutor} executor The executor to use when 69 * sending commands to the browser. 70 * @param {webdriver.promise.ControlFlow=} opt_flow The flow to 71 * schedule commands through. Defaults to the active flow object. 72 * @constructor 73 */ 74 webdriver.WebDriver = function(session, executor, opt_flow) { 75 76 /** @private {!(webdriver.Session|webdriver.promise.Promise)} */ 77 this.session_ = session; 78 79 /** @private {!webdriver.CommandExecutor} */ 80 this.executor_ = executor; 81 82 /** @private {!webdriver.promise.ControlFlow} */ 83 this.flow_ = opt_flow || webdriver.promise.controlFlow(); 84 }; 85 86 87 /** 88 * Creates a new WebDriver client for an existing session. 89 * @param {!webdriver.CommandExecutor} executor Command executor to use when 90 * querying for session details. 91 * @param {string} sessionId ID of the session to attach to. 92 * @return {!webdriver.WebDriver} A new client for the specified session. 93 */ 94 webdriver.WebDriver.attachToSession = function(executor, sessionId) { 95 return webdriver.WebDriver.acquireSession_(executor, 96 new webdriver.Command(webdriver.CommandName.DESCRIBE_SESSION). 97 setParameter('sessionId', sessionId), 98 'WebDriver.attachToSession()'); 99 }; 100 101 102 /** 103 * Creates a new WebDriver session. 104 * @param {!webdriver.CommandExecutor} executor The executor to create the new 105 * session with. 106 * @param {!webdriver.Capabilities} desiredCapabilities The desired 107 * capabilities for the new session. 108 * @return {!webdriver.WebDriver} The driver for the newly created session. 109 */ 110 webdriver.WebDriver.createSession = function(executor, desiredCapabilities) { 111 return webdriver.WebDriver.acquireSession_(executor, 112 new webdriver.Command(webdriver.CommandName.NEW_SESSION). 113 setParameter('desiredCapabilities', desiredCapabilities), 114 'WebDriver.createSession()'); 115 }; 116 117 118 /** 119 * Sends a command to the server that is expected to return the details for a 120 * {@link webdriver.Session}. This may either be an existing session, or a 121 * newly created one. 122 * @param {!webdriver.CommandExecutor} executor Command executor to use when 123 * querying for session details. 124 * @param {!webdriver.Command} command The command to send to fetch the session 125 * details. 126 * @param {string} description A descriptive debug label for this action. 127 * @return {!webdriver.WebDriver} A new WebDriver client for the session. 128 * @private 129 */ 130 webdriver.WebDriver.acquireSession_ = function(executor, command, description) { 131 var session = webdriver.promise.controlFlow().execute(function() { 132 return webdriver.WebDriver.executeCommand_(executor, command). 133 then(function(response) { 134 bot.response.checkResponse(response); 135 return new webdriver.Session(response['sessionId'], 136 response['value']); 137 }); 138 }, description); 139 return new webdriver.WebDriver(session, executor); 140 }; 141 142 143 /** 144 * Converts an object to its JSON representation in the WebDriver wire protocol. 145 * When converting values of type object, the following steps will be taken: 146 * <ol> 147 * <li>if the object provides a "toWireValue" function, the return value will 148 * be returned in its fully resolved state (e.g. this function may return 149 * promise values)</li> 150 * <li>if the object provides a "toJSON" function, the return value of this 151 * function will be returned</li> 152 * <li>otherwise, the value of each key will be recursively converted according 153 * to the rules above.</li> 154 * </ol> 155 * 156 * @param {*} obj The object to convert. 157 * @return {!webdriver.promise.Promise} A promise that will resolve to the 158 * input value's JSON representation. 159 * @private 160 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol 161 */ 162 webdriver.WebDriver.toWireValue_ = function(obj) { 163 switch (goog.typeOf(obj)) { 164 case 'array': 165 return webdriver.promise.fullyResolved( 166 goog.array.map(/** @type {!Array} */ (obj), 167 webdriver.WebDriver.toWireValue_)); 168 case 'object': 169 if (goog.isFunction(obj.toWireValue)) { 170 return webdriver.promise.fullyResolved(obj.toWireValue()); 171 } 172 if (goog.isFunction(obj.toJSON)) { 173 return webdriver.promise.fulfilled(obj.toJSON()); 174 } 175 if (goog.isNumber(obj.nodeType) && goog.isString(obj.nodeName)) { 176 throw Error([ 177 'Invalid argument type: ', obj.nodeName, '(', obj.nodeType, ')' 178 ].join('')); 179 } 180 return webdriver.promise.fullyResolved( 181 goog.object.map(/** @type {!Object} */ (obj), 182 webdriver.WebDriver.toWireValue_)); 183 case 'function': 184 return webdriver.promise.fulfilled('' + obj); 185 case 'undefined': 186 return webdriver.promise.fulfilled(null); 187 default: 188 return webdriver.promise.fulfilled(obj); 189 } 190 }; 191 192 193 /** 194 * Converts a value from its JSON representation according to the WebDriver wire 195 * protocol. Any JSON object containing a 196 * {@code webdriver.WebElement.ELEMENT_KEY} key will be decoded to a 197 * {@code webdriver.WebElement} object. All other values will be passed through 198 * as is. 199 * @param {!webdriver.WebDriver} driver The driver instance to use as the 200 * parent of any unwrapped {@code webdriver.WebElement} values. 201 * @param {*} value The value to convert. 202 * @return {*} The converted value. 203 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol 204 * @private 205 */ 206 webdriver.WebDriver.fromWireValue_ = function(driver, value) { 207 if (goog.isArray(value)) { 208 value = goog.array.map(/**@type {goog.array.ArrayLike}*/ (value), 209 goog.partial(webdriver.WebDriver.fromWireValue_, driver)); 210 } else if (value && goog.isObject(value) && !goog.isFunction(value)) { 211 if (webdriver.WebElement.ELEMENT_KEY in value) { 212 value = new webdriver.WebElement(driver, 213 value[webdriver.WebElement.ELEMENT_KEY]); 214 } else { 215 value = goog.object.map(/**@type {!Object}*/ (value), 216 goog.partial(webdriver.WebDriver.fromWireValue_, driver)); 217 } 218 } 219 return value; 220 }; 221 222 223 /** 224 * Translates a command to its wire-protocol representation before passing it 225 * to the given {@code executor} for execution. 226 * @param {!webdriver.CommandExecutor} executor The executor to use. 227 * @param {!webdriver.Command} command The command to execute. 228 * @return {!webdriver.promise.Promise} A promise that will resolve with the 229 * command response. 230 * @private 231 */ 232 webdriver.WebDriver.executeCommand_ = function(executor, command) { 233 return webdriver.promise.fullyResolved(command.getParameters()). 234 then(webdriver.WebDriver.toWireValue_). 235 then(function(parameters) { 236 command.setParameters(parameters); 237 return webdriver.promise.checkedNodeCall( 238 goog.bind(executor.execute, executor, command)); 239 }); 240 }; 241 242 243 /** 244 * @return {!webdriver.promise.ControlFlow} The control flow used by this 245 * instance. 246 */ 247 webdriver.WebDriver.prototype.controlFlow = function() { 248 return this.flow_; 249 }; 250 251 252 /** 253 * Schedules a {@code webdriver.Command} to be executed by this driver's 254 * {@code webdriver.CommandExecutor}. 255 * @param {!webdriver.Command} command The command to schedule. 256 * @param {string} description A description of the command for debugging. 257 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved 258 * with the command result. 259 * @template T 260 */ 261 webdriver.WebDriver.prototype.schedule = function(command, description) { 262 var self = this; 263 264 checkHasNotQuit(); 265 command.setParameter('sessionId', this.session_); 266 267 var flow = this.flow_; 268 return flow.execute(function() { 269 // A call to WebDriver.quit() may have been scheduled in the same event 270 // loop as this |command|, which would prevent us from detecting that the 271 // driver has quit above. Therefore, we need to make another quick check. 272 // We still check above so we can fail as early as possible. 273 checkHasNotQuit(); 274 return webdriver.WebDriver.executeCommand_(self.executor_, command); 275 }, description).then(function(response) { 276 try { 277 bot.response.checkResponse(response); 278 } catch (ex) { 279 var value = response['value']; 280 if (ex.code === bot.ErrorCode.MODAL_DIALOG_OPENED) { 281 var text = value && value['alert'] ? value['alert']['text'] : ''; 282 throw new webdriver.UnhandledAlertError(ex.message, 283 new webdriver.Alert(self, text)); 284 } 285 throw ex; 286 } 287 return webdriver.WebDriver.fromWireValue_(self, response['value']); 288 }); 289 290 function checkHasNotQuit() { 291 if (!self.session_) { 292 throw new Error('This driver instance does not have a valid session ID ' + 293 '(did you call WebDriver.quit()?) and may no longer be ' + 294 'used.'); 295 } 296 } 297 }; 298 299 300 // ---------------------------------------------------------------------------- 301 // Client command functions: 302 // ---------------------------------------------------------------------------- 303 304 305 /** 306 * @return {!webdriver.promise.Promise.<!webdriver.Session>} A promise for this 307 * client's session. 308 */ 309 webdriver.WebDriver.prototype.getSession = function() { 310 return webdriver.promise.when(this.session_); 311 }; 312 313 314 /** 315 * @return {!webdriver.promise.Promise.<!webdriver.Capabilities>} A promise 316 * that will resolve with the this instance's capabilities. 317 */ 318 webdriver.WebDriver.prototype.getCapabilities = function() { 319 return webdriver.promise.when(this.session_, function(session) { 320 return session.getCapabilities(); 321 }); 322 }; 323 324 325 /** 326 * Schedules a command to quit the current session. After calling quit, this 327 * instance will be invalidated and may no longer be used to issue commands 328 * against the browser. 329 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 330 * when the command has completed. 331 */ 332 webdriver.WebDriver.prototype.quit = function() { 333 var result = this.schedule( 334 new webdriver.Command(webdriver.CommandName.QUIT), 335 'WebDriver.quit()'); 336 // Delete our session ID when the quit command finishes; this will allow us to 337 // throw an error when attemnpting to use a driver post-quit. 338 return result.thenFinally(goog.bind(function() { 339 delete this.session_; 340 }, this)); 341 }; 342 343 344 /** 345 * Creates a new action sequence using this driver. The sequence will not be 346 * scheduled for execution until {@link webdriver.ActionSequence#perform} is 347 * called. Example: 348 * <pre><code> 349 * driver.actions(). 350 * mouseDown(element1). 351 * mouseMove(element2). 352 * mouseUp(). 353 * perform(); 354 * </code></pre> 355 * @return {!webdriver.ActionSequence} A new action sequence for this instance. 356 */ 357 webdriver.WebDriver.prototype.actions = function() { 358 return new webdriver.ActionSequence(this); 359 }; 360 361 362 /** 363 * Schedules a command to execute JavaScript in the context of the currently 364 * selected frame or window. The script fragment will be executed as the body 365 * of an anonymous function. If the script is provided as a function object, 366 * that function will be converted to a string for injection into the target 367 * window. 368 * 369 * Any arguments provided in addition to the script will be included as script 370 * arguments and may be referenced using the {@code arguments} object. 371 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}. 372 * Arrays and objects may also be used as script arguments as long as each item 373 * adheres to the types previously mentioned. 374 * 375 * The script may refer to any variables accessible from the current window. 376 * Furthermore, the script will execute in the window's context, thus 377 * {@code document} may be used to refer to the current document. Any local 378 * variables will not be available once the script has finished executing, 379 * though global variables will persist. 380 * 381 * If the script has a return value (i.e. if the script contains a return 382 * statement), then the following steps will be taken for resolving this 383 * functions return value: 384 * <ul> 385 * <li>For a HTML element, the value will resolve to a 386 * {@code webdriver.WebElement}</li> 387 * <li>Null and undefined return values will resolve to null</li> 388 * <li>Booleans, numbers, and strings will resolve as is</li> 389 * <li>Functions will resolve to their string representation</li> 390 * <li>For arrays and objects, each member item will be converted according to 391 * the rules above</li> 392 * </ul> 393 * 394 * @param {!(string|Function)} script The script to execute. 395 * @param {...*} var_args The arguments to pass to the script. 396 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the 397 * scripts return value. 398 * @template T 399 */ 400 webdriver.WebDriver.prototype.executeScript = function(script, var_args) { 401 if (goog.isFunction(script)) { 402 script = 'return (' + script + ').apply(null, arguments);'; 403 } 404 return this.schedule( 405 new webdriver.Command(webdriver.CommandName.EXECUTE_SCRIPT). 406 setParameter('script', script). 407 setParameter('args', goog.array.slice(arguments, 1)), 408 'WebDriver.executeScript()'); 409 }; 410 411 412 /** 413 * Schedules a command to execute asynchronous JavaScript in the context of the 414 * currently selected frame or window. The script fragment will be executed as 415 * the body of an anonymous function. If the script is provided as a function 416 * object, that function will be converted to a string for injection into the 417 * target window. 418 * 419 * Any arguments provided in addition to the script will be included as script 420 * arguments and may be referenced using the {@code arguments} object. 421 * Arguments may be a boolean, number, string, or {@code webdriver.WebElement}. 422 * Arrays and objects may also be used as script arguments as long as each item 423 * adheres to the types previously mentioned. 424 * 425 * Unlike executing synchronous JavaScript with 426 * {@code webdriver.WebDriver.prototype.executeScript}, scripts executed with 427 * this function must explicitly signal they are finished by invoking the 428 * provided callback. This callback will always be injected into the 429 * executed function as the last argument, and thus may be referenced with 430 * {@code arguments[arguments.length - 1]}. The following steps will be taken 431 * for resolving this functions return value against the first argument to the 432 * script's callback function: 433 * <ul> 434 * <li>For a HTML element, the value will resolve to a 435 * {@code webdriver.WebElement}</li> 436 * <li>Null and undefined return values will resolve to null</li> 437 * <li>Booleans, numbers, and strings will resolve as is</li> 438 * <li>Functions will resolve to their string representation</li> 439 * <li>For arrays and objects, each member item will be converted according to 440 * the rules above</li> 441 * </ul> 442 * 443 * Example #1: Performing a sleep that is synchronized with the currently 444 * selected window: 445 * <code><pre> 446 * var start = new Date().getTime(); 447 * driver.executeAsyncScript( 448 * 'window.setTimeout(arguments[arguments.length - 1], 500);'). 449 * then(function() { 450 * console.log('Elapsed time: ' + (new Date().getTime() - start) + ' ms'); 451 * }); 452 * </pre></code> 453 * 454 * Example #2: Synchronizing a test with an AJAX application: 455 * <code><pre> 456 * var button = driver.findElement(By.id('compose-button')); 457 * button.click(); 458 * driver.executeAsyncScript( 459 * 'var callback = arguments[arguments.length - 1];' + 460 * 'mailClient.getComposeWindowWidget().onload(callback);'); 461 * driver.switchTo().frame('composeWidget'); 462 * driver.findElement(By.id('to')).sendKEys('dog@example.com'); 463 * </pre></code> 464 * 465 * Example #3: Injecting a XMLHttpRequest and waiting for the result. In this 466 * example, the inject script is specified with a function literal. When using 467 * this format, the function is converted to a string for injection, so it 468 * should not reference any symbols not defined in the scope of the page under 469 * test. 470 * <code><pre> 471 * driver.executeAsyncScript(function() { 472 * var callback = arguments[arguments.length - 1]; 473 * var xhr = new XMLHttpRequest(); 474 * xhr.open("GET", "/resource/data.json", true); 475 * xhr.onreadystatechange = function() { 476 * if (xhr.readyState == 4) { 477 * callback(xhr.resposneText); 478 * } 479 * } 480 * xhr.send(''); 481 * }).then(function(str) { 482 * console.log(JSON.parse(str)['food']); 483 * }); 484 * </pre></code> 485 * 486 * @param {!(string|Function)} script The script to execute. 487 * @param {...*} var_args The arguments to pass to the script. 488 * @return {!webdriver.promise.Promise.<T>} A promise that will resolve to the 489 * scripts return value. 490 * @template T 491 */ 492 webdriver.WebDriver.prototype.executeAsyncScript = function(script, var_args) { 493 if (goog.isFunction(script)) { 494 script = 'return (' + script + ').apply(null, arguments);'; 495 } 496 return this.schedule( 497 new webdriver.Command(webdriver.CommandName.EXECUTE_ASYNC_SCRIPT). 498 setParameter('script', script). 499 setParameter('args', goog.array.slice(arguments, 1)), 500 'WebDriver.executeScript()'); 501 }; 502 503 504 /** 505 * Schedules a command to execute a custom function. 506 * @param {function(...): (T|webdriver.promise.Promise.<T>)} fn The function to 507 * execute. 508 * @param {Object=} opt_scope The object in whose scope to execute the function. 509 * @param {...*} var_args Any arguments to pass to the function. 510 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved' 511 * with the function's result. 512 * @template T 513 */ 514 webdriver.WebDriver.prototype.call = function(fn, opt_scope, var_args) { 515 var args = goog.array.slice(arguments, 2); 516 var flow = this.flow_; 517 return flow.execute(function() { 518 return webdriver.promise.fullyResolved(args).then(function(args) { 519 return fn.apply(opt_scope, args); 520 }); 521 }, 'WebDriver.call(' + (fn.name || 'function') + ')'); 522 }; 523 524 525 /** 526 * Schedules a command to wait for a condition to hold, as defined by some 527 * user supplied function. If any errors occur while evaluating the wait, they 528 * will be allowed to propagate. 529 * 530 * <p>In the event a condition returns a {@link webdriver.promise.Promise}, the 531 * polling loop will wait for it to be resolved and use the resolved value for 532 * evaluating whether the condition has been satisfied. The resolution time for 533 * a promise is factored into whether a wait has timed out. 534 * 535 * @param {function():boolean} fn The function to evaluate as a wait condition. 536 * @param {number} timeout How long to wait for the condition to be true. 537 * @param {string=} opt_message An optional message to use if the wait times 538 * out. 539 * @return {!webdriver.promise.Promise} A promise that will be resolved when the 540 * wait condition has been satisfied. 541 */ 542 webdriver.WebDriver.prototype.wait = function(fn, timeout, opt_message) { 543 return this.flow_.wait(fn, timeout, opt_message); 544 }; 545 546 547 /** 548 * Schedules a command to make the driver sleep for the given amount of time. 549 * @param {number} ms The amount of time, in milliseconds, to sleep. 550 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 551 * when the sleep has finished. 552 */ 553 webdriver.WebDriver.prototype.sleep = function(ms) { 554 return this.flow_.timeout(ms, 'WebDriver.sleep(' + ms + ')'); 555 }; 556 557 558 /** 559 * Schedules a command to retrieve they current window handle. 560 * @return {!webdriver.promise.Promise.<string>} A promise that will be 561 * resolved with the current window handle. 562 */ 563 webdriver.WebDriver.prototype.getWindowHandle = function() { 564 return this.schedule( 565 new webdriver.Command(webdriver.CommandName.GET_CURRENT_WINDOW_HANDLE), 566 'WebDriver.getWindowHandle()'); 567 }; 568 569 570 /** 571 * Schedules a command to retrieve the current list of available window handles. 572 * @return {!webdriver.promise.Promise.<!Array.<string>>} A promise that will 573 * be resolved with an array of window handles. 574 */ 575 webdriver.WebDriver.prototype.getAllWindowHandles = function() { 576 return this.schedule( 577 new webdriver.Command(webdriver.CommandName.GET_WINDOW_HANDLES), 578 'WebDriver.getAllWindowHandles()'); 579 }; 580 581 582 /** 583 * Schedules a command to retrieve the current page's source. The page source 584 * returned is a representation of the underlying DOM: do not expect it to be 585 * formatted or escaped in the same way as the response sent from the web 586 * server. 587 * @return {!webdriver.promise.Promise.<string>} A promise that will be 588 * resolved with the current page source. 589 */ 590 webdriver.WebDriver.prototype.getPageSource = function() { 591 return this.schedule( 592 new webdriver.Command(webdriver.CommandName.GET_PAGE_SOURCE), 593 'WebDriver.getAllWindowHandles()'); 594 }; 595 596 597 /** 598 * Schedules a command to close the current window. 599 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 600 * when this command has completed. 601 */ 602 webdriver.WebDriver.prototype.close = function() { 603 return this.schedule(new webdriver.Command(webdriver.CommandName.CLOSE), 604 'WebDriver.close()'); 605 }; 606 607 608 /** 609 * Schedules a command to navigate to the given URL. 610 * @param {string} url The fully qualified URL to open. 611 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 612 * when the document has finished loading. 613 */ 614 webdriver.WebDriver.prototype.get = function(url) { 615 return this.navigate().to(url); 616 }; 617 618 619 /** 620 * Schedules a command to retrieve the URL of the current page. 621 * @return {!webdriver.promise.Promise.<string>} A promise that will be 622 * resolved with the current URL. 623 */ 624 webdriver.WebDriver.prototype.getCurrentUrl = function() { 625 return this.schedule( 626 new webdriver.Command(webdriver.CommandName.GET_CURRENT_URL), 627 'WebDriver.getCurrentUrl()'); 628 }; 629 630 631 /** 632 * Schedules a command to retrieve the current page's title. 633 * @return {!webdriver.promise.Promise.<string>} A promise that will be 634 * resolved with the current page's title. 635 */ 636 webdriver.WebDriver.prototype.getTitle = function() { 637 return this.schedule(new webdriver.Command(webdriver.CommandName.GET_TITLE), 638 'WebDriver.getTitle()'); 639 }; 640 641 642 /** 643 * Schedule a command to find an element on the page. If the element cannot be 644 * found, a {@link bot.ErrorCode.NO_SUCH_ELEMENT} result will be returned 645 * by the driver. Unlike other commands, this error cannot be suppressed. In 646 * other words, scheduling a command to find an element doubles as an assert 647 * that the element is present on the page. To test whether an element is 648 * present on the page, use {@link #isElementPresent} instead. 649 * 650 * <p>The search criteria for an element may be defined using one of the 651 * factories in the {@link webdriver.By} namespace, or as a short-hand 652 * {@link webdriver.By.Hash} object. For example, the following two statements 653 * are equivalent: 654 * <code><pre> 655 * var e1 = driver.findElement(By.id('foo')); 656 * var e2 = driver.findElement({id:'foo'}); 657 * </pre></code> 658 * 659 * <p>You may also provide a custom locator function, which takes as input 660 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a 661 * promise that will resolve to a WebElement. For example, to find the first 662 * visible link on a page, you could write: 663 * <code><pre> 664 * var link = driver.findElement(firstVisibleLink); 665 * 666 * function firstVisibleLink(driver) { 667 * var links = driver.findElements(By.tagName('a')); 668 * return webdriver.promise.filter(links, function(link) { 669 * return links.isDisplayed(); 670 * }).then(function(visibleLinks) { 671 * return visibleLinks[0]; 672 * }); 673 * } 674 * </pre></code> 675 * 676 * <p>When running in the browser, a WebDriver cannot manipulate DOM elements 677 * directly; it may do so only through a {@link webdriver.WebElement} reference. 678 * This function may be used to generate a WebElement from a DOM element. A 679 * reference to the DOM element will be stored in a known location and this 680 * driver will attempt to retrieve it through {@link #executeScript}. If the 681 * element cannot be found (eg, it belongs to a different document than the 682 * one this instance is currently focused on), a 683 * {@link bot.ErrorCode.NO_SUCH_ELEMENT} error will be returned. 684 * 685 * @param {!(webdriver.Locator|webdriver.By.Hash|Element|Function)} locator The 686 * locator to use. 687 * @return {!webdriver.WebElement} A WebElement that can be used to issue 688 * commands against the located element. If the element is not found, the 689 * element will be invalidated and all scheduled commands aborted. 690 */ 691 webdriver.WebDriver.prototype.findElement = function(locator) { 692 var id; 693 if ('nodeType' in locator && 'ownerDocument' in locator) { 694 var element = /** @type {!Element} */ (locator); 695 id = this.findDomElement_(element).then(function(element) { 696 if (!element) { 697 throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, 698 'Unable to locate element. Is WebDriver focused on its ' + 699 'ownerDocument\'s frame?'); 700 } 701 return element; 702 }); 703 } else { 704 locator = webdriver.Locator.checkLocator(locator); 705 if (goog.isFunction(locator)) { 706 id = this.findElementInternal_(locator, this); 707 } else { 708 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENT). 709 setParameter('using', locator.using). 710 setParameter('value', locator.value); 711 id = this.schedule(command, 'WebDriver.findElement(' + locator + ')'); 712 } 713 } 714 return new webdriver.WebElement(this, id); 715 }; 716 717 718 /** 719 * @param {!Function} locatorFn The locator function to use. 720 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search 721 * context. 722 * @return {!webdriver.promise.Promise.<!webdriver.WebElement>} A 723 * promise that will resolve to a list of WebElements. 724 * @private 725 */ 726 webdriver.WebDriver.prototype.findElementInternal_ = function( 727 locatorFn, context) { 728 return this.call(goog.partial(locatorFn, context)).then(function(result) { 729 if (goog.isArray(result)) { 730 result = result[0]; 731 } 732 if (!(result instanceof webdriver.WebElement)) { 733 throw new TypeError('Custom locator did not return a WebElement'); 734 } 735 return result; 736 }); 737 }; 738 739 740 /** 741 * Locates a DOM element so that commands may be issued against it using the 742 * {@link webdriver.WebElement} class. This is accomplished by storing a 743 * reference to the element in an object on the element's ownerDocument. 744 * {@link #executeScript} will then be used to create a WebElement from this 745 * reference. This requires this driver to currently be focused on the 746 * ownerDocument's window+frame. 747 748 * @param {!Element} element The element to locate. 749 * @return {!webdriver.promise.Promise.<webdriver.WebElement>} A promise that 750 * will be fulfilled with the located element, or null if the element 751 * could not be found. 752 * @private 753 */ 754 webdriver.WebDriver.prototype.findDomElement_ = function(element) { 755 var doc = element.ownerDocument; 756 var store = doc['$webdriver$'] = doc['$webdriver$'] || {}; 757 var id = Math.floor(Math.random() * goog.now()).toString(36); 758 store[id] = element; 759 element[id] = id; 760 761 function cleanUp() { 762 delete store[id]; 763 } 764 765 function lookupElement(id) { 766 var store = document['$webdriver$']; 767 if (!store) { 768 return null; 769 } 770 771 var element = store[id]; 772 if (!element || element[id] !== id) { 773 return null; 774 } 775 return element; 776 } 777 778 /** @type {!webdriver.promise.Promise.<webdriver.WebElement>} */ 779 var foundElement = this.executeScript(lookupElement, id); 780 foundElement.thenFinally(cleanUp); 781 return foundElement; 782 }; 783 784 785 /** 786 * Schedules a command to test if an element is present on the page. 787 * 788 * <p>If given a DOM element, this function will check if it belongs to the 789 * document the driver is currently focused on. Otherwise, the function will 790 * test if at least one element can be found with the given search criteria. 791 * 792 * @param {!(webdriver.Locator|webdriver.By.Hash|Element| 793 * Function)} locatorOrElement The locator to use, or the actual 794 * DOM element to be located by the server. 795 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve 796 * with whether the element is present on the page. 797 */ 798 webdriver.WebDriver.prototype.isElementPresent = function(locatorOrElement) { 799 if ('nodeType' in locatorOrElement && 'ownerDocument' in locatorOrElement) { 800 return this.findDomElement_(/** @type {!Element} */ (locatorOrElement)). 801 then(function(result) { return !!result; }); 802 } else { 803 return this.findElements.apply(this, arguments).then(function(result) { 804 return !!result.length; 805 }); 806 } 807 }; 808 809 810 /** 811 * Schedule a command to search for multiple elements on the page. 812 * 813 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The locator 814 * strategy to use when searching for the element. 815 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A 816 * promise that will resolve to an array of WebElements. 817 */ 818 webdriver.WebDriver.prototype.findElements = function(locator) { 819 locator = webdriver.Locator.checkLocator(locator); 820 if (goog.isFunction(locator)) { 821 return this.findElementsInternal_(locator, this); 822 } else { 823 var command = new webdriver.Command(webdriver.CommandName.FIND_ELEMENTS). 824 setParameter('using', locator.using). 825 setParameter('value', locator.value); 826 return this.schedule(command, 'WebDriver.findElements(' + locator + ')'); 827 } 828 }; 829 830 831 /** 832 * @param {!Function} locatorFn The locator function to use. 833 * @param {!(webdriver.WebDriver|webdriver.WebElement)} context The search 834 * context. 835 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A 836 * promise that will resolve to an array of WebElements. 837 * @private 838 */ 839 webdriver.WebDriver.prototype.findElementsInternal_ = function( 840 locatorFn, context) { 841 return this.call(goog.partial(locatorFn, context)).then(function(result) { 842 if (result instanceof webdriver.WebElement) { 843 return [result]; 844 } 845 846 if (!goog.isArray(result)) { 847 return []; 848 } 849 850 return goog.array.filter(result, function(item) { 851 return item instanceof webdriver.WebElement; 852 }); 853 }); 854 }; 855 856 857 /** 858 * Schedule a command to take a screenshot. The driver makes a best effort to 859 * return a screenshot of the following, in order of preference: 860 * <ol> 861 * <li>Entire page 862 * <li>Current window 863 * <li>Visible portion of the current frame 864 * <li>The screenshot of the entire display containing the browser 865 * </ol> 866 * 867 * @return {!webdriver.promise.Promise.<string>} A promise that will be 868 * resolved to the screenshot as a base-64 encoded PNG. 869 */ 870 webdriver.WebDriver.prototype.takeScreenshot = function() { 871 return this.schedule(new webdriver.Command(webdriver.CommandName.SCREENSHOT), 872 'WebDriver.takeScreenshot()'); 873 }; 874 875 876 /** 877 * @return {!webdriver.WebDriver.Options} The options interface for this 878 * instance. 879 */ 880 webdriver.WebDriver.prototype.manage = function() { 881 return new webdriver.WebDriver.Options(this); 882 }; 883 884 885 /** 886 * @return {!webdriver.WebDriver.Navigation} The navigation interface for this 887 * instance. 888 */ 889 webdriver.WebDriver.prototype.navigate = function() { 890 return new webdriver.WebDriver.Navigation(this); 891 }; 892 893 894 /** 895 * @return {!webdriver.WebDriver.TargetLocator} The target locator interface for 896 * this instance. 897 */ 898 webdriver.WebDriver.prototype.switchTo = function() { 899 return new webdriver.WebDriver.TargetLocator(this); 900 }; 901 902 903 904 /** 905 * Interface for navigating back and forth in the browser history. 906 * @param {!webdriver.WebDriver} driver The parent driver. 907 * @constructor 908 */ 909 webdriver.WebDriver.Navigation = function(driver) { 910 911 /** @private {!webdriver.WebDriver} */ 912 this.driver_ = driver; 913 }; 914 915 916 /** 917 * Schedules a command to navigate to a new URL. 918 * @param {string} url The URL to navigate to. 919 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 920 * when the URL has been loaded. 921 */ 922 webdriver.WebDriver.Navigation.prototype.to = function(url) { 923 return this.driver_.schedule( 924 new webdriver.Command(webdriver.CommandName.GET). 925 setParameter('url', url), 926 'WebDriver.navigate().to(' + url + ')'); 927 }; 928 929 930 /** 931 * Schedules a command to move backwards in the browser history. 932 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 933 * when the navigation event has completed. 934 */ 935 webdriver.WebDriver.Navigation.prototype.back = function() { 936 return this.driver_.schedule( 937 new webdriver.Command(webdriver.CommandName.GO_BACK), 938 'WebDriver.navigate().back()'); 939 }; 940 941 942 /** 943 * Schedules a command to move forwards in the browser history. 944 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 945 * when the navigation event has completed. 946 */ 947 webdriver.WebDriver.Navigation.prototype.forward = function() { 948 return this.driver_.schedule( 949 new webdriver.Command(webdriver.CommandName.GO_FORWARD), 950 'WebDriver.navigate().forward()'); 951 }; 952 953 954 /** 955 * Schedules a command to refresh the current page. 956 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 957 * when the navigation event has completed. 958 */ 959 webdriver.WebDriver.Navigation.prototype.refresh = function() { 960 return this.driver_.schedule( 961 new webdriver.Command(webdriver.CommandName.REFRESH), 962 'WebDriver.navigate().refresh()'); 963 }; 964 965 966 967 /** 968 * Provides methods for managing browser and driver state. 969 * @param {!webdriver.WebDriver} driver The parent driver. 970 * @constructor 971 */ 972 webdriver.WebDriver.Options = function(driver) { 973 974 /** @private {!webdriver.WebDriver} */ 975 this.driver_ = driver; 976 }; 977 978 979 /** 980 * A JSON description of a browser cookie. 981 * @typedef {{ 982 * name: string, 983 * value: string, 984 * path: (string|undefined), 985 * domain: (string|undefined), 986 * secure: (boolean|undefined), 987 * expiry: (number|undefined) 988 * }} 989 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object 990 */ 991 webdriver.WebDriver.Options.Cookie; 992 993 994 /** 995 * Schedules a command to add a cookie. 996 * @param {string} name The cookie name. 997 * @param {string} value The cookie value. 998 * @param {string=} opt_path The cookie path. 999 * @param {string=} opt_domain The cookie domain. 1000 * @param {boolean=} opt_isSecure Whether the cookie is secure. 1001 * @param {(number|!Date)=} opt_expiry When the cookie expires. If specified as 1002 * a number, should be in milliseconds since midnight, January 1, 1970 UTC. 1003 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1004 * when the cookie has been added to the page. 1005 */ 1006 webdriver.WebDriver.Options.prototype.addCookie = function( 1007 name, value, opt_path, opt_domain, opt_isSecure, opt_expiry) { 1008 // We do not allow '=' or ';' in the name. 1009 if (/[;=]/.test(name)) { 1010 throw Error('Invalid cookie name "' + name + '"'); 1011 } 1012 1013 // We do not allow ';' in value. 1014 if (/;/.test(value)) { 1015 throw Error('Invalid cookie value "' + value + '"'); 1016 } 1017 1018 var cookieString = name + '=' + value + 1019 (opt_domain ? ';domain=' + opt_domain : '') + 1020 (opt_path ? ';path=' + opt_path : '') + 1021 (opt_isSecure ? ';secure' : ''); 1022 1023 var expiry; 1024 if (goog.isDef(opt_expiry)) { 1025 var expiryDate; 1026 if (goog.isNumber(opt_expiry)) { 1027 expiryDate = new Date(opt_expiry); 1028 } else { 1029 expiryDate = /** @type {!Date} */ (opt_expiry); 1030 opt_expiry = expiryDate.getTime(); 1031 } 1032 cookieString += ';expires=' + expiryDate.toUTCString(); 1033 // Convert from milliseconds to seconds. 1034 expiry = Math.floor(/** @type {number} */ (opt_expiry) / 1000); 1035 } 1036 1037 return this.driver_.schedule( 1038 new webdriver.Command(webdriver.CommandName.ADD_COOKIE). 1039 setParameter('cookie', { 1040 'name': name, 1041 'value': value, 1042 'path': opt_path, 1043 'domain': opt_domain, 1044 'secure': !!opt_isSecure, 1045 'expiry': expiry 1046 }), 1047 'WebDriver.manage().addCookie(' + cookieString + ')'); 1048 }; 1049 1050 1051 /** 1052 * Schedules a command to delete all cookies visible to the current page. 1053 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1054 * when all cookies have been deleted. 1055 */ 1056 webdriver.WebDriver.Options.prototype.deleteAllCookies = function() { 1057 return this.driver_.schedule( 1058 new webdriver.Command(webdriver.CommandName.DELETE_ALL_COOKIES), 1059 'WebDriver.manage().deleteAllCookies()'); 1060 }; 1061 1062 1063 /** 1064 * Schedules a command to delete the cookie with the given name. This command is 1065 * a no-op if there is no cookie with the given name visible to the current 1066 * page. 1067 * @param {string} name The name of the cookie to delete. 1068 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1069 * when the cookie has been deleted. 1070 */ 1071 webdriver.WebDriver.Options.prototype.deleteCookie = function(name) { 1072 return this.driver_.schedule( 1073 new webdriver.Command(webdriver.CommandName.DELETE_COOKIE). 1074 setParameter('name', name), 1075 'WebDriver.manage().deleteCookie(' + name + ')'); 1076 }; 1077 1078 1079 /** 1080 * Schedules a command to retrieve all cookies visible to the current page. 1081 * Each cookie will be returned as a JSON object as described by the WebDriver 1082 * wire protocol. 1083 * @return {!webdriver.promise.Promise.< 1084 * !Array.<webdriver.WebDriver.Options.Cookie>>} A promise that will be 1085 * resolved with the cookies visible to the current page. 1086 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object 1087 */ 1088 webdriver.WebDriver.Options.prototype.getCookies = function() { 1089 return this.driver_.schedule( 1090 new webdriver.Command(webdriver.CommandName.GET_ALL_COOKIES), 1091 'WebDriver.manage().getCookies()'); 1092 }; 1093 1094 1095 /** 1096 * Schedules a command to retrieve the cookie with the given name. Returns null 1097 * if there is no such cookie. The cookie will be returned as a JSON object as 1098 * described by the WebDriver wire protocol. 1099 * @param {string} name The name of the cookie to retrieve. 1100 * @return {!webdriver.promise.Promise.<?webdriver.WebDriver.Options.Cookie>} A 1101 * promise that will be resolved with the named cookie, or {@code null} 1102 * if there is no such cookie. 1103 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object 1104 */ 1105 webdriver.WebDriver.Options.prototype.getCookie = function(name) { 1106 return this.getCookies().then(function(cookies) { 1107 return goog.array.find(cookies, function(cookie) { 1108 return cookie && cookie['name'] == name; 1109 }); 1110 }); 1111 }; 1112 1113 1114 /** 1115 * @return {!webdriver.WebDriver.Logs} The interface for managing driver 1116 * logs. 1117 */ 1118 webdriver.WebDriver.Options.prototype.logs = function() { 1119 return new webdriver.WebDriver.Logs(this.driver_); 1120 }; 1121 1122 1123 /** 1124 * @return {!webdriver.WebDriver.Timeouts} The interface for managing driver 1125 * timeouts. 1126 */ 1127 webdriver.WebDriver.Options.prototype.timeouts = function() { 1128 return new webdriver.WebDriver.Timeouts(this.driver_); 1129 }; 1130 1131 1132 /** 1133 * @return {!webdriver.WebDriver.Window} The interface for managing the 1134 * current window. 1135 */ 1136 webdriver.WebDriver.Options.prototype.window = function() { 1137 return new webdriver.WebDriver.Window(this.driver_); 1138 }; 1139 1140 1141 1142 /** 1143 * An interface for managing timeout behavior for WebDriver instances. 1144 * @param {!webdriver.WebDriver} driver The parent driver. 1145 * @constructor 1146 */ 1147 webdriver.WebDriver.Timeouts = function(driver) { 1148 1149 /** @private {!webdriver.WebDriver} */ 1150 this.driver_ = driver; 1151 }; 1152 1153 1154 /** 1155 * Specifies the amount of time the driver should wait when searching for an 1156 * element if it is not immediately present. 1157 * <p/> 1158 * When searching for a single element, the driver should poll the page 1159 * until the element has been found, or this timeout expires before failing 1160 * with a {@code bot.ErrorCode.NO_SUCH_ELEMENT} error. When searching 1161 * for multiple elements, the driver should poll the page until at least one 1162 * element has been found or this timeout has expired. 1163 * <p/> 1164 * Setting the wait timeout to 0 (its default value), disables implicit 1165 * waiting. 1166 * <p/> 1167 * Increasing the implicit wait timeout should be used judiciously as it 1168 * will have an adverse effect on test run time, especially when used with 1169 * slower location strategies like XPath. 1170 * 1171 * @param {number} ms The amount of time to wait, in milliseconds. 1172 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1173 * when the implicit wait timeout has been set. 1174 */ 1175 webdriver.WebDriver.Timeouts.prototype.implicitlyWait = function(ms) { 1176 return this.driver_.schedule( 1177 new webdriver.Command(webdriver.CommandName.IMPLICITLY_WAIT). 1178 setParameter('ms', ms < 0 ? 0 : ms), 1179 'WebDriver.manage().timeouts().implicitlyWait(' + ms + ')'); 1180 }; 1181 1182 1183 /** 1184 * Sets the amount of time to wait, in milliseconds, for an asynchronous script 1185 * to finish execution before returning an error. If the timeout is less than or 1186 * equal to 0, the script will be allowed to run indefinitely. 1187 * 1188 * @param {number} ms The amount of time to wait, in milliseconds. 1189 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1190 * when the script timeout has been set. 1191 */ 1192 webdriver.WebDriver.Timeouts.prototype.setScriptTimeout = function(ms) { 1193 return this.driver_.schedule( 1194 new webdriver.Command(webdriver.CommandName.SET_SCRIPT_TIMEOUT). 1195 setParameter('ms', ms < 0 ? 0 : ms), 1196 'WebDriver.manage().timeouts().setScriptTimeout(' + ms + ')'); 1197 }; 1198 1199 1200 /** 1201 * Sets the amount of time to wait for a page load to complete before returning 1202 * an error. If the timeout is negative, page loads may be indefinite. 1203 * @param {number} ms The amount of time to wait, in milliseconds. 1204 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1205 * when the timeout has been set. 1206 */ 1207 webdriver.WebDriver.Timeouts.prototype.pageLoadTimeout = function(ms) { 1208 return this.driver_.schedule( 1209 new webdriver.Command(webdriver.CommandName.SET_TIMEOUT). 1210 setParameter('type', 'page load'). 1211 setParameter('ms', ms), 1212 'WebDriver.manage().timeouts().pageLoadTimeout(' + ms + ')'); 1213 }; 1214 1215 1216 1217 /** 1218 * An interface for managing the current window. 1219 * @param {!webdriver.WebDriver} driver The parent driver. 1220 * @constructor 1221 */ 1222 webdriver.WebDriver.Window = function(driver) { 1223 1224 /** @private {!webdriver.WebDriver} */ 1225 this.driver_ = driver; 1226 }; 1227 1228 1229 /** 1230 * Retrieves the window's current position, relative to the top left corner of 1231 * the screen. 1232 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that 1233 * will be resolved with the window's position in the form of a 1234 * {x:number, y:number} object literal. 1235 */ 1236 webdriver.WebDriver.Window.prototype.getPosition = function() { 1237 return this.driver_.schedule( 1238 new webdriver.Command(webdriver.CommandName.GET_WINDOW_POSITION). 1239 setParameter('windowHandle', 'current'), 1240 'WebDriver.manage().window().getPosition()'); 1241 }; 1242 1243 1244 /** 1245 * Repositions the current window. 1246 * @param {number} x The desired horizontal position, relative to the left side 1247 * of the screen. 1248 * @param {number} y The desired vertical position, relative to the top of the 1249 * of the screen. 1250 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1251 * when the command has completed. 1252 */ 1253 webdriver.WebDriver.Window.prototype.setPosition = function(x, y) { 1254 return this.driver_.schedule( 1255 new webdriver.Command(webdriver.CommandName.SET_WINDOW_POSITION). 1256 setParameter('windowHandle', 'current'). 1257 setParameter('x', x). 1258 setParameter('y', y), 1259 'WebDriver.manage().window().setPosition(' + x + ', ' + y + ')'); 1260 }; 1261 1262 1263 /** 1264 * Retrieves the window's current size. 1265 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A 1266 * promise that will be resolved with the window's size in the form of a 1267 * {width:number, height:number} object literal. 1268 */ 1269 webdriver.WebDriver.Window.prototype.getSize = function() { 1270 return this.driver_.schedule( 1271 new webdriver.Command(webdriver.CommandName.GET_WINDOW_SIZE). 1272 setParameter('windowHandle', 'current'), 1273 'WebDriver.manage().window().getSize()'); 1274 }; 1275 1276 1277 /** 1278 * Resizes the current window. 1279 * @param {number} width The desired window width. 1280 * @param {number} height The desired window height. 1281 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1282 * when the command has completed. 1283 */ 1284 webdriver.WebDriver.Window.prototype.setSize = function(width, height) { 1285 return this.driver_.schedule( 1286 new webdriver.Command(webdriver.CommandName.SET_WINDOW_SIZE). 1287 setParameter('windowHandle', 'current'). 1288 setParameter('width', width). 1289 setParameter('height', height), 1290 'WebDriver.manage().window().setSize(' + width + ', ' + height + ')'); 1291 }; 1292 1293 1294 /** 1295 * Maximizes the current window. 1296 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1297 * when the command has completed. 1298 */ 1299 webdriver.WebDriver.Window.prototype.maximize = function() { 1300 return this.driver_.schedule( 1301 new webdriver.Command(webdriver.CommandName.MAXIMIZE_WINDOW). 1302 setParameter('windowHandle', 'current'), 1303 'WebDriver.manage().window().maximize()'); 1304 }; 1305 1306 1307 /** 1308 * Interface for managing WebDriver log records. 1309 * @param {!webdriver.WebDriver} driver The parent driver. 1310 * @constructor 1311 */ 1312 webdriver.WebDriver.Logs = function(driver) { 1313 1314 /** @private {!webdriver.WebDriver} */ 1315 this.driver_ = driver; 1316 }; 1317 1318 1319 /** 1320 * Fetches available log entries for the given type. 1321 * 1322 * <p/>Note that log buffers are reset after each call, meaning that 1323 * available log entries correspond to those entries not yet returned for a 1324 * given log type. In practice, this means that this call will return the 1325 * available log entries since the last call, or from the start of the 1326 * session. 1327 * 1328 * @param {!webdriver.logging.Type} type The desired log type. 1329 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Entry>>} A 1330 * promise that will resolve to a list of log entries for the specified 1331 * type. 1332 */ 1333 webdriver.WebDriver.Logs.prototype.get = function(type) { 1334 return this.driver_.schedule( 1335 new webdriver.Command(webdriver.CommandName.GET_LOG). 1336 setParameter('type', type), 1337 'WebDriver.manage().logs().get(' + type + ')'). 1338 then(function(entries) { 1339 return goog.array.map(entries, function(entry) { 1340 if (!(entry instanceof webdriver.logging.Entry)) { 1341 return new webdriver.logging.Entry( 1342 entry['level'], entry['message'], entry['timestamp']); 1343 } 1344 return entry; 1345 }); 1346 }); 1347 }; 1348 1349 1350 /** 1351 * Retrieves the log types available to this driver. 1352 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.logging.Type>>} A 1353 * promise that will resolve to a list of available log types. 1354 */ 1355 webdriver.WebDriver.Logs.prototype.getAvailableLogTypes = function() { 1356 return this.driver_.schedule( 1357 new webdriver.Command(webdriver.CommandName.GET_AVAILABLE_LOG_TYPES), 1358 'WebDriver.manage().logs().getAvailableLogTypes()'); 1359 }; 1360 1361 1362 1363 /** 1364 * An interface for changing the focus of the driver to another frame or window. 1365 * @param {!webdriver.WebDriver} driver The parent driver. 1366 * @constructor 1367 */ 1368 webdriver.WebDriver.TargetLocator = function(driver) { 1369 1370 /** @private {!webdriver.WebDriver} */ 1371 this.driver_ = driver; 1372 }; 1373 1374 1375 /** 1376 * Schedules a command retrieve the {@code document.activeElement} element on 1377 * the current document, or {@code document.body} if activeElement is not 1378 * available. 1379 * @return {!webdriver.WebElement} The active element. 1380 */ 1381 webdriver.WebDriver.TargetLocator.prototype.activeElement = function() { 1382 var id = this.driver_.schedule( 1383 new webdriver.Command(webdriver.CommandName.GET_ACTIVE_ELEMENT), 1384 'WebDriver.switchTo().activeElement()'); 1385 return new webdriver.WebElement(this.driver_, id); 1386 }; 1387 1388 1389 /** 1390 * Schedules a command to switch focus of all future commands to the first frame 1391 * on the page. 1392 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1393 * when the driver has changed focus to the default content. 1394 */ 1395 webdriver.WebDriver.TargetLocator.prototype.defaultContent = function() { 1396 return this.driver_.schedule( 1397 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME). 1398 setParameter('id', null), 1399 'WebDriver.switchTo().defaultContent()'); 1400 }; 1401 1402 1403 /** 1404 * Schedules a command to switch the focus of all future commands to another 1405 * frame on the page. 1406 * <p/> 1407 * If the frame is specified by a number, the command will switch to the frame 1408 * by its (zero-based) index into the {@code window.frames} collection. 1409 * <p/> 1410 * If the frame is specified by a string, the command will select the frame by 1411 * its name or ID. To select sub-frames, simply separate the frame names/IDs by 1412 * dots. As an example, "main.child" will select the frame with the name "main" 1413 * and then its child "child". 1414 * <p/> 1415 * If the specified frame can not be found, the deferred result will errback 1416 * with a {@code bot.ErrorCode.NO_SUCH_FRAME} error. 1417 * @param {string|number} nameOrIndex The frame locator. 1418 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1419 * when the driver has changed focus to the specified frame. 1420 */ 1421 webdriver.WebDriver.TargetLocator.prototype.frame = function(nameOrIndex) { 1422 return this.driver_.schedule( 1423 new webdriver.Command(webdriver.CommandName.SWITCH_TO_FRAME). 1424 setParameter('id', nameOrIndex), 1425 'WebDriver.switchTo().frame(' + nameOrIndex + ')'); 1426 }; 1427 1428 1429 /** 1430 * Schedules a command to switch the focus of all future commands to another 1431 * window. Windows may be specified by their {@code window.name} attribute or 1432 * by its handle (as returned by {@code webdriver.WebDriver#getWindowHandles}). 1433 * <p/> 1434 * If the specificed window can not be found, the deferred result will errback 1435 * with a {@code bot.ErrorCode.NO_SUCH_WINDOW} error. 1436 * @param {string} nameOrHandle The name or window handle of the window to 1437 * switch focus to. 1438 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1439 * when the driver has changed focus to the specified window. 1440 */ 1441 webdriver.WebDriver.TargetLocator.prototype.window = function(nameOrHandle) { 1442 return this.driver_.schedule( 1443 new webdriver.Command(webdriver.CommandName.SWITCH_TO_WINDOW). 1444 setParameter('name', nameOrHandle), 1445 'WebDriver.switchTo().window(' + nameOrHandle + ')'); 1446 }; 1447 1448 1449 /** 1450 * Schedules a command to change focus to the active alert dialog. This command 1451 * will return a {@link bot.ErrorCode.NO_MODAL_DIALOG_OPEN} error if a modal 1452 * dialog is not currently open. 1453 * @return {!webdriver.Alert} The open alert. 1454 */ 1455 webdriver.WebDriver.TargetLocator.prototype.alert = function() { 1456 var text = this.driver_.schedule( 1457 new webdriver.Command(webdriver.CommandName.GET_ALERT_TEXT), 1458 'WebDriver.switchTo().alert()'); 1459 return new webdriver.Alert(this.driver_, text); 1460 }; 1461 1462 1463 /** 1464 * Simulate pressing many keys at once in a "chord". Takes a sequence of 1465 * {@link webdriver.Key}s or strings, appends each of the values to a string, 1466 * and adds the chord termination key ({@link webdriver.Key.NULL}) and returns 1467 * the resultant string. 1468 * 1469 * Note: when the low-level webdriver key handlers see Keys.NULL, active 1470 * modifier keys (CTRL/ALT/SHIFT/etc) release via a keyup event. 1471 * 1472 * @param {...string} var_args The key sequence to concatenate. 1473 * @return {string} The null-terminated key sequence. 1474 * @see http://code.google.com/p/webdriver/issues/detail?id=79 1475 */ 1476 webdriver.Key.chord = function(var_args) { 1477 var sequence = goog.array.reduce( 1478 goog.array.slice(arguments, 0), 1479 function(str, key) { 1480 return str + key; 1481 }, ''); 1482 sequence += webdriver.Key.NULL; 1483 return sequence; 1484 }; 1485 1486 1487 ////////////////////////////////////////////////////////////////////////////// 1488 // 1489 // webdriver.WebElement 1490 // 1491 ////////////////////////////////////////////////////////////////////////////// 1492 1493 1494 1495 /** 1496 * Represents a DOM element. WebElements can be found by searching from the 1497 * document root using a {@code webdriver.WebDriver} instance, or by searching 1498 * under another {@code webdriver.WebElement}: 1499 * <pre><code> 1500 * driver.get('http://www.google.com'); 1501 * var searchForm = driver.findElement(By.tagName('form')); 1502 * var searchBox = searchForm.findElement(By.name('q')); 1503 * searchBox.sendKeys('webdriver'); 1504 * </code></pre> 1505 * 1506 * The WebElement is implemented as a promise for compatibility with the promise 1507 * API. It will always resolve itself when its internal state has been fully 1508 * resolved and commands may be issued against the element. This can be used to 1509 * catch errors when an element cannot be located on the page: 1510 * <pre><code> 1511 * driver.findElement(By.id('not-there')).then(function(element) { 1512 * alert('Found an element that was not expected to be there!'); 1513 * }, function(error) { 1514 * alert('The element was not found, as expected'); 1515 * }); 1516 * </code></pre> 1517 * 1518 * @param {!webdriver.WebDriver} driver The parent WebDriver instance for this 1519 * element. 1520 * @param {!(string|webdriver.promise.Promise)} id Either the opaque ID for the 1521 * underlying DOM element assigned by the server, or a promise that will 1522 * resolve to that ID or another WebElement. 1523 * @constructor 1524 * @extends {webdriver.promise.Deferred} 1525 */ 1526 webdriver.WebElement = function(driver, id) { 1527 webdriver.promise.Deferred.call(this, null, driver.controlFlow()); 1528 1529 /** 1530 * The parent WebDriver instance for this element. 1531 * @private {!webdriver.WebDriver} 1532 */ 1533 this.driver_ = driver; 1534 1535 // This class is responsible for resolving itself; delete the resolve and 1536 // reject methods so they may not be accessed by consumers of this class. 1537 var fulfill = goog.partial(this.fulfill, this); 1538 var reject = this.reject; 1539 delete this.promise; 1540 delete this.fulfill; 1541 delete this.reject; 1542 1543 /** 1544 * A promise that resolves to the JSON representation of this WebElement's 1545 * ID, as defined by the WebDriver wire protocol. 1546 * @private {!webdriver.promise.Promise.<webdriver.WebElement.Id>} 1547 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol 1548 */ 1549 this.id_ = webdriver.promise.when(id, function(id) { 1550 if (id instanceof webdriver.WebElement) { 1551 return id.id_; 1552 } else if (goog.isDef(id[webdriver.WebElement.ELEMENT_KEY])) { 1553 return id; 1554 } 1555 1556 var json = {}; 1557 json[webdriver.WebElement.ELEMENT_KEY] = id; 1558 return json; 1559 }); 1560 1561 // This WebElement should not be resolved until its ID has been 1562 // fully resolved. 1563 this.id_.then(fulfill, reject); 1564 }; 1565 goog.inherits(webdriver.WebElement, webdriver.promise.Deferred); 1566 1567 1568 /** 1569 * Wire protocol definition of a WebElement ID. 1570 * @typedef {{ELEMENT: string}} 1571 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol 1572 */ 1573 webdriver.WebElement.Id; 1574 1575 1576 /** 1577 * The property key used in the wire protocol to indicate that a JSON object 1578 * contains the ID of a WebElement. 1579 * @type {string} 1580 * @const 1581 */ 1582 webdriver.WebElement.ELEMENT_KEY = 'ELEMENT'; 1583 1584 1585 /** 1586 * Compares to WebElements for equality. 1587 * @param {!webdriver.WebElement} a A WebElement. 1588 * @param {!webdriver.WebElement} b A WebElement. 1589 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be 1590 * resolved to whether the two WebElements are equal. 1591 */ 1592 webdriver.WebElement.equals = function(a, b) { 1593 if (a == b) { 1594 return webdriver.promise.fulfilled(true); 1595 } 1596 return webdriver.promise.fullyResolved([a.id_, b.id_]).then(function(ids) { 1597 // If the two element's have the same ID, they should be considered 1598 // equal. Otherwise, they may still be equivalent, but we'll need to 1599 // ask the server to check for us. 1600 if (ids[0][webdriver.WebElement.ELEMENT_KEY] == 1601 ids[1][webdriver.WebElement.ELEMENT_KEY]) { 1602 return true; 1603 } 1604 1605 var command = new webdriver.Command( 1606 webdriver.CommandName.ELEMENT_EQUALS); 1607 command.setParameter('other', b); 1608 return a.schedule_(command, 'webdriver.WebElement.equals()'); 1609 }); 1610 }; 1611 1612 1613 /** 1614 * @return {!webdriver.WebDriver} The parent driver for this instance. 1615 */ 1616 webdriver.WebElement.prototype.getDriver = function() { 1617 return this.driver_; 1618 }; 1619 1620 1621 /** 1622 * @return {!webdriver.promise.Promise.<webdriver.WebElement.Id>} A promise 1623 * that resolves to this element's JSON representation as defined by the 1624 * WebDriver wire protocol. 1625 * @see http://code.google.com/p/selenium/wiki/JsonWireProtocol 1626 */ 1627 webdriver.WebElement.prototype.toWireValue = function() { 1628 return this.id_; 1629 }; 1630 1631 1632 /** 1633 * Schedules a command that targets this element with the parent WebDriver 1634 * instance. Will ensure this element's ID is included in the command parameters 1635 * under the "id" key. 1636 * @param {!webdriver.Command} command The command to schedule. 1637 * @param {string} description A description of the command for debugging. 1638 * @return {!webdriver.promise.Promise.<T>} A promise that will be resolved 1639 * with the command result. 1640 * @template T 1641 * @see webdriver.WebDriver.prototype.schedule 1642 * @private 1643 */ 1644 webdriver.WebElement.prototype.schedule_ = function(command, description) { 1645 command.setParameter('id', this.id_); 1646 return this.driver_.schedule(command, description); 1647 }; 1648 1649 1650 /** 1651 * Schedule a command to find a descendant of this element. If the element 1652 * cannot be found, a {@code bot.ErrorCode.NO_SUCH_ELEMENT} result will 1653 * be returned by the driver. Unlike other commands, this error cannot be 1654 * suppressed. In other words, scheduling a command to find an element doubles 1655 * as an assert that the element is present on the page. To test whether an 1656 * element is present on the page, use {@code #isElementPresent} instead. 1657 * 1658 * <p>The search criteria for an element may be defined using one of the 1659 * factories in the {@link webdriver.By} namespace, or as a short-hand 1660 * {@link webdriver.By.Hash} object. For example, the following two statements 1661 * are equivalent: 1662 * <code><pre> 1663 * var e1 = element.findElement(By.id('foo')); 1664 * var e2 = element.findElement({id:'foo'}); 1665 * </pre></code> 1666 * 1667 * <p>You may also provide a custom locator function, which takes as input 1668 * this WebDriver instance and returns a {@link webdriver.WebElement}, or a 1669 * promise that will resolve to a WebElement. For example, to find the first 1670 * visible link on a page, you could write: 1671 * <code><pre> 1672 * var link = element.findElement(firstVisibleLink); 1673 * 1674 * function firstVisibleLink(element) { 1675 * var links = element.findElements(By.tagName('a')); 1676 * return webdriver.promise.filter(links, function(link) { 1677 * return links.isDisplayed(); 1678 * }).then(function(visibleLinks) { 1679 * return visibleLinks[0]; 1680 * }); 1681 * } 1682 * </pre></code> 1683 * 1684 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The 1685 * locator strategy to use when searching for the element. 1686 * @return {!webdriver.WebElement} A WebElement that can be used to issue 1687 * commands against the located element. If the element is not found, the 1688 * element will be invalidated and all scheduled commands aborted. 1689 */ 1690 webdriver.WebElement.prototype.findElement = function(locator) { 1691 locator = webdriver.Locator.checkLocator(locator); 1692 var id; 1693 if (goog.isFunction(locator)) { 1694 id = this.driver_.findElementInternal_(locator, this); 1695 } else { 1696 var command = new webdriver.Command( 1697 webdriver.CommandName.FIND_CHILD_ELEMENT). 1698 setParameter('using', locator.using). 1699 setParameter('value', locator.value); 1700 id = this.schedule_(command, 'WebElement.findElement(' + locator + ')'); 1701 } 1702 return new webdriver.WebElement(this.driver_, id); 1703 }; 1704 1705 1706 /** 1707 * Schedules a command to test if there is at least one descendant of this 1708 * element that matches the given search criteria. 1709 * 1710 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The 1711 * locator strategy to use when searching for the element. 1712 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be 1713 * resolved with whether an element could be located on the page. 1714 */ 1715 webdriver.WebElement.prototype.isElementPresent = function(locator) { 1716 return this.findElements(locator).then(function(result) { 1717 return !!result.length; 1718 }); 1719 }; 1720 1721 1722 /** 1723 * Schedules a command to find all of the descendants of this element that 1724 * match the given search criteria. 1725 * 1726 * @param {!(webdriver.Locator|webdriver.By.Hash|Function)} locator The 1727 * locator strategy to use when searching for the elements. 1728 * @return {!webdriver.promise.Promise.<!Array.<!webdriver.WebElement>>} A 1729 * promise that will resolve to an array of WebElements. 1730 */ 1731 webdriver.WebElement.prototype.findElements = function(locator) { 1732 locator = webdriver.Locator.checkLocator(locator); 1733 if (goog.isFunction(locator)) { 1734 return this.driver_.findElementsInternal_(locator, this); 1735 } else { 1736 var command = new webdriver.Command( 1737 webdriver.CommandName.FIND_CHILD_ELEMENTS). 1738 setParameter('using', locator.using). 1739 setParameter('value', locator.value); 1740 return this.schedule_(command, 'WebElement.findElements(' + locator + ')'); 1741 } 1742 }; 1743 1744 1745 /** 1746 * Schedules a command to click on this element. 1747 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1748 * when the click command has completed. 1749 */ 1750 webdriver.WebElement.prototype.click = function() { 1751 return this.schedule_( 1752 new webdriver.Command(webdriver.CommandName.CLICK_ELEMENT), 1753 'WebElement.click()'); 1754 }; 1755 1756 1757 /** 1758 * Schedules a command to type a sequence on the DOM element represented by this 1759 * instance. 1760 * <p/> 1761 * Modifier keys (SHIFT, CONTROL, ALT, META) are stateful; once a modifier is 1762 * processed in the keysequence, that key state is toggled until one of the 1763 * following occurs: 1764 * <ul> 1765 * <li>The modifier key is encountered again in the sequence. At this point the 1766 * state of the key is toggled (along with the appropriate keyup/down events). 1767 * </li> 1768 * <li>The {@code webdriver.Key.NULL} key is encountered in the sequence. When 1769 * this key is encountered, all modifier keys current in the down state are 1770 * released (with accompanying keyup events). The NULL key can be used to 1771 * simulate common keyboard shortcuts: 1772 * <code><pre> 1773 * element.sendKeys("text was", 1774 * webdriver.Key.CONTROL, "a", webdriver.Key.NULL, 1775 * "now text is"); 1776 * // Alternatively: 1777 * element.sendKeys("text was", 1778 * webdriver.Key.chord(webdriver.Key.CONTROL, "a"), 1779 * "now text is"); 1780 * </pre></code></li> 1781 * <li>The end of the keysequence is encountered. When there are no more keys 1782 * to type, all depressed modifier keys are released (with accompanying keyup 1783 * events). 1784 * </li> 1785 * </ul> 1786 * <strong>Note:</strong> On browsers where native keyboard events are not yet 1787 * supported (e.g. Firefox on OS X), key events will be synthesized. Special 1788 * punctionation keys will be synthesized according to a standard QWERTY en-us 1789 * keyboard layout. 1790 * 1791 * @param {...string} var_args The sequence of keys to 1792 * type. All arguments will be joined into a single sequence (var_args is 1793 * permitted for convenience). 1794 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1795 * when all keys have been typed. 1796 */ 1797 webdriver.WebElement.prototype.sendKeys = function(var_args) { 1798 // Coerce every argument to a string. This protects us from users that 1799 // ignore the jsdoc and give us a number (which ends up causing problems on 1800 // the server, which requires strings). 1801 var keys = webdriver.promise.fullyResolved(goog.array.slice(arguments, 0)). 1802 then(function(args) { 1803 return goog.array.map(goog.array.slice(args, 0), function(key) { 1804 return key + ''; 1805 }); 1806 }); 1807 return this.schedule_( 1808 new webdriver.Command(webdriver.CommandName.SEND_KEYS_TO_ELEMENT). 1809 setParameter('value', keys), 1810 'WebElement.sendKeys(' + keys + ')'); 1811 }; 1812 1813 1814 /** 1815 * Schedules a command to query for the tag/node name of this element. 1816 * @return {!webdriver.promise.Promise.<string>} A promise that will be 1817 * resolved with the element's tag name. 1818 */ 1819 webdriver.WebElement.prototype.getTagName = function() { 1820 return this.schedule_( 1821 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TAG_NAME), 1822 'WebElement.getTagName()'); 1823 }; 1824 1825 1826 /** 1827 * Schedules a command to query for the computed style of the element 1828 * represented by this instance. If the element inherits the named style from 1829 * its parent, the parent will be queried for its value. Where possible, color 1830 * values will be converted to their hex representation (e.g. #00ff00 instead of 1831 * rgb(0, 255, 0)). 1832 * <p/> 1833 * <em>Warning:</em> the value returned will be as the browser interprets it, so 1834 * it may be tricky to form a proper assertion. 1835 * 1836 * @param {string} cssStyleProperty The name of the CSS style property to look 1837 * up. 1838 * @return {!webdriver.promise.Promise.<string>} A promise that will be 1839 * resolved with the requested CSS value. 1840 */ 1841 webdriver.WebElement.prototype.getCssValue = function(cssStyleProperty) { 1842 var name = webdriver.CommandName.GET_ELEMENT_VALUE_OF_CSS_PROPERTY; 1843 return this.schedule_( 1844 new webdriver.Command(name). 1845 setParameter('propertyName', cssStyleProperty), 1846 'WebElement.getCssValue(' + cssStyleProperty + ')'); 1847 }; 1848 1849 1850 /** 1851 * Schedules a command to query for the value of the given attribute of the 1852 * element. Will return the current value, even if it has been modified after 1853 * the page has been loaded. More exactly, this method will return the value of 1854 * the given attribute, unless that attribute is not present, in which case the 1855 * value of the property with the same name is returned. If neither value is 1856 * set, null is returned (for example, the "value" property of a textarea 1857 * element). The "style" attribute is converted as best can be to a 1858 * text representation with a trailing semi-colon. The following are deemed to 1859 * be "boolean" attributes and will return either "true" or null: 1860 * 1861 * <p>async, autofocus, autoplay, checked, compact, complete, controls, declare, 1862 * defaultchecked, defaultselected, defer, disabled, draggable, ended, 1863 * formnovalidate, hidden, indeterminate, iscontenteditable, ismap, itemscope, 1864 * loop, multiple, muted, nohref, noresize, noshade, novalidate, nowrap, open, 1865 * paused, pubdate, readonly, required, reversed, scoped, seamless, seeking, 1866 * selected, spellcheck, truespeed, willvalidate 1867 * 1868 * <p>Finally, the following commonly mis-capitalized attribute/property names 1869 * are evaluated as expected: 1870 * <ul> 1871 * <li>"class" 1872 * <li>"readonly" 1873 * </ul> 1874 * @param {string} attributeName The name of the attribute to query. 1875 * @return {!webdriver.promise.Promise.<?string>} A promise that will be 1876 * resolved with the attribute's value. The returned value will always be 1877 * either a string or null. 1878 */ 1879 webdriver.WebElement.prototype.getAttribute = function(attributeName) { 1880 return this.schedule_( 1881 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_ATTRIBUTE). 1882 setParameter('name', attributeName), 1883 'WebElement.getAttribute(' + attributeName + ')'); 1884 }; 1885 1886 1887 /** 1888 * Get the visible (i.e. not hidden by CSS) innerText of this element, including 1889 * sub-elements, without any leading or trailing whitespace. 1890 * @return {!webdriver.promise.Promise.<string>} A promise that will be 1891 * resolved with the element's visible text. 1892 */ 1893 webdriver.WebElement.prototype.getText = function() { 1894 return this.schedule_( 1895 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_TEXT), 1896 'WebElement.getText()'); 1897 }; 1898 1899 1900 /** 1901 * Schedules a command to compute the size of this element's bounding box, in 1902 * pixels. 1903 * @return {!webdriver.promise.Promise.<{width: number, height: number}>} A 1904 * promise that will be resolved with the element's size as a 1905 * {@code {width:number, height:number}} object. 1906 */ 1907 webdriver.WebElement.prototype.getSize = function() { 1908 return this.schedule_( 1909 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_SIZE), 1910 'WebElement.getSize()'); 1911 }; 1912 1913 1914 /** 1915 * Schedules a command to compute the location of this element in page space. 1916 * @return {!webdriver.promise.Promise.<{x: number, y: number}>} A promise that 1917 * will be resolved to the element's location as a 1918 * {@code {x:number, y:number}} object. 1919 */ 1920 webdriver.WebElement.prototype.getLocation = function() { 1921 return this.schedule_( 1922 new webdriver.Command(webdriver.CommandName.GET_ELEMENT_LOCATION), 1923 'WebElement.getLocation()'); 1924 }; 1925 1926 1927 /** 1928 * Schedules a command to query whether the DOM element represented by this 1929 * instance is enabled, as dicted by the {@code disabled} attribute. 1930 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be 1931 * resolved with whether this element is currently enabled. 1932 */ 1933 webdriver.WebElement.prototype.isEnabled = function() { 1934 return this.schedule_( 1935 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_ENABLED), 1936 'WebElement.isEnabled()'); 1937 }; 1938 1939 1940 /** 1941 * Schedules a command to query whether this element is selected. 1942 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be 1943 * resolved with whether this element is currently selected. 1944 */ 1945 webdriver.WebElement.prototype.isSelected = function() { 1946 return this.schedule_( 1947 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_SELECTED), 1948 'WebElement.isSelected()'); 1949 }; 1950 1951 1952 /** 1953 * Schedules a command to submit the form containing this element (or this 1954 * element if it is a FORM element). This command is a no-op if the element is 1955 * not contained in a form. 1956 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1957 * when the form has been submitted. 1958 */ 1959 webdriver.WebElement.prototype.submit = function() { 1960 return this.schedule_( 1961 new webdriver.Command(webdriver.CommandName.SUBMIT_ELEMENT), 1962 'WebElement.submit()'); 1963 }; 1964 1965 1966 /** 1967 * Schedules a command to clear the {@code value} of this element. This command 1968 * has no effect if the underlying DOM element is neither a text INPUT element 1969 * nor a TEXTAREA element. 1970 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 1971 * when the element has been cleared. 1972 */ 1973 webdriver.WebElement.prototype.clear = function() { 1974 return this.schedule_( 1975 new webdriver.Command(webdriver.CommandName.CLEAR_ELEMENT), 1976 'WebElement.clear()'); 1977 }; 1978 1979 1980 /** 1981 * Schedules a command to test whether this element is currently displayed. 1982 * @return {!webdriver.promise.Promise.<boolean>} A promise that will be 1983 * resolved with whether this element is currently visible on the page. 1984 */ 1985 webdriver.WebElement.prototype.isDisplayed = function() { 1986 return this.schedule_( 1987 new webdriver.Command(webdriver.CommandName.IS_ELEMENT_DISPLAYED), 1988 'WebElement.isDisplayed()'); 1989 }; 1990 1991 1992 /** 1993 * Schedules a command to retrieve the outer HTML of this element. 1994 * @return {!webdriver.promise.Promise.<string>} A promise that will be 1995 * resolved with the element's outer HTML. 1996 */ 1997 webdriver.WebElement.prototype.getOuterHtml = function() { 1998 return this.driver_.executeScript(function() { 1999 var element = arguments[0]; 2000 if ('outerHTML' in element) { 2001 return element.outerHTML; 2002 } else { 2003 var div = element.ownerDocument.createElement('div'); 2004 div.appendChild(element.cloneNode(true)); 2005 return div.innerHTML; 2006 } 2007 }, this); 2008 }; 2009 2010 2011 /** 2012 * Schedules a command to retrieve the inner HTML of this element. 2013 * @return {!webdriver.promise.Promise.<string>} A promise that will be 2014 * resolved with the element's inner HTML. 2015 */ 2016 webdriver.WebElement.prototype.getInnerHtml = function() { 2017 return this.driver_.executeScript('return arguments[0].innerHTML', this); 2018 }; 2019 2020 2021 2022 /** 2023 * Represents a modal dialog such as {@code alert}, {@code confirm}, or 2024 * {@code prompt}. Provides functions to retrieve the message displayed with 2025 * the alert, accept or dismiss the alert, and set the response text (in the 2026 * case of {@code prompt}). 2027 * @param {!webdriver.WebDriver} driver The driver controlling the browser this 2028 * alert is attached to. 2029 * @param {!(string|webdriver.promise.Promise.<string>)} text Either the 2030 * message text displayed with this alert, or a promise that will be 2031 * resolved to said text. 2032 * @constructor 2033 * @extends {webdriver.promise.Deferred} 2034 */ 2035 webdriver.Alert = function(driver, text) { 2036 goog.base(this, null, driver.controlFlow()); 2037 2038 /** @private {!webdriver.WebDriver} */ 2039 this.driver_ = driver; 2040 2041 // This class is responsible for resolving itself; delete the resolve and 2042 // reject methods so they may not be accessed by consumers of this class. 2043 var fulfill = goog.partial(this.fulfill, this); 2044 var reject = this.reject; 2045 delete this.promise; 2046 delete this.fulfill; 2047 delete this.reject; 2048 2049 /** @private {!webdriver.promise.Promise.<string>} */ 2050 this.text_ = webdriver.promise.when(text); 2051 2052 // Make sure this instance is resolved when its displayed text is. 2053 this.text_.then(fulfill, reject); 2054 }; 2055 goog.inherits(webdriver.Alert, webdriver.promise.Deferred); 2056 2057 2058 /** 2059 * Retrieves the message text displayed with this alert. For instance, if the 2060 * alert were opened with alert("hello"), then this would return "hello". 2061 * @return {!webdriver.promise.Promise.<string>} A promise that will be 2062 * resolved to the text displayed with this alert. 2063 */ 2064 webdriver.Alert.prototype.getText = function() { 2065 return this.text_; 2066 }; 2067 2068 2069 /** 2070 * Accepts this alert. 2071 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 2072 * when this command has completed. 2073 */ 2074 webdriver.Alert.prototype.accept = function() { 2075 return this.driver_.schedule( 2076 new webdriver.Command(webdriver.CommandName.ACCEPT_ALERT), 2077 'WebDriver.switchTo().alert().accept()'); 2078 }; 2079 2080 2081 /** 2082 * Dismisses this alert. 2083 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 2084 * when this command has completed. 2085 */ 2086 webdriver.Alert.prototype.dismiss = function() { 2087 return this.driver_.schedule( 2088 new webdriver.Command(webdriver.CommandName.DISMISS_ALERT), 2089 'WebDriver.switchTo().alert().dismiss()'); 2090 }; 2091 2092 2093 /** 2094 * Sets the response text on this alert. This command will return an error if 2095 * the underlying alert does not support response text (e.g. window.alert and 2096 * window.confirm). 2097 * @param {string} text The text to set. 2098 * @return {!webdriver.promise.Promise.<void>} A promise that will be resolved 2099 * when this command has completed. 2100 */ 2101 webdriver.Alert.prototype.sendKeys = function(text) { 2102 return this.driver_.schedule( 2103 new webdriver.Command(webdriver.CommandName.SET_ALERT_TEXT). 2104 setParameter('text', text), 2105 'WebDriver.switchTo().alert().sendKeys(' + text + ')'); 2106 }; 2107 2108 2109 2110 /** 2111 * An error returned to indicate that there is an unhandled modal dialog on the 2112 * current page. 2113 * @param {string} message The error message. 2114 * @param {!webdriver.Alert} alert The alert handle. 2115 * @constructor 2116 * @extends {bot.Error} 2117 */ 2118 webdriver.UnhandledAlertError = function(message, alert) { 2119 goog.base(this, bot.ErrorCode.MODAL_DIALOG_OPENED, message); 2120 2121 /** @private {!webdriver.Alert} */ 2122 this.alert_ = alert; 2123 }; 2124 goog.inherits(webdriver.UnhandledAlertError, bot.Error); 2125 2126 2127 /** 2128 * @return {!webdriver.Alert} The open alert. 2129 */ 2130 webdriver.UnhandledAlertError.prototype.getAlert = function() { 2131 return this.alert_; 2132 }; net/index.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 'use strict'; 17 18 var os = require('os'); 19 20 21 function getLoInterface() { 22 var name; 23 if (process.platform === 'darwin') { 24 name = 'lo0'; 25 } else if (process.platform === 'linux') { 26 name = 'lo'; 27 } 28 return name ? os.networkInterfaces()[name] : null; 29 } 30 31 32 /** 33 * Queries the system network interfaces for an IP address. 34 * @param {boolean} loopback Whether to find a loopback address. 35 * @param {string=} opt_family The IP family (IPv4 or IPv6). Defaults to IPv4. 36 * @return {string} The located IP address or undefined. 37 */ 38 function getAddress(loopback, opt_family) { 39 var family = opt_family || 'IPv4'; 40 var addresses = []; 41 42 var interfaces; 43 if (loopback) { 44 var lo = getLoInterface(); 45 interfaces = lo ? [lo] : null; 46 } 47 interfaces = interfaces || os.networkInterfaces(); 48 for (var key in interfaces) { 49 interfaces[key].forEach(function(ipAddress) { 50 if (ipAddress.family === family && 51 ipAddress.internal === loopback) { 52 addresses.push(ipAddress.address); 53 } 54 }); 55 } 56 return addresses[0]; 57 } 58 59 60 // PUBLIC API 61 62 63 /** 64 * Retrieves the external IP address for this host. 65 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4". 66 * @return {string} The IP address or undefined if not available. 67 */ 68 exports.getAddress = function(opt_family) { 69 return getAddress(false, opt_family); 70 }; 71 72 73 /** 74 * Retrieves a loopback address for this machine. 75 * @param {string=} opt_family The IP family to retrieve. Defaults to "IPv4". 76 * @return {string} The IP address or undefined if not available. 77 */ 78 exports.getLoopbackAddress = function(opt_family) { 79 return getAddress(true, opt_family); 80 }; net/portprober.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 'use strict'; 17 18 var exec = require('child_process').exec, 19 fs = require('fs'), 20 net = require('net'); 21 22 var promise = require('../index').promise; 23 24 25 /** 26 * The IANA suggested ephemeral port range. 27 * @type {{min: number, max: number}} 28 * @const 29 * @see http://en.wikipedia.org/wiki/Ephemeral_ports 30 */ 31 var DEFAULT_IANA_RANGE = {min: 49152, max: 65535}; 32 33 34 /** 35 * The epheremal port range for the current system. Lazily computed on first 36 * access. 37 * @type {webdriver.promise.Promise.<{min: number, max: number}>} 38 */ 39 var systemRange = null; 40 41 42 /** 43 * Computes the ephemeral port range for the current system. This is based on 44 * http://stackoverflow.com/a/924337. 45 * @return {webdriver.promise.Promise.<{min: number, max: number}>} A promise 46 * that will resolve to the ephemeral port range of the current system. 47 */ 48 function findSystemPortRange() { 49 if (systemRange) { 50 return systemRange; 51 } 52 var range = process.platform === 'win32' ? 53 findWindowsPortRange() : findUnixPortRange(); 54 return systemRange = range.thenCatch(function() { 55 return DEFAULT_IANA_RANGE; 56 }); 57 } 58 59 60 /** 61 * Executes a command and returns its output if it succeeds. 62 * @param {string} cmd The command to execute. 63 * @return {!webdriver.promise.Promise<string>} A promise that will resolve 64 * with the command's stdout data. 65 */ 66 function execute(cmd) { 67 var result = promise.defer(); 68 exec(cmd, function(err, stdout) { 69 if (err) { 70 result.reject(err); 71 } else { 72 result.fulfill(stdout); 73 } 74 }); 75 return result.promise; 76 } 77 78 79 /** 80 * Computes the ephemeral port range for a Unix-like system. 81 * @return {!webdriver.promise.Promise<{min: number, max: number}>} A promise 82 * that will resolve with the ephemeral port range on the current system. 83 */ 84 function findUnixPortRange() { 85 var cmd; 86 if (process.platform === 'sunos') { 87 cmd = 88 '/usr/sbin/ndd /dev/tcp tcp_smallest_anon_port tcp_largest_anon_port'; 89 } else if (fs.existsSync('/proc/sys/net/ipv4/ip_local_port_range')) { 90 // Linux 91 cmd = 'cat /proc/sys/net/ipv4/ip_local_port_range'; 92 } else { 93 cmd = 'sysctl net.inet.ip.portrange.first net.inet.ip.portrange.last' + 94 ' | sed -e "s/.*:\\s*//"'; 95 } 96 97 return execute(cmd).then(function(stdout) { 98 if (!stdout || !stdout.length) return DEFAULT_IANA_RANGE; 99 var range = stdout.trim().split(/\s+/).map(Number); 100 if (range.some(isNaN)) return DEFAULT_IANA_RANGE; 101 return {min: range[0], max: range[1]}; 102 }); 103 } 104 105 106 /** 107 * Computes the ephemeral port range for a Windows system. 108 * @return {!webdriver.promise.Promise<{min: number, max: number}>} A promise 109 * that will resolve with the ephemeral port range on the current system. 110 */ 111 function findWindowsPortRange() { 112 var deferredRange = promise.defer(); 113 // First, check if we're running on XP. If this initial command fails, 114 // we just fallback on the default IANA range. 115 return execute('cmd.exe /c ver').then(function(stdout) { 116 if (/Windows XP/.test(stdout)) { 117 // TODO: Try to read these values from the registry. 118 return {min: 1025, max: 5000}; 119 } else { 120 return execute('netsh int ipv4 show dynamicport tcp'). 121 then(function(stdout) { 122 /* > netsh int ipv4 show dynamicport tcp 123 Protocol tcp Dynamic Port Range 124 --------------------------------- 125 Start Port : 49152 126 Number of Ports : 16384 127 */ 128 var range = stdout.split(/\n/).filter(function(line) { 129 return /.*:\s*\d+/.test(line); 130 }).map(function(line) { 131 return Number(line.split(/:\s*/)[1]); 132 }); 133 134 return { 135 min: range[0], 136 max: range[0] + range[1] 137 }; 138 }); 139 } 140 }); 141 } 142 143 144 /** 145 * Tests if a port is free. 146 * @param {number} port The port to test. 147 * @param {string=} opt_host The bound host to test the {@code port} against. 148 * Defaults to {@code INADDR_ANY}. 149 * @return {!webdriver.promise.Promise.<boolean>} A promise that will resolve 150 * with whether the port is free. 151 */ 152 function isFree(port, opt_host) { 153 var result = promise.defer(function() { 154 server.cancel(); 155 }); 156 157 var server = net.createServer().on('error', function(e) { 158 if (e.code === 'EADDRINUSE') { 159 result.fulfill(false); 160 } else { 161 result.reject(e); 162 } 163 }); 164 165 server.listen(port, opt_host, function() { 166 server.close(function() { 167 result.fulfill(true); 168 }); 169 }); 170 171 return result.promise; 172 } 173 174 175 /** 176 * @param {string=} opt_host The bound host to test the {@code port} against. 177 * Defaults to {@code INADDR_ANY}. 178 * @return {!webdriver.promise.Promise.<number>} A promise that will resolve 179 * to a free port. If a port cannot be found, the promise will be 180 * rejected. 181 */ 182 function findFreePort(opt_host) { 183 return findSystemPortRange().then(function(range) { 184 var attempts = 0; 185 var deferredPort = promise.defer(); 186 findPort(); 187 return deferredPort.promise; 188 189 function findPort() { 190 attempts += 1; 191 if (attempts > 10) { 192 deferredPort.reject(Error('Unable to find a free port')); 193 } 194 195 var port = Math.floor( 196 Math.random() * (range.max - range.min) + range.min); 197 isFree(port, opt_host).then(function(isFree) { 198 if (isFree) { 199 deferredPort.fulfill(port); 200 } else { 201 findPort(); 202 } 203 }); 204 } 205 }); 206 } 207 208 209 // PUBLIC API 210 211 212 exports.findFreePort = findFreePort; 213 exports.isFree = isFree; phantomjs.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 'use strict'; 17 18 var fs = require('fs'), 19 util = require('util'); 20 21 var webdriver = require('./index'), 22 LogLevel = webdriver.logging.LevelName, 23 executors = require('./executors'), 24 io = require('./io'), 25 portprober = require('./net/portprober'), 26 remote = require('./remote'); 27 28 29 /** 30 * Name of the PhantomJS executable. 31 * @type {string} 32 * @const 33 */ 34 var PHANTOMJS_EXE = 35 process.platform === 'win32' ? 'phantomjs.exe' : 'phantomjs'; 36 37 38 /** 39 * Capability that designates the location of the PhantomJS executable to use. 40 * @type {string} 41 * @const 42 */ 43 var BINARY_PATH_CAPABILITY = 'phantomjs.binary.path'; 44 45 46 /** 47 * Capability that designates the CLI arguments to pass to PhantomJS. 48 * @type {string} 49 * @const 50 */ 51 var CLI_ARGS_CAPABILITY = 'phantomjs.cli.args'; 52 53 54 /** 55 * Default log file to use if one is not specified through CLI args. 56 * @type {string} 57 * @const 58 */ 59 var DEFAULT_LOG_FILE = 'phantomjsdriver.log'; 60 61 62 /** 63 * Finds the PhantomJS executable. 64 * @param {string=} opt_exe Path to the executable to use. 65 * @return {string} The located executable. 66 * @throws {Error} If the executable cannot be found on the PATH, or if the 67 * provided executable path does not exist. 68 */ 69 function findExecutable(opt_exe) { 70 var exe = opt_exe || io.findInPath(PHANTOMJS_EXE, true); 71 if (!exe) { 72 throw Error( 73 'The PhantomJS executable could not be found on the current PATH. ' + 74 'Please download the latest version from ' + 75 'http://phantomjs.org/download.html and ensure it can be found on ' + 76 'your PATH. For more information, see ' + 77 'https://github.com/ariya/phantomjs/wiki'); 78 } 79 if (!fs.existsSync(exe)) { 80 throw Error('File does not exist: ' + exe); 81 } 82 return exe; 83 } 84 85 86 /** 87 * Maps WebDriver logging level name to those recognised by PhantomJS. 88 * @type {!Object.<webdriver.logging.LevelName, string>} 89 * @const 90 */ 91 var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() { 92 var map = {}; 93 map[LogLevel.ALL] = map[LogLevel.DEBUG] = 'DEBUG'; 94 map[LogLevel.INFO] = 'INFO'; 95 map[LogLevel.WARNING] = 'WARN'; 96 map[LogLevel.SEVERE] = map[LogLevel.OFF] = 'ERROR'; 97 return map; 98 })(); 99 100 101 /** 102 * Creates a new PhantomJS WebDriver client. 103 * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities. 104 * @return {!webdriver.WebDriver} A new WebDriver instance. 105 */ 106 function createDriver(opt_capabilities) { 107 var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs(); 108 var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY)); 109 var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE]; 110 111 var logPrefs = capabilities.get(webdriver.Capability.LOGGING_PREFS); 112 if (logPrefs && logPrefs[webdriver.logging.Type.DRIVER]) { 113 var level = WEBDRIVER_TO_PHANTOMJS_LEVEL[ 114 logPrefs[webdriver.logging.Type.DRIVER]]; 115 if (level) { 116 args.push('--webdriver-loglevel=' + level); 117 } 118 } 119 120 var proxy = capabilities.get(webdriver.Capability.PROXY); 121 if (proxy) { 122 switch (proxy.proxyType) { 123 case 'manual': 124 if (proxy.httpProxy) { 125 args.push( 126 '--proxy-type=http', 127 '--proxy=http://' + proxy.httpProxy); 128 } 129 break; 130 case 'pac': 131 throw Error('PhantomJS does not support Proxy PAC files'); 132 case 'system': 133 args.push('--proxy-type=system'); 134 break; 135 case 'direct': 136 args.push('--proxy-type=none'); 137 break; 138 } 139 } 140 args = args.concat(capabilities.get(CLI_ARGS_CAPABILITY) || []); 141 142 var port = portprober.findFreePort(); 143 var service = new remote.DriverService(exe, { 144 port: port, 145 args: webdriver.promise.when(port, function(port) { 146 args.push('--webdriver=' + port); 147 return args; 148 }) 149 }); 150 151 var executor = executors.createExecutor(service.start()); 152 var driver = webdriver.WebDriver.createSession(executor, capabilities); 153 var boundQuit = driver.quit.bind(driver); 154 driver.quit = function() { 155 return boundQuit().thenFinally(service.kill.bind(service)); 156 }; 157 return driver; 158 } 159 160 161 // PUBLIC API 162 163 164 exports.createDriver = createDriver; proxy.js
1 // Copyright 2013 Selenium committers 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Defines functions for configuring a webdriver proxy: 17 * <pre><code> 18 * var webdriver = require('selenium-webdriver'), 19 * proxy = require('selenium-webdriver/proxy'); 20 * 21 * var driver = new webdriver.Builder() 22 * .withCapabilities(webdriver.Capabilities.chrome()) 23 * .setProxy(proxy.manual({http: 'host:1234'})) 24 * .build(); 25 * </code></pre> 26 */ 27 28 'use strict'; 29 30 var util = require('util'); 31 32 33 /** 34 * Proxy configuration object, as defined by the WebDriver wire protocol. 35 * @typedef {( 36 * {proxyType: string}| 37 * {proxyType: string, 38 * proxyAutoconfigUrl: string}| 39 * {proxyType: string, 40 * ftpProxy: string, 41 * httpProxy: string, 42 * sslProxy: string, 43 * noProxy: string})} 44 */ 45 var ProxyConfig; 46 47 48 49 // PUBLIC API 50 51 52 /** 53 * Configures WebDriver to bypass all browser proxies. 54 * @return {!ProxyConfig} A new proxy configuration object. 55 */ 56 exports.direct = function() { 57 return {proxyType: 'direct'}; 58 }; 59 60 61 /** 62 * Manually configures the browser proxy. The following options are 63 * supported: 64 * <ul> 65 * <li>{@code ftp}: Proxy host to use for FTP requests 66 * <li>{@code http}: Proxy host to use for HTTP requests 67 * <li>{@code https}: Proxy host to use for HTTPS requests 68 * <li>{@code bypass}: A list of hosts requests should directly connect to, 69 * bypassing any other proxies for that request. May be specified as a 70 * comma separated string, or a list of strings. 71 * </ul> 72 * 73 * Behavior is undefined for FTP, HTTP, and HTTPS requests if the 74 * corresponding key is omitted from the configuration options. 75 * 76 * @param {{ftp: (string|undefined), 77 * http: (string|undefined), 78 * https: (string|undefined), 79 * bypass: (string|!Array.<string>|undefined)}} options Proxy 80 * configuration options. 81 * @return {!ProxyConfig} A new proxy configuration object. 82 */ 83 exports.manual = function(options) { 84 return { 85 proxyType: 'manual', 86 ftpProxy: options.ftp, 87 httpProxy: options.http, 88 sslProxy: options.https, 89 noProxy: util.isArray(options.bypass) ? 90 options.bypass.join(',') : options.bypass 91 }; 92 }; 93 94 95 /** 96 * Configures WebDriver to configure the browser proxy using the PAC file at 97 * the given URL. 98 * @param {string} url URL for the PAC proxy to use. 99 * @return {!ProxyConfig} A new proxy configuration object. 100 */ 101 exports.pac = function(url) { 102 return { 103 proxyType: 'pac', 104 proxyAutoconfigUrl: url 105 }; 106 }; 107 108 109 /** 110 * Configures WebDriver to use the current system's proxy. 111 * @return {!ProxyConfig} A new proxy configuration object. 112 */ 113 exports.system = function() { 114 return {proxyType: 'system'}; 115 }; remote/index.js
1 // Copyright 2013 Software Freedom Conservancy 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 'use strict'; 16 17 var spawn = require('child_process').spawn, 18 os = require('os'), 19 path = require('path'), 20 url = require('url'), 21 util = require('util'); 22 23 var promise = require('../').promise, 24 httpUtil = require('../http/util'), 25 net = require('../net'), 26 portprober = require('../net/portprober'); 27 28 29 30 /** 31 * Configuration options for a DriverService instance. 32 * <ul> 33 * <li> 34 * <li>{@code loopback} - Whether the service should only be accessed on this 35 * host's loopback address. 36 * <li>{@code port} - The port to start the server on (must be > 0). If the 37 * port is provided as a promise, the service will wait for the promise to 38 * resolve before starting. 39 * <li>{@code args} - The arguments to pass to the service. If a promise is 40 * provided, the service will wait for it to resolve before starting. 41 * <li>{@code path} - The base path on the server for the WebDriver wire 42 * protocol (e.g. '/wd/hub'). Defaults to '/'. 43 * <li>{@code env} - The environment variables that should be visible to the 44 * server process. Defaults to inheriting the current process's 45 * environment. 46 * <li>{@code stdio} - IO configuration for the spawned server process. For 47 * more information, refer to the documentation of 48 * {@code child_process.spawn}. 49 * </ul> 50 * 51 * @typedef {{ 52 * port: (number|!webdriver.promise.Promise.<number>), 53 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>), 54 * path: (string|undefined), 55 * env: (!Object.<string, string>|undefined), 56 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined) 57 * }} 58 */ 59 var ServiceOptions; 60 61 62 /** 63 * Manages the life and death of a native executable WebDriver server. 64 * 65 * <p>It is expected that the driver server implements the 66 * <a href="http://code.google.com/p/selenium/wiki/JsonWireProtocol">WebDriver 67 * Wire Protocol</a>. Furthermore, the managed server should support multiple 68 * concurrent sessions, so that this class may be reused for multiple clients. 69 * 70 * @param {string} executable Path to the executable to run. 71 * @param {!ServiceOptions} options Configuration options for the service. 72 * @constructor 73 */ 74 function DriverService(executable, options) { 75 76 /** @private {string} */ 77 this.executable_ = executable; 78 79 /** @private {boolean} */ 80 this.loopbackOnly_ = !!options.loopback; 81 82 /** @private {(number|!webdriver.promise.Promise.<number>)} */ 83 this.port_ = options.port; 84 85 /** 86 * @private {!(Array.<string>|webdriver.promise.Promise.<!Array.<string>>)} 87 */ 88 this.args_ = options.args; 89 90 /** @private {string} */ 91 this.path_ = options.path || '/'; 92 93 /** @private {!Object.<string, string>} */ 94 this.env_ = options.env || process.env; 95 96 /** @private {(string|!Array.<string|number|!Stream|null|undefined>)} */ 97 this.stdio_ = options.stdio || 'ignore'; 98 } 99 100 101 /** 102 * The default amount of time, in milliseconds, to wait for the server to 103 * start. 104 * @type {number} 105 */ 106 DriverService.DEFAULT_START_TIMEOUT_MS = 30 * 1000; 107 108 109 /** @private {child_process.ChildProcess} */ 110 DriverService.prototype.process_ = null; 111 112 113 /** 114 * Promise that resolves to the server's address or null if the server has not 115 * been started. 116 * @private {webdriver.promise.Promise.<string>} 117 */ 118 DriverService.prototype.address_ = null; 119 120 121 /** 122 * Promise that tracks the status of shutting down the server, or null if the 123 * server is not currently shutting down. 124 * @private {webdriver.promise.Promise} 125 */ 126 DriverService.prototype.shutdownHook_ = null; 127 128 129 /** 130 * @return {!webdriver.promise.Promise.<string>} A promise that resolves to 131 * the server's address. 132 * @throws {Error} If the server has not been started. 133 */ 134 DriverService.prototype.address = function() { 135 if (this.address_) { 136 return this.address_; 137 } 138 throw Error('Server has not been started.'); 139 }; 140 141 142 /** 143 * @return {boolean} Whether the underlying service process is running. 144 */ 145 DriverService.prototype.isRunning = function() { 146 return !!this.address_; 147 }; 148 149 150 /** 151 * Starts the server if it is not already running. 152 * @param {number=} opt_timeoutMs How long to wait, in milliseconds, for the 153 * server to start accepting requests. Defaults to 30 seconds. 154 * @return {!webdriver.promise.Promise.<string>} A promise that will resolve 155 * to the server's base URL when it has started accepting requests. If the 156 * timeout expires before the server has started, the promise will be 157 * rejected. 158 */ 159 DriverService.prototype.start = function(opt_timeoutMs) { 160 if (this.address_) { 161 return this.address_; 162 } 163 164 var timeout = opt_timeoutMs || DriverService.DEFAULT_START_TIMEOUT_MS; 165 166 var self = this; 167 this.address_ = promise.defer(); 168 this.address_.fulfill(promise.when(this.port_, function(port) { 169 if (port <= 0) { 170 throw Error('Port must be > 0: ' + port); 171 } 172 return promise.when(self.args_, function(args) { 173 self.process_ = spawn(self.executable_, args, { 174 env: self.env_, 175 stdio: self.stdio_ 176 }).once('exit', onServerExit); 177 178 // This process should not wait on the spawned child, however, we do 179 // want to ensure the child is killed when this process exits. 180 self.process_.unref(); 181 process.once('exit', killServer); 182 183 var serverUrl = url.format({ 184 protocol: 'http', 185 hostname: !self.loopbackOnly_ && net.getAddress() || 186 net.getLoopbackAddress(), 187 port: port, 188 pathname: self.path_ 189 }); 190 191 return httpUtil.waitForServer(serverUrl, timeout).then(function() { 192 return serverUrl; 193 }); 194 }); 195 })); 196 197 return this.address_; 198 199 function onServerExit(code, signal) { 200 self.address_.reject(code == null ? 201 Error('Server was killed with ' + signal) : 202 Error('Server exited with ' + code)); 203 204 if (self.shutdownHook_) { 205 self.shutdownHook_.fulfill(); 206 } 207 208 self.shutdownHook_ = null; 209 self.address_ = null; 210 self.process_ = null; 211 process.removeListener('exit', killServer); 212 } 213 214 function killServer() { 215 process.removeListener('exit', killServer); 216 self.process_ && self.process_.kill('SIGTERM'); 217 } 218 }; 219 220 221 /** 222 * Stops the service if it is not currently running. This function will kill 223 * the server immediately. To synchronize with the active control flow, use 224 * {@link #stop()}. 225 * @return {!webdriver.promise.Promise} A promise that will be resolved when 226 * the server has been stopped. 227 */ 228 DriverService.prototype.kill = function() { 229 if (!this.address_) { 230 return promise.fulfilled(); // Not currently running. 231 } 232 233 if (!this.shutdownHook_) { 234 // No process: still starting; wait on address. 235 // Otherwise, kill the process now. Exit handler will resolve the 236 // shutdown hook. 237 if (this.process_) { 238 this.shutdownHook_ = promise.defer(); 239 this.process_.kill('SIGTERM'); 240 } else { 241 var self = this; 242 this.shutdownHook_ = this.address_.thenFinally(function() { 243 self.process_ && self.process_.kill('SIGTERM'); 244 }); 245 } 246 } 247 248 return this.shutdownHook_; 249 }; 250 251 252 /** 253 * Schedules a task in the current control flow to stop the server if it is 254 * currently running. 255 * @return {!webdriver.promise.Promise} A promise that will be resolved when 256 * the server has been stopped. 257 */ 258 DriverService.prototype.stop = function() { 259 return promise.controlFlow().execute(this.kill.bind(this)); 260 }; 261 262 263 264 /** 265 * Manages the life and death of the Selenium standalone server. The server 266 * may be obtained from http://selenium-release.storage.googleapis.com/index.html. 267 * @param {string} jar Path to the Selenium server jar. 268 * @param {!SeleniumServer.Options} options Configuration options for the 269 * server. 270 * @throws {Error} If an invalid port is specified. 271 * @constructor 272 * @extends {DriverService} 273 */ 274 function SeleniumServer(jar, options) { 275 if (options.port < 0) 276 throw Error('Port must be >= 0: ' + options.port); 277 278 var port = options.port || portprober.findFreePort(); 279 var args = promise.when(options.jvmArgs || [], function(jvmArgs) { 280 return promise.when(options.args || [], function(args) { 281 return promise.when(port, function(port) { 282 return jvmArgs.concat(['-jar', jar, '-port', port]).concat(args); 283 }); 284 }); 285 }); 286 287 DriverService.call(this, 'java', { 288 port: port, 289 args: args, 290 path: '/wd/hub', 291 env: options.env, 292 stdio: options.stdio 293 }); 294 } 295 util.inherits(SeleniumServer, DriverService); 296 297 298 /** 299 * Options for the Selenium server: 300 * <ul> 301 * <li>{@code port} - The port to start the server on (must be > 0). If the 302 * port is provided as a promise, the service will wait for the promise to 303 * resolve before starting. 304 * <li>{@code args} - The arguments to pass to the service. If a promise is 305 * provided, the service will wait for it to resolve before starting. 306 * <li>{@code jvmArgs} - The arguments to pass to the JVM. If a promise is 307 * provided, the service will wait for it to resolve before starting. 308 * <li>{@code env} - The environment variables that should be visible to the 309 * server process. Defaults to inheriting the current process's 310 * environment. 311 * <li>{@code stdio} - IO configuration for the spawned server process. For 312 * more information, refer to the documentation of 313 * {@code child_process.spawn}. 314 * </ul> 315 * 316 * @typedef {{ 317 * port: (number|!webdriver.promise.Promise.<number>), 318 * args: !(Array.<string>|webdriver.promise.Promise.<!Array.<string>>), 319 * jvmArgs: (!Array.<string>| 320 * !webdriver.promise.Promise.<!Array.<string>>| 321 * undefined), 322 * env: (!Object.<string, string>|undefined), 323 * stdio: (string|!Array.<string|number|!Stream|null|undefined>|undefined) 324 * }} 325 */ 326 SeleniumServer.Options; 327 328 329 // PUBLIC API 330 331 exports.DriverService = DriverService; 332 exports.SeleniumServer = SeleniumServer; testing/assert.js
1 // Copyright 2013 Software Freedom Conservancy 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 /** 16 * @fileoverview Defines a library that simplifies writing assertions against 17 * promised values. 18 * 19 * <blockquote> 20 * <hr> 21 * <b>NOTE:</b> This module is considered experimental and is subject to 22 * change, or removal, at any time! 23 * <hr> 24 * </blockquote> 25 * 26 * Sample usage: 27 * <pre><code> 28 * var driver = new webdriver.Builder().build(); 29 * driver.get('http://www.google.com'); 30 * 31 * assert(driver.getTitle()).equalTo('Google'); 32 * </code></pre> 33 */ 34 35 var base = require('../_base'), 36 assert = base.require('webdriver.testing.assert'); 37 38 39 // PUBLIC API 40 41 42 /** @type {webdriver.testing.assert.} */ 43 module.exports = assert; testing/index.js
1 // Copyright 2013 Selenium committers 2 // Copyright 2013 Software Freedom Conservancy 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 16 /** 17 * @fileoverview Provides wrappers around the following global functions from 18 * <a href="http://visionmedia.github.io/mocha/">Mocha's BDD interface</a>: 19 * <ul> 20 * <li>after 21 * <li>afterEach 22 * <li>before 23 * <li>beforeEach 24 * <li>it 25 * <li>it.only 26 * <li>it.skip 27 * <li>xit 28 * </ul> 29 * 30 * <p>The provided wrappers leverage the {@link webdriver.promise.ControlFlow} 31 * to simplify writing asynchronous tests: 32 * <pre><code> 33 * var webdriver = require('selenium-webdriver'), 34 * portprober = require('selenium-webdriver/net/portprober'), 35 * remote = require('selenium-webdriver/remote'), 36 * test = require('selenium-webdriver/testing'); 37 * 38 * test.describe('Google Search', function() { 39 * var driver, server; 40 * 41 * test.before(function() { 42 * server = new remote.SeleniumServer( 43 * 'path/to/selenium-server-standalone.jar', 44 * {port: portprober.findFreePort()}); 45 * server.start(); 46 * 47 * driver = new webdriver.Builder(). 48 * withCapabilities({'browserName': 'firefox'}). 49 * usingServer(server.address()). 50 * build(); 51 * }); 52 * 53 * test.after(function() { 54 * driver.quit(); 55 * server.stop(); 56 * }); 57 * 58 * test.it('should append query to title', function() { 59 * driver.get('http://www.google.com'); 60 * driver.findElement(webdriver.By.name('q')).sendKeys('webdriver'); 61 * driver.findElement(webdriver.By.name('btnG')).click(); 62 * driver.wait(function() { 63 * return driver.getTitle().then(function(title) { 64 * return 'webdriver - Google Search' === title; 65 * }); 66 * }, 1000, 'Waiting for title to update'); 67 * }); 68 * }); 69 * </code></pre> 70 * 71 * <p>You may conditionally suppress a test function using the exported 72 * "ignore" function. If the provided predicate returns true, the attached 73 * test case will be skipped: 74 * <pre><code> 75 * test.ignore(maybe()).it('is flaky', function() { 76 * if (Math.random() < 0.5) throw Error(); 77 * }); 78 * 79 * function maybe() { return Math.random() < 0.5; } 80 * </code></pre> 81 */ 82 83 var promise = require('..').promise; 84 var flow = promise.controlFlow(); 85 86 87 /** 88 * Wraps a function so that all passed arguments are ignored. 89 * @param {!Function} fn The function to wrap. 90 * @return {!Function} The wrapped function. 91 */ 92 function seal(fn) { 93 return function() { 94 fn(); 95 }; 96 } 97 98 99 /** 100 * Wraps a function on Mocha's BDD interface so it runs inside a 101 * webdriver.promise.ControlFlow and waits for the flow to complete before 102 * continuing. 103 * @param {!Function} globalFn The function to wrap. 104 * @return {!Function} The new function. 105 */ 106 function wrapped(globalFn) { 107 return function() { 108 switch (arguments.length) { 109 case 1: 110 globalFn(asyncTestFn(arguments[0])); 111 break; 112 113 case 2: 114 globalFn(arguments[0], asyncTestFn(arguments[1])); 115 break; 116 117 default: 118 throw Error('Invalid # arguments: ' + arguments.length); 119 } 120 }; 121 122 function asyncTestFn(fn) { 123 var ret = function(done) { 124 this.timeout(0); 125 var timeout = this.timeout; 126 this.timeout = undefined; // Do not let tests change the timeout. 127 try { 128 var testFn = fn.bind(this); 129 flow.execute(function() { 130 var done = promise.defer(); 131 promise.asap(testFn(done.reject), done.fulfill, done.reject); 132 return done.promise; 133 }).then(seal(done), done); 134 } finally { 135 this.timeout = timeout; 136 } 137 }; 138 139 ret.toString = function() { 140 return fn.toString(); 141 }; 142 143 return ret; 144 } 145 } 146 147 148 /** 149 * Ignores the test chained to this function if the provided predicate returns 150 * true. 151 * @param {function(): boolean} predicateFn A predicate to call to determine 152 * if the test should be suppressed. This function MUST be synchronous. 153 * @return {!Object} An object with wrapped versions of {@link #it()} and 154 * {@link #describe()} that ignore tests as indicated by the predicate. 155 */ 156 function ignore(predicateFn) { 157 var describe = wrap(exports.xdescribe, exports.describe); 158 describe.only = wrap(exports.xdescribe, exports.describe.only); 159 160 var it = wrap(exports.xit, exports.it); 161 it.only = wrap(exports.xit, exports.it.only); 162 163 return { 164 describe: describe, 165 it: it 166 }; 167 168 function wrap(onSkip, onRun) { 169 return function(title, fn) { 170 if (predicateFn()) { 171 onSkip(title, fn); 172 } else { 173 onRun(title, fn); 174 } 175 }; 176 } 177 } 178 179 180 // PUBLIC API 181 182 /** 183 * Registers a new test suite. 184 * @param {string} name The suite name. 185 * @param {function()=} fn The suite function, or {@code undefined} to define 186 * a pending test suite. 187 */ 188 exports.describe = global.describe; 189 190 /** 191 * Defines a suppressed test suite. 192 * @param {string} name The suite name. 193 * @param {function()=} fn The suite function, or {@code undefined} to define 194 * a pending test suite. 195 */ 196 exports.xdescribe = global.xdescribe; 197 exports.describe.skip = global.describe.skip; 198 199 /** 200 * Register a function to call after the current suite finishes. 201 * @param {function()} fn . 202 */ 203 exports.after = wrapped(global.after); 204 205 /** 206 * Register a function to call after each test in a suite. 207 * @param {function()} fn . 208 */ 209 exports.afterEach = wrapped(global.afterEach); 210 211 /** 212 * Register a function to call before the current suite starts. 213 * @param {function()} fn . 214 */ 215 exports.before = wrapped(global.before); 216 217 /** 218 * Register a function to call before each test in a suite. 219 * @param {function()} fn . 220 */ 221 exports.beforeEach = wrapped(global.beforeEach); 222 223 /** 224 * Add a test to the current suite. 225 * @param {string} name The test name. 226 * @param {function()=} fn The test function, or {@code undefined} to define 227 * a pending test case. 228 */ 229 exports.it = wrapped(global.it); 230 231 /** 232 * An alias for {@link #it()} that flags the test as the only one that should 233 * be run within the current suite. 234 * @param {string} name The test name. 235 * @param {function()=} fn The test function, or {@code undefined} to define 236 * a pending test case. 237 */ 238 exports.iit = exports.it.only = wrapped(global.it.only); 239 240 /** 241 * Adds a test to the current suite while suppressing it so it is not run. 242 * @param {string} name The test name. 243 * @param {function()=} fn The test function, or {@code undefined} to define 244 * a pending test case. 245 */ 246 exports.xit = exports.it.skip = wrapped(global.xit); 247 248 exports.ignore = ignore;
+ *
+ *
+ * @param {string|number} version The version number to check.
+ * @return {boolean} Whether the browser engine version is the same or higher
+ * than the given version.
+ */
+bot.userAgent.isEngineVersion = function(version) {
+ if (bot.userAgent.FIREFOX_EXTENSION) {
+ return bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_(version);
+ } else if (goog.userAgent.IE) {
+ return goog.string.compareVersions(
+ /** @type {number} */ (goog.userAgent.DOCUMENT_MODE), version) >= 0;
+ } else {
+ return goog.userAgent.isVersionOrHigher(version);
+ }
+};
+
+
+/**
+ * Whether the product version of the current browser is equal to or greater
+ * than the given version. This implementation differs from
+ * goog.userAgent.product.isVersion in the following ways:
+ *
+ *
+ *
+ * @param {string|number} version The version number to check.
+ * @return {boolean} Whether the browser product version is the same or higher
+ * than the given version.
+ */
+bot.userAgent.isProductVersion = function(version) {
+ if (bot.userAgent.FIREFOX_EXTENSION) {
+ return bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_(version);
+ } else if (goog.userAgent.product.ANDROID) {
+ return goog.string.compareVersions(
+ bot.userAgent.ANDROID_VERSION_, version) >= 0;
+ } else {
+ return goog.userAgent.product.isVersion(version);
+ }
+};
+
+
+/**
+ * When we are in a Firefox extension, this is a function that accepts a version
+ * and returns whether the version of Gecko we are on is the same or higher
+ * than the given version. When we are not in a Firefox extension, this is null.
+ * @private {(undefined|function((string|number)): boolean)}
+ */
+bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_;
+
+
+/**
+ * When we are in a Firefox extension, this is a function that accepts a version
+ * and returns whether the version of Firefox we are on is the same or higher
+ * than the given version. When we are not in a Firefox extension, this is null.
+ * @private {(undefined|function((string|number)): boolean)}
+ */
+bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_;
+
+
+/**
+ * Whether we are in a Firefox extension.
+ *
+ * @const
+ * @type {boolean}
+ */
+bot.userAgent.FIREFOX_EXTENSION = (function() {
+ // False if this browser is not a Gecko browser.
+ if (!goog.userAgent.GECKO) {
+ return false;
+ }
+
+ // False if this code isn't running in an extension.
+ var Components = goog.global.Components;
+ if (!Components) {
+ return false;
+ }
+ try {
+ if (!Components['classes']) {
+ return false;
+ }
+ } catch (e) {
+ return false;
+ }
+
+ // Populate the version checker functions.
+ var cc = Components['classes'];
+ var ci = Components['interfaces'];
+ var versionComparator = cc['@mozilla.org/xpcom/version-comparator;1'][
+ 'getService'](ci['nsIVersionComparator']);
+ var appInfo = cc['@mozilla.org/xre/app-info;1']['getService'](
+ ci['nsIXULAppInfo']);
+ var geckoVersion = appInfo['platformVersion'];
+ var firefoxVersion = appInfo['version'];
+
+ bot.userAgent.FIREFOX_EXTENSION_IS_ENGINE_VERSION_ = function(version) {
+ return versionComparator.compare(geckoVersion, '' + version) >= 0;
+ };
+ bot.userAgent.FIREFOX_EXTENSION_IS_PRODUCT_VERSION_ = function(version) {
+ return versionComparator.compare(firefoxVersion, '' + version) >= 0;
+ };
+
+ return true;
+})();
+
+
+/**
+ * Whether we are on IOS.
+ *
+ * @const
+ * @type {boolean}
+ */
+bot.userAgent.IOS = goog.userAgent.product.IPAD ||
+ goog.userAgent.product.IPHONE;
+
+
+/**
+ * Whether we are on a mobile browser.
+ *
+ * @const
+ * @type {boolean}
+ */
+bot.userAgent.MOBILE = bot.userAgent.IOS || goog.userAgent.product.ANDROID;
+
+
+/**
+ * Android Operating System Version.
+ * @private {string}
+ * @const
+ */
+bot.userAgent.ANDROID_VERSION_ = (function() {
+ if (goog.userAgent.product.ANDROID) {
+ var userAgentString = goog.userAgent.getUserAgentString();
+ var match = /Android\s+([0-9\.]+)/.exec(userAgentString);
+ return match ? match[1] : '0';
+ } else {
+ return '0';
+ }
+})();
+
+
+/**
+ * Whether the current document is IE in a documentMode older than 8.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_PRE8 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentModeOrHigher(8);
+
+
+/**
+ * Whether the current document is IE in IE9 (or newer) standards mode.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_9 = goog.userAgent.isDocumentModeOrHigher(9);
+
+
+/**
+ * Whether the current document is IE in a documentMode older than 9.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_PRE9 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentModeOrHigher(9);
+
+
+/**
+ * Whether the current document is IE in IE10 (or newer) standards mode.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_10 = goog.userAgent.isDocumentModeOrHigher(10);
+
+
+/**
+ * Whether the current document is IE in a documentMode older than 10.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.IE_DOC_PRE10 = goog.userAgent.IE &&
+ !goog.userAgent.isDocumentModeOrHigher(10);
+
+
+/**
+ * Whether the current browser is Android pre-gingerbread.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.ANDROID_PRE_GINGERBREAD = goog.userAgent.product.ANDROID &&
+ !bot.userAgent.isProductVersion(2.3);
+
+
+/**
+ * Whether the current browser is Android pre-icecreamsandwich
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.ANDROID_PRE_ICECREAMSANDWICH = goog.userAgent.product.ANDROID &&
+ !bot.userAgent.isProductVersion(4);
+
+
+/**
+ * Whether the current browser is Safari 6.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.SAFARI_6 = goog.userAgent.product.SAFARI &&
+ bot.userAgent.isProductVersion(6);
+
+
+/**
+ * Whether the current browser is Windows Phone.
+ * @type {boolean}
+ * @const
+ */
+bot.userAgent.WINDOWS_PHONE = goog.userAgent.IE &&
+ goog.userAgent.getUserAgentString().indexOf('IEMobile') != -1;
diff --git a/node_modules/selenium-webdriver/lib/goog/LICENSE b/node_modules/selenium-webdriver/lib/goog/LICENSE
new file mode 100644
index 0000000..d9a10c0
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/goog/LICENSE
@@ -0,0 +1,176 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
diff --git a/node_modules/selenium-webdriver/lib/goog/array/array.js b/node_modules/selenium-webdriver/lib/goog/array/array.js
new file mode 100644
index 0000000..2d8c23f
--- /dev/null
+++ b/node_modules/selenium-webdriver/lib/goog/array/array.js
@@ -0,0 +1,1570 @@
+// 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 arrays.
+ *
+ */
+
+
+goog.provide('goog.array');
+goog.provide('goog.array.ArrayLike');
+
+goog.require('goog.asserts');
+
+
+/**
+ * @define {boolean} NATIVE_ARRAY_PROTOTYPES indicates whether the code should
+ * rely on Array.prototype functions, if available.
+ *
+ * The Array.prototype functions can be defined by external libraries like
+ * Prototype and setting this flag to false forces closure to use its own
+ * goog.array implementation.
+ *
+ * If your javascript can be loaded by a third party site and you are wary about
+ * relying on the prototype functions, specify
+ * "--define goog.NATIVE_ARRAY_PROTOTYPES=false" to the JSCompiler.
+ *
+ * Setting goog.TRUSTED_SITE to false will automatically set
+ * NATIVE_ARRAY_PROTOTYPES to false.
+ */
+goog.define('goog.NATIVE_ARRAY_PROTOTYPES', goog.TRUSTED_SITE);
+
+
+/**
+ * @define {boolean} If true, JSCompiler will use the native implementation of
+ * array functions where appropriate (e.g., {@code Array#filter}) and remove the
+ * unused pure JS implementation.
+ */
+goog.define('goog.array.ASSUME_NATIVE_FUNCTIONS', false);
+
+
+/**
+ * @typedef {Array|NodeList|Arguments|{length: number}}
+ */
+goog.array.ArrayLike;
+
+
+/**
+ * Returns the last element in an array without removing it.
+ * Same as goog.array.last.
+ * @param {Array.goog.array.defaultCompare, which compares the elements
+ * using the built in < and > operators. This will produce the expected
+ * behavior for homogeneous arrays of String(s) and Number(s). The array
+ * specified must be sorted in ascending order (as defined by the
+ * comparison function). If the array is not sorted, results are undefined.
+ * If the array contains multiple instances of the specified target value, any
+ * of these instances may be found.
+ *
+ * Runtime: O(log n)
+ *
+ * @param {Array.goog.array.defaultCompare, which compares the elements using
+ * the built in < and > operators. This will produce the expected behavior
+ * for homogeneous arrays of String(s) and Number(s), unlike the native sort,
+ * but will give unpredictable results for heterogenous lists of strings and
+ * numbers with different numbers of digits.
+ *
+ * This sort is not guaranteed to be stable.
+ *
+ * Runtime: Same as Array.prototype.sort
+ *
+ * @param {Array.goog.array.defaultCompare, which compares the elements using
+ * the built in < and > operators. This will produce the expected behavior
+ * for homogeneous arrays of String(s) and Number(s).
+ *
+ * Runtime: Same as Array.prototype.sort, plus an additional
+ * O(n) overhead of copying the array twice.
+ *
+ * @param {Array.goog.array.defaultCompare.
+ * This won't work for keys that get renamed by the compiler. So use
+ * {'foo': 1, 'bar': 2} rather than {foo: 1, bar: 2}.
+ * @param {Array.